commit 8fca7e375363139362cdd9a15724754225933171 Author: Hiroyuki Eguchi Date: Fri Jan 27 17:40:43 2017 +0900 first commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68c771a --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..dbc6f48 --- /dev/null +++ b/README.rst @@ -0,0 +1,58 @@ +========== +meteos-ui +========== + +Meteos UI + +* Free software: Apache license +* Source: http://git.openstack.org/cgit/openstack/meteos-ui +* Bugs: http://bugs.launchpad.net/meteos-ui + +Enabling in DevStack +-------------------- + +Add this repo as an external repository into your ``local.conf`` file:: + + [[local|localrc]] + enable_plugin meteos-ui https://github.com/openstack/meteos-ui + +Manual Installation +------------------- + +Begin by cloning the Horizon and Meteos UI repositories:: + + git clone https://github.com/openstack/horizon + git clone https://github.com/openstack/meteos-ui + +Create a virtual environment and install Horizon dependencies:: + + cd horizon + python tools/install_venv.py + +Set up your ``local_settings.py`` file:: + + cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py + +Open up the copied ``local_settings.py`` file in your preferred text +editor. You will want to customize several settings: + +- ``OPENSTACK_HOST`` should be configured with the hostname of your + OpenStack server. Verify that the ``OPENSTACK_KEYSTONE_URL`` and + ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` settings are correct for your + environment. (They should be correct unless you modified your + OpenStack server to change them.) + +Install Meteos UI with all dependencies in your virtual environment:: + + tools/with_venv.sh pip install -e ../meteos-ui/ + +And enable it in Horizon:: + + cp ../meteos-ui/meteos_ui/enabled/* openstack_dashboard/local/enabled/ + +To run horizon with the newly enabled Meteos UI plugin run:: + + ./run_tests.sh --runserver 0.0.0.0:8080 + +to have the application start on port 8080 and the horizon dashboard will be +available in your browser at http://localhost:8080/ diff --git a/babel-django.cfg b/babel-django.cfg new file mode 100644 index 0000000..ad09d34 --- /dev/null +++ b/babel-django.cfg @@ -0,0 +1,5 @@ +[extractors] +django = django_babel.extract:extract_django + +[python: **.py] +[django: templates/**.html] diff --git a/babel-djangojs.cfg b/babel-djangojs.cfg new file mode 100644 index 0000000..a8273b6 --- /dev/null +++ b/babel-djangojs.cfg @@ -0,0 +1,14 @@ +[extractors] +# We use a custom extractor to find translatable strings in AngularJS +# templates. The extractor is included in horizon.utils for now. +# See http://babel.pocoo.org/docs/messages/#referencing-extraction-methods for +# details on how this works. +angular = horizon.utils.babel_extract_angular:extract_angular + +[javascript: **.js] + +# We need to look into all static folders for HTML files. +# The **/static ensures that we also search within +# /openstack_dashboard/dashboards/XYZ/static which will ensure +# that plugins are also translated. +[angular: **/static/**.html] diff --git a/devstack/plugin.sh b/devstack/plugin.sh new file mode 100644 index 0000000..6d89c6f --- /dev/null +++ b/devstack/plugin.sh @@ -0,0 +1,60 @@ +# plugin.sh - DevStack plugin.sh dispatch script meteos-ui + +METEOS_UI_DIR=$(cd $(dirname $BASH_SOURCE)/.. && pwd) + +function install_meteos_ui { + # NOTE(shu-mutou): workaround for devstack bug: 1540328 + # where devstack install 'test-requirements' but should not do it + # for meteos-ui project as it installs Horizon from url. + # Remove following two 'mv' commands when mentioned bug is fixed. + mv $METEOS_UI_DIR/test-requirements.txt $METEOS_UI_DIR/_test-requirements.txt + + setup_develop ${METEOS_UI_DIR} + + mv $METEOS_UI_DIR/_test-requirements.txt $METEOS_UI_DIR/test-requirements.txt +} + +function configure_meteos_ui { + cp -a ${METEOS_UI_DIR}/meteos_ui/enabled/* ${DEST}/horizon/openstack_dashboard/local/enabled/ + # NOTE: If locale directory does not exist, compilemessages will fail, + # so check for an existence of locale directory is required. + if [ -d ${METEOS_UI_DIR}/meteos_ui/locale ]; then + (cd ${METEOS_UI_DIR}/meteos_ui; DJANGO_SETTINGS_MODULE=openstack_dashboard.settings ../manage.py compilemessages) + fi +} + +# check for service enabled +if is_service_enabled meteos-ui; then + + if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then + # Set up system services + # no-op + : + + elif [[ "$1" == "stack" && "$2" == "install" ]]; then + # Perform installation of service source + echo_summary "Installing Meteos UI" + install_meteos_ui + + elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then + # Configure after the other layer 1 and 2 services have been configured + echo_summary "Configurng Meteos UI" + configure_meteos_ui + + elif [[ "$1" == "stack" && "$2" == "extra" ]]; then + # no-op + : + fi + + if [[ "$1" == "unstack" ]]; then + # no-op + : + fi + + if [[ "$1" == "clean" ]]; then + # Remove state and transient data + # Remember clean.sh first calls unstack.sh + # no-op + : + fi +fi diff --git a/devstack/settings b/devstack/settings new file mode 100644 index 0000000..541a4ab --- /dev/null +++ b/devstack/settings @@ -0,0 +1,2 @@ +# settings file for meteos-ui plugin +enable_service meteos-ui diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..369a7cc --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,152 @@ +# Makefile for Sphinx documentation + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Meteos-UI.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Meteos-UI.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Meteos-UI" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Meteos-UI" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 0000000..c197b29 --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,441 @@ +# 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. +# +# Horizon documentation build configuration file, created by +# sphinx-quickstart on Thu Oct 27 11:38:59 2011. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +from __future__ import print_function + +import os +import subprocess +import sys + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) + +sys.path.insert(0, ROOT) + +# This is required for ReadTheDocs.org, but isn't a bad idea anyway. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', + 'meteos_ui.test.settings') + +from meteos_ui \ + import version as meteosui_ver + + +def write_autodoc_index(): + + def find_autodoc_modules(module_name, sourcedir): + """returns a list of modules in the SOURCE directory.""" + modlist = [] + os.chdir(os.path.join(sourcedir, module_name)) + print("SEARCHING %s" % sourcedir) + for root, dirs, files in os.walk("."): + for filename in files: + if filename == 'tests.py': + continue + if filename.endswith(".py"): + # remove the pieces of the root + elements = root.split(os.path.sep) + # replace the leading "." with the module name + elements[0] = module_name + # and get the base module name + base, extension = os.path.splitext(filename) + if not (base == "__init__"): + elements.append(base) + result = ".".join(elements) + # print result + modlist.append(result) + return modlist + + RSTDIR = os.path.abspath(os.path.join(BASE_DIR, "sourcecode")) + SRCS = [('meteos_ui', ROOT), ] + + EXCLUDED_MODULES = () + CURRENT_SOURCES = {} + + if not(os.path.exists(RSTDIR)): + os.mkdir(RSTDIR) + CURRENT_SOURCES[RSTDIR] = ['autoindex.rst'] + + INDEXOUT = open(os.path.join(RSTDIR, "autoindex.rst"), "w") + INDEXOUT.write(""" +================= +Source Code Index +================= + +.. contents:: + :depth: 1 + :local: + +""") + + for modulename, path in SRCS: + sys.stdout.write("Generating source documentation for %s\n" % + modulename) + INDEXOUT.write("\n%s\n" % modulename.capitalize()) + INDEXOUT.write("%s\n" % ("=" * len(modulename),)) + INDEXOUT.write(".. toctree::\n") + INDEXOUT.write(" :maxdepth: 1\n") + INDEXOUT.write("\n") + + MOD_DIR = os.path.join(RSTDIR, modulename) + CURRENT_SOURCES[MOD_DIR] = [] + if not(os.path.exists(MOD_DIR)): + os.mkdir(MOD_DIR) + for module in find_autodoc_modules(modulename, path): + if any([module.startswith(exclude) for exclude + in EXCLUDED_MODULES]): + print("Excluded module %s." % module) + continue + mod_path = os.path.join(path, *module.split(".")) + generated_file = os.path.join(MOD_DIR, "%s.rst" % module) + + INDEXOUT.write(" %s/%s\n" % (modulename, module)) + + # Find the __init__.py module if this is a directory + if os.path.isdir(mod_path): + source_file = ".".join((os.path.join(mod_path, "__init__"), + "py",)) + else: + source_file = ".".join((os.path.join(mod_path), "py")) + + CURRENT_SOURCES[MOD_DIR].append("%s.rst" % module) + # Only generate a new file if the source has changed or we don't + # have a doc file to begin with. + if not os.access(generated_file, os.F_OK) or ( + os.stat(generated_file).st_mtime < + os.stat(source_file).st_mtime): + print("Module %s updated, generating new documentation." + % module) + FILEOUT = open(generated_file, "w") + header = "The :mod:`%s` Module" % module + FILEOUT.write("%s\n" % ("=" * len(header),)) + FILEOUT.write("%s\n" % header) + FILEOUT.write("%s\n" % ("=" * len(header),)) + FILEOUT.write(".. automodule:: %s\n" % module) + FILEOUT.write(" :members:\n") + FILEOUT.write(" :undoc-members:\n") + FILEOUT.write(" :show-inheritance:\n") + FILEOUT.write(" :noindex:\n") + FILEOUT.close() + + INDEXOUT.close() + + # Delete auto-generated .rst files for sources which no longer exist + for directory, subdirs, files in list(os.walk(RSTDIR)): + for old_file in files: + if old_file not in CURRENT_SOURCES.get(directory, []): + print("Removing outdated file for %s" % old_file) + os.remove(os.path.join(directory, old_file)) + + +write_autodoc_index() + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ---------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. +# They can be extensions coming with Sphinx (named 'sphinx.ext.*') +# or your custom ones. +extensions = ['sphinx.ext.autodoc', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', + 'oslosphinx', + ] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Meteos UI' +copyright = u'2016, OpenStack Foundation' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = meteosui_ver.version_info.version_string() +# The full version, including alpha/beta/rc tags. +release = meteosui_ver.version_info.release_string() + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['**/#*', '**~', '**/#*#'] + +# The reST default role (used for this markup: `text`) +# to use for all documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +primary_domain = 'py' +nitpicky = False + + +# -- Options for HTML output -------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# html_theme_path = ['.'] +# html_theme = '_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +html_theme_options = { + "nosidebar": "false" +} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' +git_cmd = ["git", "log", "--pretty=format:'%ad, commit %h'", "--date=local", + "-n1"] +html_last_updated_fmt = subprocess.check_output(git_cmd, + stdin=subprocess.PIPE) + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Horizondoc' + + +# -- Options for LaTeX output ------------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto/manual]). +latex_documents = [ + ('index', 'Horizon.tex', u'Horizon Documentation', + u'OpenStack Foundation', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output ------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', u'Meteos UI Documentation', + 'Documentation for the Meteos UI plugin to the Openstack\ + Dashboard (Horizon)', + [u'OpenStack'], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ----------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Horizon', u'Horizon Documentation', u'OpenStack', + 'Horizon', 'One line description of project.', 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + + +# -- Options for Epub output -------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'Horizon' +epub_author = u'OpenStack' +epub_publisher = u'OpenStack' +epub_copyright = u'2012, OpenStack' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +# epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +# epub_scheme = '' + +# The unique identifier of the text. This can be an ISBN number +# or the project homepage. +# epub_identifier = '' + +# A unique identification for the text. +# epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +# epub_cover = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_post_files = [] + +# A list of files that should not be packed into the epub file. +# epub_exclude_files = [] + +# The depth of the table of contents in toc.ncx. +# epub_tocdepth = 3 + +# Allow duplicate toc entries. +# epub_tocdup = True diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 0000000..48f5346 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,78 @@ +========== +meteos-ui +========== + +Meteos UI + +* Free software: Apache license +* Source: http://git.openstack.org/cgit/openstack/meteos-ui +* Bugs: http://bugs.launchpad.net/meteos-ui + +Enabling in DevStack +-------------------- + +Add this repo as an external repository into your ``local.conf`` file:: + + [[local|localrc]] + enable_plugin meteos-ui https://github.com/openstack/meteos-ui + +Manual Installation +------------------- + +Begin by cloning the Horizon and Meteos UI repositories:: + + git clone https://github.com/openstack/horizon + git clone https://github.com/openstack/meteos-ui + +Create a virtual environment and install Horizon dependencies:: + + cd horizon + python tools/install_venv.py + +Set up your ``local_settings.py`` file:: + + cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py + +Open up the copied ``local_settings.py`` file in your preferred text +editor. You will want to customize several settings: + +- ``OPENSTACK_HOST`` should be configured with the hostname of your + OpenStack server. Verify that the ``OPENSTACK_KEYSTONE_URL`` and + ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` settings are correct for your + environment. (They should be correct unless you modified your + OpenStack server to change them.) + +Install Meteos UI with all dependencies in your virtual environment:: + + tools/with_venv.sh pip install -e ../meteos-ui/ + +And enable it in Horizon:: + + cp ../meteos-ui/meteos_ui/enabled/* openstack_dashboard/local/enabled/ + +To run horizon with the newly enabled Meteos UI plugin run:: + + ./run_tests.sh --runserver 0.0.0.0:8080 + +to have the application start on port 8080 and the horizon dashboard will be +available in your browser at http://localhost:8080/ + +Release Notes +============= + +.. toctree:: + :glob: + :maxdepth: 1 + + releases/* + +Source Code Reference +===================== + +.. toctree:: + :glob: + :maxdepth: 1 + + sourcecode/autoindex + + diff --git a/doc/source/releases/0.1.0.rst b/doc/source/releases/0.1.0.rst new file mode 100644 index 0000000..489d83b --- /dev/null +++ b/doc/source/releases/0.1.0.rst @@ -0,0 +1,2 @@ +Meteos UI 0.1.0 +============ diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..172fb99 --- /dev/null +++ b/manage.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +# 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. + +import os +import sys + +from django.core.management import execute_from_command_line # noqa + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", + "meteos_ui.test.settings") + execute_from_command_line(sys.argv) diff --git a/meteos_ui/__init__.py b/meteos_ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/api/__init__.py b/meteos_ui/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/api/client.py b/meteos_ui/api/client.py new file mode 100644 index 0000000..b4d5d24 --- /dev/null +++ b/meteos_ui/api/client.py @@ -0,0 +1,226 @@ +# 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 horizon import exceptions +from horizon.utils.memoized import memoized +import logging +from openstack_dashboard.api import base +from meteosclient.api import client as meteos_client + + +LOG = logging.getLogger(__name__) + +TEMPLATE_CREATE_ATTRS = ['display_name', 'display_description', 'image_id', + 'master_nodes_num', 'master_flavor_id', + 'worker_nodes_num', 'worker_flavor_id', + 'spark_version', 'floating_ip_pool'] +EXPERIMENT_CREATE_ATTRS = ['display_name', 'display_description', 'key_name', + 'neutron_management_network', 'template_id'] +DATASET_CREATE_ATTRS = ['method', 'source_dataset_url', 'display_name', + 'display_description', 'experiment_id', 'params', + 'swift_tenant', 'swift_username', 'swift_password'] +MODEL_CREATE_ATTRS = ['source_dataset_url', 'display_name', + 'display_description', 'experiment_id', + 'model_type', 'model_params', 'dataset_format', + 'swift_tenant', 'swift_username', 'swift_password'] +MODEL_EVA_CREATE_ATTRS = ['source_dataset_url', 'display_name', + 'display_description', 'model_id', 'dataset_format', + 'swift_tenant', 'swift_username', 'swift_password'] +LEARNING_CREATE_ATTRS = ['display_name', 'display_description', + 'model_id', 'method', 'args'] + + +@memoized +def meteosclient(request): + meteos_url = "" + try: + meteos_url = base.url_for(request, 'machine-learning') + except exceptions.ServiceCatalogException: + LOG.debug('No Machine Learning service is configured.') + return None + + LOG.debug('meteosclient connection created using the token "%s" and url' + '"%s"' % (request.user.token.id, meteos_url)) + c = meteos_client.Client(username=request.user.username, + project_id=request.user.tenant_id, + input_auth_token=request.user.token.id, + meteos_url=meteos_url) + return c + + +def template_create(request, **kwargs): + args = {} + for (key, value) in kwargs.items(): + if key in TEMPLATE_CREATE_ATTRS: + args[str(key)] = str(value) + else: + raise exceptions.BadRequest( + "Key must be in %s" % ",".join(TEMPLATE_CREATE_ATTRS)) + return meteosclient(request).templates.create(**args) + + +def template_delete(request, id): + return meteosclient(request).templates.delete(id) + + +def template_list(request, search_opts=None, limit=None, marker=None, + sort_key=None, sort_dir=None): + return meteosclient(request).templates.list(search_opts, + limit, + marker, + sort_key, + sort_dir) + + +def template_show(request, id): + return meteosclient(request).templates.get(id) + + +def experiment_create(request, **kwargs): + args = {} + for (key, value) in kwargs.items(): + if key in EXPERIMENT_CREATE_ATTRS: + args[str(key)] = str(value) + else: + raise exceptions.BadRequest( + "Key must be in %s" % ",".join(EXPERIMENT_CREATE_ATTRS)) + return meteosclient(request).experiments.create(**args) + + +def experiment_delete(request, id): + return meteosclient(request).experiments.delete(id) + + +def experiment_list(request, search_opts=None, limit=None, marker=None, + sort_key=None, sort_dir=None): + return meteosclient(request).experiments.list(search_opts, + limit, + marker, + sort_key, + sort_dir) + + +def experiment_show(request, id): + return meteosclient(request).experiments.get(id) + + +def dataset_create(request, **kwargs): + args = {} + for (key, value) in kwargs.items(): + if key in DATASET_CREATE_ATTRS: + args[str(key)] = str(value) + else: + raise exceptions.BadRequest( + "Key must be in %s" % ",".join(DATASET_CREATE_ATTRS)) + return meteosclient(request).datasets.create(**args) + + +def dataset_delete(request, id): + return meteosclient(request).datasets.delete(id) + + +def dataset_list(request, search_opts=None, limit=None, marker=None, + sort_key=None, sort_dir=None): + return meteosclient(request).datasets.list(search_opts, + limit, + marker, + sort_key, + sort_dir) + + +def dataset_show(request, id): + return meteosclient(request).datasets.get(id) + + +def model_create(request, **kwargs): + args = {} + for (key, value) in kwargs.items(): + if key in MODEL_CREATE_ATTRS: + args[str(key)] = str(value) + else: + raise exceptions.BadRequest( + "Key must be in %s" % ",".join(MODEL_CREATE_ATTRS)) + return meteosclient(request).models.create(**args) + + +def model_delete(request, id): + return meteosclient(request).models.delete(id) + + +def model_list(request, search_opts=None, limit=None, marker=None, + sort_key=None, sort_dir=None): + return meteosclient(request).models.list(search_opts, + limit, + marker, + sort_key, + sort_dir) + + +def model_show(request, id): + return meteosclient(request).models.get(id) + + +def model_evaluation_create(request, **kwargs): + args = {} + for (key, value) in kwargs.items(): + if key in MODEL_EVA_CREATE_ATTRS: + args[str(key)] = str(value) + else: + raise exceptions.BadRequest( + "Key must be in %s" % ",".join(MODEL_EVA_CREATE_ATTRS)) + return meteosclient(request).model_evaluations.create(**args) + + +def model_evaluation_delete(request, id): + return meteosclient(request).model_evaluations.delete(id) + + +def model_evaluation_list(request, search_opts=None, limit=None, marker=None, + sort_key=None, sort_dir=None): + return meteosclient(request).model_evaluations.list(search_opts, + limit, + marker, + sort_key, + sort_dir) + + +def model_evaluation_show(request, id): + return meteosclient(request).model_evaluations.get(id) + + +def learning_create(request, **kwargs): + args = {} + for (key, value) in kwargs.items(): + if key in LEARNING_CREATE_ATTRS: + args[str(key)] = str(value) + else: + raise exceptions.BadRequest( + "Key must be in %s" % ",".join(LEARNING_CREATE_ATTRS)) + return meteosclient(request).learnings.create(**args) + + +def learning_delete(request, id): + return meteosclient(request).learnings.delete(id) + + +def learning_list(request, search_opts=None, limit=None, marker=None, + sort_key=None, sort_dir=None): + return meteosclient(request).learnings.list(search_opts, + limit, + marker, + sort_key, + sort_dir) + + +def learning_show(request, id): + return meteosclient(request).learnings.get(id) diff --git a/meteos_ui/api/rest_api.py b/meteos_ui/api/rest_api.py new file mode 100644 index 0000000..3d615b7 --- /dev/null +++ b/meteos_ui/api/rest_api.py @@ -0,0 +1,337 @@ +# 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 import generic + +from meteos_ui.api import client + +from openstack_dashboard.api.rest import urls +from openstack_dashboard.api.rest import utils as rest_utils + + +@urls.register +class Template(generic.View): + """API for retrieving a single template""" + url_regex = r'meteos/templates/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, id): + """Get a specific template""" + return change_to_id(client.template_show(request, id).to_dict()) + + +@urls.register +class TemplateActions(generic.View): + """API for retrieving a single template""" + url_regex = r'meteos/templates/(?P[^/]+)/(?P[^/]+)$' + + +@urls.register +class Templates(generic.View): + """API for Meteos Templates""" + url_regex = r'meteos/templates/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the Templates for a project. + + The returned result is an object with property 'items' and each + item under this is a Template. + """ + result = client.template_list(request) + return {'items': [n.to_dict() for n in result]} + + @rest_utils.ajax(data_required=True) + def delete(self, request): + """Delete one or more Templates by id. + + Returns HTTP 204 (no content) on successful deletion. + """ + for id in request.DATA: + client.template_delete(request, id) + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a new Template. + + Returns the new Template object on success. + """ + new_template = client.template_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/meteos/template/%s' % new_template.id, + new_template.to_dict()) + + +@urls.register +class Experiment(generic.View): + """API for retrieving a single experiment""" + url_regex = r'meteos/experiments/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, id): + """Get a specific experiment""" + return change_to_id(client.experiment_show(request, id).to_dict()) + + +@urls.register +class ExperimentActions(generic.View): + """API for retrieving a single experiment""" + url_regex = r'meteos/experiments/(?P[^/]+)/(?P[^/]+)$' + + +@urls.register +class Experiments(generic.View): + """API for Meteos Experiments""" + url_regex = r'meteos/experiments/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the Experiments for a project. + + The returned result is an object with property 'items' and each + item under this is a Experiment. + """ + result = client.experiment_list(request) + return {'items': [n.to_dict() for n in result]} + + @rest_utils.ajax(data_required=True) + def delete(self, request): + """Delete one or more Experiments by id. + + Returns HTTP 204 (no content) on successful deletion. + """ + for id in request.DATA: + client.experiment_delete(request, id) + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a new Experiment. + + Returns the new Experiment object on success. + """ + new_experiment = client.experiment_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/meteos/experiment/%s' % new_experiment.id, + new_experiment.to_dict()) + + +@urls.register +class Dataset(generic.View): + """API for retrieving a single dataset""" + url_regex = r'meteos/datasets/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, id): + """Get a specific dataset""" + return change_to_id(client.dataset_show(request, id).to_dict()) + + +@urls.register +class DatasetActions(generic.View): + """API for retrieving a single dataset""" + url_regex = r'meteos/datasets/(?P[^/]+)/(?P[^/]+)$' + + +@urls.register +class Datasets(generic.View): + """API for Meteos Datasets""" + url_regex = r'meteos/datasets/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the Datasets for a project. + + The returned result is an object with property 'items' and each + item under this is a Dataset. + """ + result = client.dataset_list(request) + return {'items': [n.to_dict() for n in result]} + + @rest_utils.ajax(data_required=True) + def delete(self, request): + """Delete one or more Datasets by id. + + Returns HTTP 204 (no content) on successful deletion. + """ + for id in request.DATA: + client.dataset_delete(request, id) + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a new Dataset. + + Returns the new Dataset object on success. + """ + new_dataset = client.dataset_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/meteos/dataset/%s' % new_dataset.id, + new_dataset.to_dict()) + + +@urls.register +class Model(generic.View): + """API for retrieving a single model""" + url_regex = r'meteos/models/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, id): + """Get a specific model""" + return change_to_id(client.model_show(request, id).to_dict()) + + +@urls.register +class ModelActions(generic.View): + """API for retrieving a single model""" + url_regex = r'meteos/models/(?P[^/]+)/(?P[^/]+)$' + + +@urls.register +class Models(generic.View): + """API for Meteos Models""" + url_regex = r'meteos/models/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the Models for a project. + + The returned result is an object with property 'items' and each + item under this is a Model. + """ + result = client.model_list(request) + return {'items': [n.to_dict() for n in result]} + + @rest_utils.ajax(data_required=True) + def delete(self, request): + """Delete one or more Models by id. + + Returns HTTP 204 (no content) on successful deletion. + """ + for id in request.DATA: + client.model_delete(request, id) + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a new Model. + + Returns the new Model object on success. + """ + new_model = client.model_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/meteos/model/%s' % new_model.id, + new_model.to_dict()) + + +@urls.register +class ModelEvaluation(generic.View): + """API for retrieving a single model_evaluation""" + url_regex = r'meteos/model_evaluations/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, id): + """Get a specific model_evaluation""" + return change_to_id(client.model_evaluation_show(request, id).to_dict()) + + +@urls.register +class ModelEvaluationActions(generic.View): + """API for retrieving a single model_evaluation""" + url_regex = r'meteos/model_evaluations/(?P[^/]+)/(?P[^/]+)$' + + +@urls.register +class ModelEvaluations(generic.View): + """API for Meteos ModelEvaluations""" + url_regex = r'meteos/model_evaluations/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the ModelEvaluations for a project. + + The returned result is an object with property 'items' and each + item under this is a ModelEvaluation. + """ + result = client.model_evaluation_list(request) + return {'items': [n.to_dict() for n in result]} + + @rest_utils.ajax(data_required=True) + def delete(self, request): + """Delete one or more ModelEvaluations by id. + + Returns HTTP 204 (no content) on successful deletion. + """ + for id in request.DATA: + client.model_evaluation_delete(request, id) + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a new ModelEvaluation. + + Returns the new ModelEvaluation object on success. + """ + new_model_evaluation = client.model_evaluation_create(request, + **request.DATA) + return rest_utils.CreatedResponse( + '/api/meteos/model_evaluation/%s' % new_model_evaluation.id, + new_model_evaluation.to_dict()) + + +@urls.register +class Learning(generic.View): + """API for retrieving a single learning""" + url_regex = r'meteos/learnings/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, id): + """Get a specific learning""" + return change_to_id(client.learning_show(request, id).to_dict()) + + +@urls.register +class LearningActions(generic.View): + """API for retrieving a single learning""" + url_regex = r'meteos/learnings/(?P[^/]+)/(?P[^/]+)$' + + +@urls.register +class Learnings(generic.View): + """API for Meteos Learnings""" + url_regex = r'meteos/learnings/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the Learnings for a project. + + The returned result is an object with property 'items' and each + item under this is a Learning. + """ + result = client.learning_list(request) + return {'items': [n.to_dict() for n in result]} + + @rest_utils.ajax(data_required=True) + def delete(self, request): + """Delete one or more Learnings by id. + + Returns HTTP 204 (no content) on successful deletion. + """ + for id in request.DATA: + client.learning_delete(request, id) + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a new Learning. + + Returns the new Learning object on success. + """ + new_learning = client.learning_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/meteos/learning/%s' % new_learning.id, + new_learning.to_dict()) diff --git a/meteos_ui/content/__init__.py b/meteos_ui/content/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/content/machine_learning/__init__.py b/meteos_ui/content/machine_learning/__init__.py new file mode 100644 index 0000000..b9127ca --- /dev/null +++ b/meteos_ui/content/machine_learning/__init__.py @@ -0,0 +1,14 @@ +# 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. + +# Register the REST API URLs so they can be called from the JavaScript files +import meteos_ui.api.rest_api # noqa diff --git a/meteos_ui/content/machine_learning/datasets/__init__.py b/meteos_ui/content/machine_learning/datasets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/content/machine_learning/datasets/panel.py b/meteos_ui/content/machine_learning/datasets/panel.py new file mode 100644 index 0000000..1b8e7b8 --- /dev/null +++ b/meteos_ui/content/machine_learning/datasets/panel.py @@ -0,0 +1,23 @@ +# 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.utils.translation import ugettext_lazy as _ +import horizon + +# This panel will be loaded from horizon, because specified in enabled file. +# To register REST api, import below here. +from meteos_ui.api import rest_api # noqa + + +class Datasets(horizon.Panel): + name = _("Datasets") + slug = "machine_learning.datasets" diff --git a/meteos_ui/content/machine_learning/datasets/tests.py b/meteos_ui/content/machine_learning/datasets/tests.py new file mode 100644 index 0000000..88dfd86 --- /dev/null +++ b/meteos_ui/content/machine_learning/datasets/tests.py @@ -0,0 +1,19 @@ +# 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 openstack_dashboard.test import helpers as test + + +class DatasetTests(test.TestCase): + # Unit tests for Dataset. + def test_me(self): + self.assertTrue(1 + 1 == 2) diff --git a/meteos_ui/content/machine_learning/datasets/urls.py b/meteos_ui/content/machine_learning/datasets/urls.py new file mode 100644 index 0000000..a80a028 --- /dev/null +++ b/meteos_ui/content/machine_learning/datasets/urls.py @@ -0,0 +1,20 @@ +# 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 url +from django.utils.translation import ugettext_lazy as _ +from horizon.browsers import views + +title = _("Datasets") +urlpatterns = [ + url('', views.AngularIndexView.as_view(title=title), name='index'), +] diff --git a/meteos_ui/content/machine_learning/experiments/__init__.py b/meteos_ui/content/machine_learning/experiments/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/content/machine_learning/experiments/panel.py b/meteos_ui/content/machine_learning/experiments/panel.py new file mode 100644 index 0000000..248a79d --- /dev/null +++ b/meteos_ui/content/machine_learning/experiments/panel.py @@ -0,0 +1,23 @@ +# 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.utils.translation import ugettext_lazy as _ +import horizon + +# This panel will be loaded from horizon, because specified in enabled file. +# To register REST api, import below here. +from meteos_ui.api import rest_api # noqa + + +class Experiments(horizon.Panel): + name = _("Experiments") + slug = "machine_learning.experiments" diff --git a/meteos_ui/content/machine_learning/experiments/tests.py b/meteos_ui/content/machine_learning/experiments/tests.py new file mode 100644 index 0000000..a4745b1 --- /dev/null +++ b/meteos_ui/content/machine_learning/experiments/tests.py @@ -0,0 +1,19 @@ +# 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 openstack_dashboard.test import helpers as test + + +class ExperimentTests(test.TestCase): + # Unit tests for Experiment. + def test_me(self): + self.assertTrue(1 + 1 == 2) diff --git a/meteos_ui/content/machine_learning/experiments/urls.py b/meteos_ui/content/machine_learning/experiments/urls.py new file mode 100644 index 0000000..ce771ab --- /dev/null +++ b/meteos_ui/content/machine_learning/experiments/urls.py @@ -0,0 +1,20 @@ +# 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 url +from django.utils.translation import ugettext_lazy as _ +from horizon.browsers import views + +title = _("Experiments") +urlpatterns = [ + url('', views.AngularIndexView.as_view(title=title), name='index'), +] diff --git a/meteos_ui/content/machine_learning/learnings/__init__.py b/meteos_ui/content/machine_learning/learnings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/content/machine_learning/learnings/panel.py b/meteos_ui/content/machine_learning/learnings/panel.py new file mode 100644 index 0000000..3820987 --- /dev/null +++ b/meteos_ui/content/machine_learning/learnings/panel.py @@ -0,0 +1,23 @@ +# 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.utils.translation import ugettext_lazy as _ +import horizon + +# This panel will be loaded from horizon, because specified in enabled file. +# To register REST api, import below here. +from meteos_ui.api import rest_api # noqa + + +class Learnings(horizon.Panel): + name = _("Learnings") + slug = "machine_learning.learnings" diff --git a/meteos_ui/content/machine_learning/learnings/tests.py b/meteos_ui/content/machine_learning/learnings/tests.py new file mode 100644 index 0000000..58105ba --- /dev/null +++ b/meteos_ui/content/machine_learning/learnings/tests.py @@ -0,0 +1,19 @@ +# 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 openstack_dashboard.test import helpers as test + + +class LearningTests(test.TestCase): + # Unit tests for Learning. + def test_me(self): + self.assertTrue(1 + 1 == 2) diff --git a/meteos_ui/content/machine_learning/learnings/urls.py b/meteos_ui/content/machine_learning/learnings/urls.py new file mode 100644 index 0000000..1cb32ca --- /dev/null +++ b/meteos_ui/content/machine_learning/learnings/urls.py @@ -0,0 +1,20 @@ +# 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 url +from django.utils.translation import ugettext_lazy as _ +from horizon.browsers import views + +title = _("Learnings") +urlpatterns = [ + url('', views.AngularIndexView.as_view(title=title), name='index'), +] diff --git a/meteos_ui/content/machine_learning/model_evaluations/__init__.py b/meteos_ui/content/machine_learning/model_evaluations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/content/machine_learning/model_evaluations/panel.py b/meteos_ui/content/machine_learning/model_evaluations/panel.py new file mode 100644 index 0000000..ee60aa5 --- /dev/null +++ b/meteos_ui/content/machine_learning/model_evaluations/panel.py @@ -0,0 +1,23 @@ +# 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.utils.translation import ugettext_lazy as _ +import horizon + +# This panel will be loaded from horizon, because specified in enabled file. +# To register REST api, import below here. +from meteos_ui.api import rest_api # noqa + + +class ModelEvaluations(horizon.Panel): + name = _("ModelEvaluations") + slug = "machine_learning.model_evaluations" diff --git a/meteos_ui/content/machine_learning/model_evaluations/tests.py b/meteos_ui/content/machine_learning/model_evaluations/tests.py new file mode 100644 index 0000000..7b82268 --- /dev/null +++ b/meteos_ui/content/machine_learning/model_evaluations/tests.py @@ -0,0 +1,19 @@ +# 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 openstack_dashboard.test import helpers as test + + +class ModelEvaluationTests(test.TestCase): + # Unit tests for ModelEvaluation. + def test_me(self): + self.assertTrue(1 + 1 == 2) diff --git a/meteos_ui/content/machine_learning/model_evaluations/urls.py b/meteos_ui/content/machine_learning/model_evaluations/urls.py new file mode 100644 index 0000000..ad640eb --- /dev/null +++ b/meteos_ui/content/machine_learning/model_evaluations/urls.py @@ -0,0 +1,20 @@ +# 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 url +from django.utils.translation import ugettext_lazy as _ +from horizon.browsers import views + +title = _("ModelEvaluations") +urlpatterns = [ + url('', views.AngularIndexView.as_view(title=title), name='index'), +] diff --git a/meteos_ui/content/machine_learning/models/__init__.py b/meteos_ui/content/machine_learning/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/content/machine_learning/models/panel.py b/meteos_ui/content/machine_learning/models/panel.py new file mode 100644 index 0000000..8b5a649 --- /dev/null +++ b/meteos_ui/content/machine_learning/models/panel.py @@ -0,0 +1,23 @@ +# 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.utils.translation import ugettext_lazy as _ +import horizon + +# This panel will be loaded from horizon, because specified in enabled file. +# To register REST api, import below here. +from meteos_ui.api import rest_api # noqa + + +class Models(horizon.Panel): + name = _("Models") + slug = "machine_learning.models" diff --git a/meteos_ui/content/machine_learning/models/tests.py b/meteos_ui/content/machine_learning/models/tests.py new file mode 100644 index 0000000..3406af1 --- /dev/null +++ b/meteos_ui/content/machine_learning/models/tests.py @@ -0,0 +1,19 @@ +# 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 openstack_dashboard.test import helpers as test + + +class ModelTests(test.TestCase): + # Unit tests for Model. + def test_me(self): + self.assertTrue(1 + 1 == 2) diff --git a/meteos_ui/content/machine_learning/models/urls.py b/meteos_ui/content/machine_learning/models/urls.py new file mode 100644 index 0000000..dacfe3c --- /dev/null +++ b/meteos_ui/content/machine_learning/models/urls.py @@ -0,0 +1,20 @@ +# 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 url +from django.utils.translation import ugettext_lazy as _ +from horizon.browsers import views + +title = _("Models") +urlpatterns = [ + url('', views.AngularIndexView.as_view(title=title), name='index'), +] diff --git a/meteos_ui/content/machine_learning/templates/__init__.py b/meteos_ui/content/machine_learning/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/content/machine_learning/templates/panel.py b/meteos_ui/content/machine_learning/templates/panel.py new file mode 100644 index 0000000..786749a --- /dev/null +++ b/meteos_ui/content/machine_learning/templates/panel.py @@ -0,0 +1,23 @@ +# 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.utils.translation import ugettext_lazy as _ +import horizon + +# This panel will be loaded from horizon, because specified in enabled file. +# To register REST api, import below here. +from meteos_ui.api import rest_api # noqa + + +class Templates(horizon.Panel): + name = _("Templates") + slug = "machine_learning.templates" diff --git a/meteos_ui/content/machine_learning/templates/tests.py b/meteos_ui/content/machine_learning/templates/tests.py new file mode 100644 index 0000000..9a8e20f --- /dev/null +++ b/meteos_ui/content/machine_learning/templates/tests.py @@ -0,0 +1,19 @@ +# 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 openstack_dashboard.test import helpers as test + + +class TemplateTests(test.TestCase): + # Unit tests for Template. + def test_me(self): + self.assertTrue(1 + 1 == 2) diff --git a/meteos_ui/content/machine_learning/templates/urls.py b/meteos_ui/content/machine_learning/templates/urls.py new file mode 100644 index 0000000..c64eab2 --- /dev/null +++ b/meteos_ui/content/machine_learning/templates/urls.py @@ -0,0 +1,20 @@ +# 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 url +from django.utils.translation import ugettext_lazy as _ +from horizon.browsers import views + +title = _("Templates") +urlpatterns = [ + url('', views.AngularIndexView.as_view(title=title), name='index'), +] diff --git a/meteos_ui/enabled/_1710_machine_learning_panel_group.py b/meteos_ui/enabled/_1710_machine_learning_panel_group.py new file mode 100644 index 0000000..7834cdd --- /dev/null +++ b/meteos_ui/enabled/_1710_machine_learning_panel_group.py @@ -0,0 +1,36 @@ +# 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.utils.translation import ugettext_lazy as _ + +# The slug of the panel group to be added to HORIZON_CONFIG. Required. +PANEL_GROUP = 'machine_learning' +# The display name of the PANEL_GROUP. Required. +PANEL_GROUP_NAME = _('Machine Learning') +# The slug of the dashboard the PANEL_GROUP associated with. Required. +PANEL_GROUP_DASHBOARD = 'project' + +ADD_INSTALLED_APPS = ['meteos_ui'] + +ADD_ANGULAR_MODULES = [ + 'horizon.dashboard.machine_learning' +] + +ADD_JS_FILES = [ + 'horizon/lib/angular/angular-route.js' +] + +ADD_SCSS_FILES = [ + 'dashboard/machine_learning/machine_learning.scss' +] + +AUTO_DISCOVER_STATIC_FILES = True diff --git a/meteos_ui/enabled/_1720_machine_learning_templates_panel.py b/meteos_ui/enabled/_1720_machine_learning_templates_panel.py new file mode 100644 index 0000000..030cd6e --- /dev/null +++ b/meteos_ui/enabled/_1720_machine_learning_templates_panel.py @@ -0,0 +1,21 @@ +# 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. + +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'machine_learning.templates' +# The slug of the panel group the PANEL is associated with. +PANEL_GROUP = 'machine_learning' +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'project' + +# Python panel class of the PANEL to be added. +ADD_PANEL = 'meteos_ui.content.machine_learning.templates.panel.Templates' diff --git a/meteos_ui/enabled/_1730_machine_learning_experiments_panel.py b/meteos_ui/enabled/_1730_machine_learning_experiments_panel.py new file mode 100644 index 0000000..781b0f0 --- /dev/null +++ b/meteos_ui/enabled/_1730_machine_learning_experiments_panel.py @@ -0,0 +1,21 @@ +# 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. + +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'machine_learning.experiments' +# The slug of the panel group the PANEL is associated with. +PANEL_GROUP = 'machine_learning' +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'project' + +# Python panel class of the PANEL to be added. +ADD_PANEL = 'meteos_ui.content.machine_learning.experiments.panel.Experiments' diff --git a/meteos_ui/enabled/_1740_machine_learning_datasets_panel.py b/meteos_ui/enabled/_1740_machine_learning_datasets_panel.py new file mode 100644 index 0000000..4dbc29a --- /dev/null +++ b/meteos_ui/enabled/_1740_machine_learning_datasets_panel.py @@ -0,0 +1,21 @@ +# 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. + +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'machine_learning.datasets' +# The slug of the panel group the PANEL is associated with. +PANEL_GROUP = 'machine_learning' +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'project' + +# Python panel class of the PANEL to be added. +ADD_PANEL = 'meteos_ui.content.machine_learning.datasets.panel.Datasets' diff --git a/meteos_ui/enabled/_1750_machine_learning_models_panel.py b/meteos_ui/enabled/_1750_machine_learning_models_panel.py new file mode 100644 index 0000000..e673399 --- /dev/null +++ b/meteos_ui/enabled/_1750_machine_learning_models_panel.py @@ -0,0 +1,21 @@ +# 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. + +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'machine_learning.models' +# The slug of the panel group the PANEL is associated with. +PANEL_GROUP = 'machine_learning' +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'project' + +# Python panel class of the PANEL to be added. +ADD_PANEL = 'meteos_ui.content.machine_learning.models.panel.Models' diff --git a/meteos_ui/enabled/_1760_machine_learning_model_evaluations_panel.py b/meteos_ui/enabled/_1760_machine_learning_model_evaluations_panel.py new file mode 100644 index 0000000..7792c96 --- /dev/null +++ b/meteos_ui/enabled/_1760_machine_learning_model_evaluations_panel.py @@ -0,0 +1,21 @@ +# 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. + +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'machine_learning.model_evaluations' +# The slug of the panel group the PANEL is associated with. +PANEL_GROUP = 'machine_learning' +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'project' + +# Python panel class of the PANEL to be added. +ADD_PANEL = 'meteos_ui.content.machine_learning.model_evaluations.panel.ModelEvaluations' diff --git a/meteos_ui/enabled/_1770_machine_learning_learnings_panel.py b/meteos_ui/enabled/_1770_machine_learning_learnings_panel.py new file mode 100644 index 0000000..ebc4da4 --- /dev/null +++ b/meteos_ui/enabled/_1770_machine_learning_learnings_panel.py @@ -0,0 +1,21 @@ +# 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. + +# The slug of the panel to be added to HORIZON_CONFIG. Required. +PANEL = 'machine_learning.learnings' +# The slug of the panel group the PANEL is associated with. +PANEL_GROUP = 'machine_learning' +# The slug of the dashboard the PANEL associated with. Required. +PANEL_DASHBOARD = 'project' + +# Python panel class of the PANEL to be added. +ADD_PANEL = 'meteos_ui.content.machine_learning.learnings.panel.Learnings' diff --git a/meteos_ui/enabled/__init__.py b/meteos_ui/enabled/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/karma.conf.js b/meteos_ui/karma.conf.js new file mode 100644 index 0000000..c4e8b5b --- /dev/null +++ b/meteos_ui/karma.conf.js @@ -0,0 +1,154 @@ +/* + * 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. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); + +module.exports = function (config) { + // This tox venv is setup in the post-install npm step + var toxPath = '../.tox/py27/lib/python2.7/site-packages/'; + + config.set({ + preprocessors: { + // Used to collect templates for preprocessing. + // NOTE: the templates must also be listed in the files section below. + './static/**/*.html': ['ng-html2js'], + // Used to indicate files requiring coverage reports. + './static/**/!(*.spec).js': ['coverage'], + }, + + // Sets up module to process templates. + ngHtml2JsPreprocessor: { + prependPrefix: '/', + moduleName: 'templates' + }, + + basePath: './', + + // Contains both source and test files. + files: [ + /* + * shim, partly stolen from /i18n/js/horizon/ + * Contains expected items not provided elsewhere (dynamically by + * Django or via jasmine template. + */ + '../test-shim.js', + + // from jasmine.html + toxPath + 'xstatic/pkg/jquery/data/jquery.js', + toxPath + 'xstatic/pkg/angular/data/angular.js', + toxPath + 'xstatic/pkg/angular/data/angular-route.js', + toxPath + 'xstatic/pkg/angular/data/angular-mocks.js', + toxPath + 'xstatic/pkg/angular/data/angular-cookies.js', + toxPath + 'xstatic/pkg/angular_bootstrap/data/angular-bootstrap.js', + toxPath + 'xstatic/pkg/angular_gettext/data/angular-gettext.js', + toxPath + 'xstatic/pkg/angular_fileupload/data/ng-file-upload-all.js', + toxPath + 'xstatic/pkg/angular/data/angular-sanitize.js', + toxPath + 'xstatic/pkg/d3/data/d3.js', + toxPath + 'xstatic/pkg/rickshaw/data/rickshaw.js', + toxPath + 'xstatic/pkg/angular_smart_table/data/smart-table.js', + toxPath + 'xstatic/pkg/angular_lrdragndrop/data/lrdragndrop.js', + toxPath + 'xstatic/pkg/spin/data/spin.js', + toxPath + 'xstatic/pkg/spin/data/spin.jquery.js', + toxPath + 'xstatic/pkg/tv4/data/tv4.js', + toxPath + 'xstatic/pkg/objectpath/data/ObjectPath.js', + toxPath + 'xstatic/pkg/angular_schema_form/data/schema-form.js', + + // TODO: These should be mocked. + toxPath + '/horizon/static/horizon/js/horizon.js', + + /** + * Include framework source code from horizon that we need. + * Otherwise, karma will not be able to find them when testing. + * These files should be mocked in the foreseeable future. + */ + toxPath + 'horizon/static/framework/**/*.module.js', + toxPath + 'horizon/static/framework/**/!(*.spec|*.mock).js', + toxPath + 'openstack_dashboard/static/**/*.module.js', + toxPath + 'openstack_dashboard/static/**/!(*.spec|*.mock).js', + toxPath + 'openstack_dashboard/dashboards/**/static/**/*.module.js', + toxPath + 'openstack_dashboard/dashboards/**/static/**/!(*.spec|*.mock).js', + + /** + * First, list all the files that defines application's angular modules. + * Those files have extension of `.module.js`. The order among them is + * not significant. + */ + './static/**/*.module.js', + + /** + * Followed by other JavaScript files that defines angular providers + * on the modules defined in files listed above. And they are not mock + * files or spec files defined below. The order among them is not + * significant. + */ + './static/**/!(*.spec|*.mock).js', + + /** + * Then, list files for mocks with `mock.js` extension. The order + * among them should not be significant. + */ + toxPath + 'openstack_dashboard/static/**/*.mock.js', + //'./static/**/*.mock.js', + + /** + * Finally, list files for spec with `spec.js` extension. The order + * among them should not be significant. + */ + './static/**/*.spec.js', + + /** + * Angular external templates + */ + './static/**/*.html' + ], + + autoWatch: true, + + frameworks: ['jasmine'], + + browsers: ['Chrome'], + + phantomjsLauncher: { + // Have phantomjs exit if a ResourceError is encountered + // (useful if karma exits without killing phantom) + exitOnResourceError: true + }, + + reporters: ['progress', 'coverage', 'threshold'], + + plugins: [ + 'karma-chrome-launcher', + 'karma-jasmine', + 'karma-ng-html2js-preprocessor', + 'karma-coverage', + 'karma-threshold-reporter' + ], + + coverageReporter: { + type: 'html', + dir: '../cover/meteos_ui' + }, + + // Coverage threshold values. + thresholdReporter: { + statements: 45, + branches: 0, + functions: 15, + lines: 45 + } + }); +}; diff --git a/meteos_ui/models.py b/meteos_ui/models.py new file mode 100644 index 0000000..1b3d5f9 --- /dev/null +++ b/meteos_ui/models.py @@ -0,0 +1,3 @@ +""" +Stub file to work around django bug: https://code.djangoproject.com/ticket/7198 +""" diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/actions.module.js b/meteos_ui/static/dashboard/machine_learning/datasets/actions.module.js new file mode 100644 index 0000000..2502f8e --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/actions.module.js @@ -0,0 +1,66 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.machine_learning.datasets.actions + * + * @description + * Provides all of the actions for datasets. + */ + angular.module('horizon.dashboard.machine_learning.datasets.actions', ['horizon.framework', 'horizon.dashboard.machine_learning']) + .run(registerDatasetActions); + + registerDatasetActions.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.dashboard.machine_learning.datasets.create.service', + 'horizon.dashboard.machine_learning.datasets.delete.service', + 'horizon.dashboard.machine_learning.datasets.resourceType', + ]; + + function registerDatasetActions( + registry, + gettext, + createDatasetService, + deleteDatasetService, + resourceType) + { + var datasetsResourceType = registry.getResourceType(resourceType); + + datasetsResourceType.globalActions + .append({ + id: 'createDatasetAction', + service: createDatasetService, + template: { + type: 'create', + text: gettext('Create Dataset') + } + }); + + datasetsResourceType.batchActions + .append({ + id: 'batchDeleteDatasetAction', + service: deleteDatasetService, + template: { + type: 'delete-selected', + text: gettext('Delete Datasets') + } + }); + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/create/create-workflow.service.js b/meteos_ui/static/dashboard/machine_learning/datasets/create/create-workflow.service.js new file mode 100644 index 0000000..7454e1d --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/create/create-workflow.service.js @@ -0,0 +1,56 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.datasets') + .factory('horizon.dashboard.machine_learning.datasets.workflow', datasetWorkflow); + + datasetWorkflow.$inject = [ + 'horizon.dashboard.machine_learning.basePath', + 'horizon.app.core.workflow.factory', + 'horizon.framework.util.i18n.gettext' + ]; + + function datasetWorkflow(basePath, dashboardWorkflow, gettext) { + return dashboardWorkflow({ + title: gettext('Create Dataset'), + + steps: [ + { + title: gettext('Info'), + templateUrl: basePath + 'datasets/create/info/info.html', + helpUrl: basePath + 'datasets/create/info/info.help.html', + formName: 'datasetInfoForm' + }, + { + title: gettext('Spec'), + templateUrl: basePath + 'datasets/create/spec/spec.html', + helpUrl: basePath + 'datasets/create/spec/spec.help.html', + formName: 'datasetSpecForm' + }, + ], + + btnText: { + finish: gettext('Create') + }, + + btnIcon: { + finish: 'fa fa-check' + } + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/create/create.service.js b/meteos_ui/static/dashboard/machine_learning/datasets/create/create.service.js new file mode 100644 index 0000000..e1e1612 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/create/create.service.js @@ -0,0 +1,97 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.datasets.create.service + * @description Service for the dataset create modal + */ + angular + .module('horizon.dashboard.machine_learning.datasets') + .factory('horizon.dashboard.machine_learning.datasets.create.service', createService); + + createService.$inject = [ + '$location', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.wizard-modal.service', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.datasets.datasetModel', + 'horizon.dashboard.machine_learning.datasets.events', + 'horizon.dashboard.machine_learning.datasets.resourceType', + 'horizon.dashboard.machine_learning.datasets.workflow' + ]; + + function createService( + $location, policy, actionResult, gettext, $qExtensions, wizardModalService, toast, model, events, resourceType, createWorkflow + ) { + var scope; + var message = { + success: gettext('Dataset %s was successfully created.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function perform(selected, newScope) { + scope = newScope; + scope.workflow = createWorkflow; + scope.model = model; + scope.model.init(); + // for creation according to selected item + scope.selected = selected; + return wizardModalService.modal({ + scope: scope, + workflow: createWorkflow, + submit: submit + }).result; + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + //return policy.ifAllowed({ rules: [['dataset', 'add_dataset']] }); + } + + function submit(){ + return model.createDataset().then(success); + } + + function success(response) { + response.data.id = response.data.id; + toast.add('success', interpolate(message.success, [response.data.id])); + var result = actionResult.getActionResult() + .created(resourceType, response.data.id); + if(result.result.failed.length == 0 && result.result.created.length > 0){ + $location.path('/project/machine_learning/datasets'); + }else{ + return result.result; + } + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/create/dataset-model.js b/meteos_ui/static/dashboard/machine_learning/datasets/create/dataset-model.js new file mode 100644 index 0000000..3c06217 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/create/dataset-model.js @@ -0,0 +1,75 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.datasets') + .factory('horizon.dashboard.machine_learning.datasets.datasetModel', datasetModel); + + datasetModel.$inject = [ + 'horizon.app.core.openstack-service-api.meteos' + ]; + + function datasetModel(meteos) { + var model = { + newDatasetSpec: {}, + + // API methods + init: init, + createDataset: createDataset + }; + + function initNewDatasetSpec() { + model.newDatasetSpec = { + method: null, + display_name: null, + display_description: null, + source_dataset_url: null, + experiment_id: null, + params: null, + swift_tenant: null, + swift_username: null, + swift_password: null + }; + } + + function init() { + // Reset the new Dataset spec + initNewDatasetSpec(); + } + + function createDataset() { + var finalSpec = angular.copy(model.newDatasetSpec); + + cleanNullProperties(finalSpec); + + return meteos.createDataset(finalSpec); + } + + function cleanNullProperties(finalSpec) { + // Initially clean fields that don't have any value. + // Not only "null", blank too. + for (var key in finalSpec) { + if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null + || finalSpec[key] === "") { + delete finalSpec[key]; + } + } + } + + return model; + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/create/info/info.help.html b/meteos_ui/static/dashboard/machine_learning/datasets/create/info/info.help.html new file mode 100644 index 0000000..466e501 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/create/info/info.help.html @@ -0,0 +1,8 @@ +
+
Dataset Name
+
An arbitrary human-readable name.
+
Dataset Description
+
An arbitrary human-readable description.
+
Template
+
Name or ID of dataset template.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/create/info/info.html b/meteos_ui/static/dashboard/machine_learning/datasets/create/info/info.html new file mode 100644 index 0000000..6fdc3cf --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/create/info/info.html @@ -0,0 +1,32 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/create/info/template.info.controller.js b/meteos_ui/static/dashboard/machine_learning/datasets/create/info/template.info.controller.js new file mode 100644 index 0000000..de3f4b9 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/create/info/template.info.controller.js @@ -0,0 +1,34 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createDatasetInfoController + * @ngController + * @description + * Controller for the dataset info step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.datasets') + .controller('createDatasetInfoController', createDatasetInfoController); + + createDatasetInfoController.$inject = [ + ]; + + function createDatasetInfoController() { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/create/spec/spec.help.html b/meteos_ui/static/dashboard/machine_learning/datasets/create/spec/spec.help.html new file mode 100644 index 0000000..f20f2f6 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/create/spec/spec.help.html @@ -0,0 +1,14 @@ +
+
Spark Version
+
Version of Apache Spark.
+
Master Nodes
+
Number of Master Node.
+
Master Flavor
+
Flavor ID of Master Node.
+
Worker Nodes
+
Number of Worker Node.
+
Worker Flavor
+
Flavor ID of Worker Node.
+
Floating IP Pool
+
The UUID of Neutron External Network.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/create/spec/spec.html b/meteos_ui/static/dashboard/machine_learning/datasets/create/spec/spec.html new file mode 100644 index 0000000..6826812 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/create/spec/spec.html @@ -0,0 +1,76 @@ +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/create/spec/template.spec.controller.js b/meteos_ui/static/dashboard/machine_learning/datasets/create/spec/template.spec.controller.js new file mode 100644 index 0000000..074b27e --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/create/spec/template.spec.controller.js @@ -0,0 +1,44 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createDatasetSpecController + * @ngController + * @description + * Controller for the dataset spec step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.datasets') + .controller('createDatasetSpecController', createDatasetSpecController); + + createDatasetSpecController.$inject = [ + '$scope', + 'horizon.framework.util.i18n.gettext' + ]; + + function createDatasetSpecController($scope, gettext) { + var ctrl = this; + + ctrl.datasetMethodOptions = [ + { label: gettext('Download'), value: 'download' }, + { label: gettext('Parse'), value: 'parse' } + ]; + + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/datasets.module.js b/meteos_ui/static/dashboard/machine_learning/datasets/datasets.module.js new file mode 100644 index 0000000..d653559 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/datasets.module.js @@ -0,0 +1,156 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.datasets + * @ngModule + * @description + * Provides all the services and widgets require to display the dataset + * panel + */ + angular + .module('horizon.dashboard.machine_learning.datasets', [ + 'ngRoute', + 'horizon.dashboard.machine_learning.datasets.actions' + ]) + .constant('horizon.dashboard.machine_learning.datasets.events', events()) + .constant('horizon.dashboard.machine_learning.datasets.resourceType', 'OS::Meteos::Dataset') + .run(run) + .config(config); + + /** + * @ngdoc constant + * @name horizon.dashboard.machine_learning.datasets.events + * @description A list of events used by Dataset + */ + function events() { + return { + CREATE_SUCCESS: 'horizon.dashboard.machine_learning.datasets.CREATE_SUCCESS', + DELETE_SUCCESS: 'horizon.dashboard.machine_learning.datasets.DELETE_SUCCESS' + }; + } + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.dashboard.machine_learning.datasets.basePath', + 'horizon.dashboard.machine_learning.datasets.resourceType' + ]; + + function run(registry, meteos, basePath, resourceType) { + registry.getResourceType(resourceType) + .setNames(gettext('Machine Learning'), gettext('Datasets')) + // for detail summary view on table row. + .setSummaryTemplateUrl(basePath + 'details/drawer.html') + // for table row items and detail summary view. + .setProperty('name', { + label: gettext('Name') + }) + .setProperty('id', { + label: gettext('ID') + }) + .setProperty('status', { + label: gettext('Status') + }) + .setProperty('source_dataset_url', { + label: gettext('Source Dataset URL') + }) + .setListFunction(listFunction) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + filters: ['noName'], + urlFunction: urlFunction + }) + .append({ + id: 'id', + priority: 3 + }) + .append({ + id: 'status', + priority: 2 + }) + .append({ + id: 'source_dataset_url', + priority: 2 + }) + // for magic-search + registry.getResourceType(resourceType).filterFacets + .append({ + 'label': gettext('Name'), + 'name': 'name', + 'singleton': true + }) + .append({ + 'label': gettext('ID'), + 'name': 'id', + 'singleton': true + }) + .append({ + 'label': gettext('Status'), + 'name': 'status', + 'singleton': true + }) + .append({ + 'label': gettext('Source Dataset URL'), + 'name': 'source_dataset_url', + 'singleton': true + }); + + function listFunction(params) { + return meteos.getDatasets(params).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(addTrackBy)}}; + + function addTrackBy(item) { + item.trackBy = item.id; + return item; + } + } + } + + function urlFunction(item) { + return 'project/ngdetails/OS::Meteos::Dataset/' + item.id; + } + } + + config.$inject = [ + '$provide', + '$windowProvider', + '$routeProvider' + ]; + + /** + * @name config + * @param {Object} $provide + * @param {Object} $windowProvider + * @param {Object} $routeProvider + * @description Routes used by this module. + * @returns {undefined} Returns nothing + */ + function config($provide, $windowProvider, $routeProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/machine_learning/datasets/'; + $provide.constant('horizon.dashboard.machine_learning.datasets.basePath', path); + $routeProvider.when('/project/machine_learning/datasets', { + templateUrl: path + 'panel.html' + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/datasets.module.spec.js b/meteos_ui/static/dashboard/machine_learning/datasets/datasets.module.spec.js new file mode 100644 index 0000000..c2354aa --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/datasets.module.spec.js @@ -0,0 +1,23 @@ +/** + * 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. + */ +(function() { + 'use strict'; + + describe('horizon.dashboard.machine_learning.datasets', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.machine_learning.datasets')).toBeDefined(); + }); + }); + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/datasets.scss b/meteos_ui/static/dashboard/machine_learning/datasets/datasets.scss new file mode 100644 index 0000000..160d1aa --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/datasets.scss @@ -0,0 +1,3 @@ +.logs { + margin-top: 18px; +} \ No newline at end of file diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/delete/delete.service.js b/meteos_ui/static/dashboard/machine_learning/datasets/delete/delete.service.js new file mode 100644 index 0000000..91ec49f --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/delete/delete.service.js @@ -0,0 +1,149 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.datasets') + .factory('horizon.dashboard.machine_learning.datasets.delete.service', deleteService); + + deleteService.$inject = [ + '$location', + '$q', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.datasets.resourceType', + 'horizon.dashboard.machine_learning.datasets.events' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.machine_learning.datasets.delete.service + * @Description + * Brings up the delete datasets confirmation modal dialog. + * On submit, delete selected resources. + * On cancel, do nothing. + */ + function deleteService( + $location, $q, meteos, policy, actionResult, gettext, $qExtensions, deleteModal, toast, resourceType, events + ) { + var scope; + var context = { + labels: null, + deleteEntity: deleteEntity, + successEvent: events.DELETE_SUCCESS + }; + var service = { + initAction: initAction, + allowed: allowed, + perform: perform + }; + var notAllowedMessage = gettext("You are not allowed to delete datasets: %s"); + + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + // delete selected resource objects + function perform(selected, newScope) { + scope = newScope; + var selected = angular.isArray(selected) ? selected : [selected]; + context.labels = labelize(selected.length); + return $qExtensions.allSettled(selected.map(checkPermission)).then(afterCheck); + } + + function labelize(count){ + return { + title: ngettext('Confirm Delete Dataset', + 'Confirm Delete Datasets', count), + /* eslint-disable max-len */ + message: ngettext('You have selected "%s". Please confirm your selection. Deleted dataset is not recoverable.', + 'You have selected "%s". Please confirm your selection. Deleted datasets are not recoverable.', count), + /* eslint-enable max-len */ + submit: ngettext('Delete Dataset', + 'Delete Datasets', count), + success: ngettext('Deleted Dataset: %s.', + 'Deleted Datasets: %s.', count), + error: ngettext('Unable to delete Dataset: %s.', + 'Unable to delete Datasets: %s.', count) + }; + } + + // for batch delete + function checkPermission(selected) { + return {promise: allowed(selected), context: selected}; + } + + // for batch delete + function afterCheck(result){ + var outcome = $q.reject(); // Reject the promise by default + if (result.fail.length > 0) { + toast.add('error', getMessage(notAllowedMessage, result.fail)); + outcome = $q.reject(result.fail); + } + if (result.pass.length > 0) { + outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult); + } + return outcome; + } + + function createResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var result = actionResult.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + result.deleted(resourceType, getEntity(item).id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + result.failed(resourceType, getEntity(item).id); + }); + if(result.result.failed.length == 0 && result.result.deleted.length > 0){ + $location.path('/project/machine_learning/datasets'); + }else{ + return result.result; + } + } + + function getMessage(message, entities) { + return interpolate(message, [entities.map(getName).join(", ")]); + } + + function getName(result) { + return getEntity(result).name; + } + + // for batch delete + function getEntity(result) { + return result.context; + } + + // call delete REST API + function deleteEntity(id){ + return meteos.deleteDataset(id, true); + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/details/drawer.controller.js b/meteos_ui/static/dashboard/machine_learning/datasets/details/drawer.controller.js new file mode 100644 index 0000000..9bf8ef6 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/details/drawer.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name horizon.dashboard.machine_learning.datasets.DrawerController + * @description + * This is the controller for the drawer (summary) view. + * Its primary purpose is to provide the metadata definitions to + * the dataset via the ctrl.metadataDefs member. + */ + angular + .module('horizon.dashboard.machine_learning.datasets') + .controller('horizon.dashboard.machine_learning.datasets.DrawerController', controller); + + controller.$inject = [ + ]; + + function controller() { + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/details/drawer.html b/meteos_ui/static/dashboard/machine_learning/datasets/details/drawer.html new file mode 100644 index 0000000..beddc61 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/details/drawer.html @@ -0,0 +1,14 @@ +
+
+ +
+
ID
+
{$ item.id $}
+
+
+
Description
+
{$ item.description $}
+
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/datasets/panel.html b/meteos_ui/static/dashboard/machine_learning/datasets/panel.html new file mode 100644 index 0000000..28e9536 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/datasets/panel.html @@ -0,0 +1,4 @@ + + + diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/actions.module.js b/meteos_ui/static/dashboard/machine_learning/experiments/actions.module.js new file mode 100644 index 0000000..0dfcea9 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/actions.module.js @@ -0,0 +1,66 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.machine_learning.experiments.actions + * + * @description + * Provides all of the actions for experiments. + */ + angular.module('horizon.dashboard.machine_learning.experiments.actions', ['horizon.framework', 'horizon.dashboard.machine_learning']) + .run(registerExperimentActions); + + registerExperimentActions.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.dashboard.machine_learning.experiments.create.service', + 'horizon.dashboard.machine_learning.experiments.delete.service', + 'horizon.dashboard.machine_learning.experiments.resourceType', + ]; + + function registerExperimentActions( + registry, + gettext, + createExperimentService, + deleteExperimentService, + resourceType) + { + var experimentsResourceType = registry.getResourceType(resourceType); + + experimentsResourceType.globalActions + .append({ + id: 'createExperimentAction', + service: createExperimentService, + template: { + type: 'create', + text: gettext('Create Experiment') + } + }); + + experimentsResourceType.batchActions + .append({ + id: 'batchDeleteExperimentAction', + service: deleteExperimentService, + template: { + type: 'delete-selected', + text: gettext('Delete Experiments') + } + }); + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/create/create-workflow.service.js b/meteos_ui/static/dashboard/machine_learning/experiments/create/create-workflow.service.js new file mode 100644 index 0000000..32f7d96 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/create/create-workflow.service.js @@ -0,0 +1,56 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.experiments') + .factory('horizon.dashboard.machine_learning.experiments.workflow', experimentWorkflow); + + experimentWorkflow.$inject = [ + 'horizon.dashboard.machine_learning.basePath', + 'horizon.app.core.workflow.factory', + 'horizon.framework.util.i18n.gettext' + ]; + + function experimentWorkflow(basePath, dashboardWorkflow, gettext) { + return dashboardWorkflow({ + title: gettext('Create Experiment'), + + steps: [ + { + title: gettext('Info'), + templateUrl: basePath + 'experiments/create/info/info.html', + helpUrl: basePath + 'experiments/create/info/info.help.html', + formName: 'experimentInfoForm' + }, + { + title: gettext('Spec'), + templateUrl: basePath + 'experiments/create/spec/spec.html', + helpUrl: basePath + 'experiments/create/spec/spec.help.html', + formName: 'experimentSpecForm' + }, + ], + + btnText: { + finish: gettext('Create') + }, + + btnIcon: { + finish: 'fa fa-check' + } + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/create/create.service.js b/meteos_ui/static/dashboard/machine_learning/experiments/create/create.service.js new file mode 100644 index 0000000..a505388 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/create/create.service.js @@ -0,0 +1,97 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.experiments.create.service + * @description Service for the experiment create modal + */ + angular + .module('horizon.dashboard.machine_learning.experiments') + .factory('horizon.dashboard.machine_learning.experiments.create.service', createService); + + createService.$inject = [ + '$location', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.wizard-modal.service', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.experiments.experimentModel', + 'horizon.dashboard.machine_learning.experiments.events', + 'horizon.dashboard.machine_learning.experiments.resourceType', + 'horizon.dashboard.machine_learning.experiments.workflow' + ]; + + function createService( + $location, policy, actionResult, gettext, $qExtensions, wizardModalService, toast, model, events, resourceType, createWorkflow + ) { + var scope; + var message = { + success: gettext('Experiment %s was successfully created.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function perform(selected, newScope) { + scope = newScope; + scope.workflow = createWorkflow; + scope.model = model; + scope.model.init(); + // for creation according to selected item + scope.selected = selected; + return wizardModalService.modal({ + scope: scope, + workflow: createWorkflow, + submit: submit + }).result; + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + //return policy.ifAllowed({ rules: [['experiment', 'add_experiment']] }); + } + + function submit(){ + return model.createExperiment().then(success); + } + + function success(response) { + response.data.id = response.data.id; + toast.add('success', interpolate(message.success, [response.data.id])); + var result = actionResult.getActionResult() + .created(resourceType, response.data.id); + if(result.result.failed.length == 0 && result.result.created.length > 0){ + $location.path('/project/machine_learning/experiments'); + }else{ + return result.result; + } + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/create/experiment-model.js b/meteos_ui/static/dashboard/machine_learning/experiments/create/experiment-model.js new file mode 100644 index 0000000..74e5d09 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/create/experiment-model.js @@ -0,0 +1,71 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.experiments') + .factory('horizon.dashboard.machine_learning.experiments.experimentModel', experimentModel); + + experimentModel.$inject = [ + 'horizon.app.core.openstack-service-api.meteos' + ]; + + function experimentModel(meteos) { + var model = { + newExperimentSpec: {}, + + // API methods + init: init, + createExperiment: createExperiment + }; + + function initNewExperimentSpec() { + model.newExperimentSpec = { + display_name: null, + display_description: null, + key_name: null, + neutron_management_network: null, + template_id: null + }; + } + + function init() { + // Reset the new Experiment spec + initNewExperimentSpec(); + } + + function createExperiment() { + var finalSpec = angular.copy(model.newExperimentSpec); + + cleanNullProperties(finalSpec); + + return meteos.createExperiment(finalSpec); + } + + function cleanNullProperties(finalSpec) { + // Initially clean fields that don't have any value. + // Not only "null", blank too. + for (var key in finalSpec) { + if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null + || finalSpec[key] === "") { + delete finalSpec[key]; + } + } + } + + return model; + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/create/info/info.help.html b/meteos_ui/static/dashboard/machine_learning/experiments/create/info/info.help.html new file mode 100644 index 0000000..c8ed009 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/create/info/info.help.html @@ -0,0 +1,8 @@ +
+
Experiment Name
+
An arbitrary human-readable name.
+
Experiment Description
+
An arbitrary human-readable description.
+
Template
+
Name or ID of experiment template.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/create/info/info.html b/meteos_ui/static/dashboard/machine_learning/experiments/create/info/info.html new file mode 100644 index 0000000..834c2b1 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/create/info/info.html @@ -0,0 +1,32 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/create/info/template.info.controller.js b/meteos_ui/static/dashboard/machine_learning/experiments/create/info/template.info.controller.js new file mode 100644 index 0000000..a81767a --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/create/info/template.info.controller.js @@ -0,0 +1,34 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createExperimentInfoController + * @ngController + * @description + * Controller for the experiment info step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.experiments') + .controller('createExperimentInfoController', createExperimentInfoController); + + createExperimentInfoController.$inject = [ + ]; + + function createExperimentInfoController() { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/create/spec/spec.help.html b/meteos_ui/static/dashboard/machine_learning/experiments/create/spec/spec.help.html new file mode 100644 index 0000000..f20f2f6 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/create/spec/spec.help.html @@ -0,0 +1,14 @@ +
+
Spark Version
+
Version of Apache Spark.
+
Master Nodes
+
Number of Master Node.
+
Master Flavor
+
Flavor ID of Master Node.
+
Worker Nodes
+
Number of Worker Node.
+
Worker Flavor
+
Flavor ID of Worker Node.
+
Floating IP Pool
+
The UUID of Neutron External Network.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/create/spec/spec.html b/meteos_ui/static/dashboard/machine_learning/experiments/create/spec/spec.html new file mode 100644 index 0000000..5752284 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/create/spec/spec.html @@ -0,0 +1,28 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/create/spec/template.spec.controller.js b/meteos_ui/static/dashboard/machine_learning/experiments/create/spec/template.spec.controller.js new file mode 100644 index 0000000..92f1915 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/create/spec/template.spec.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createExperimentSpecController + * @ngController + * @description + * Controller for the experiment spec step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.experiments') + .controller('createExperimentSpecController', createExperimentSpecController); + + createExperimentSpecController.$inject = [ + '$scope', + 'horizon.framework.util.i18n.gettext' + ]; + + function createExperimentSpecController($scope, gettext) { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/delete/delete.service.js b/meteos_ui/static/dashboard/machine_learning/experiments/delete/delete.service.js new file mode 100644 index 0000000..aaa9713 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/delete/delete.service.js @@ -0,0 +1,149 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.experiments') + .factory('horizon.dashboard.machine_learning.experiments.delete.service', deleteService); + + deleteService.$inject = [ + '$location', + '$q', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.experiments.resourceType', + 'horizon.dashboard.machine_learning.experiments.events' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.machine_learning.experiments.delete.service + * @Description + * Brings up the delete experiments confirmation modal dialog. + * On submit, delete selected resources. + * On cancel, do nothing. + */ + function deleteService( + $location, $q, meteos, policy, actionResult, gettext, $qExtensions, deleteModal, toast, resourceType, events + ) { + var scope; + var context = { + labels: null, + deleteEntity: deleteEntity, + successEvent: events.DELETE_SUCCESS + }; + var service = { + initAction: initAction, + allowed: allowed, + perform: perform + }; + var notAllowedMessage = gettext("You are not allowed to delete experiments: %s"); + + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + // delete selected resource objects + function perform(selected, newScope) { + scope = newScope; + var selected = angular.isArray(selected) ? selected : [selected]; + context.labels = labelize(selected.length); + return $qExtensions.allSettled(selected.map(checkPermission)).then(afterCheck); + } + + function labelize(count){ + return { + title: ngettext('Confirm Delete Experiment', + 'Confirm Delete Experiments', count), + /* eslint-disable max-len */ + message: ngettext('You have selected "%s". Please confirm your selection. Deleted experiment is not recoverable.', + 'You have selected "%s". Please confirm your selection. Deleted experiments are not recoverable.', count), + /* eslint-enable max-len */ + submit: ngettext('Delete Experiment', + 'Delete Experiments', count), + success: ngettext('Deleted Experiment: %s.', + 'Deleted Experiments: %s.', count), + error: ngettext('Unable to delete Experiment: %s.', + 'Unable to delete Experiments: %s.', count) + }; + } + + // for batch delete + function checkPermission(selected) { + return {promise: allowed(selected), context: selected}; + } + + // for batch delete + function afterCheck(result){ + var outcome = $q.reject(); // Reject the promise by default + if (result.fail.length > 0) { + toast.add('error', getMessage(notAllowedMessage, result.fail)); + outcome = $q.reject(result.fail); + } + if (result.pass.length > 0) { + outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult); + } + return outcome; + } + + function createResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var result = actionResult.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + result.deleted(resourceType, getEntity(item).id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + result.failed(resourceType, getEntity(item).id); + }); + if(result.result.failed.length == 0 && result.result.deleted.length > 0){ + $location.path('/project/machine_learning/experiments'); + }else{ + return result.result; + } + } + + function getMessage(message, entities) { + return interpolate(message, [entities.map(getName).join(", ")]); + } + + function getName(result) { + return getEntity(result).name; + } + + // for batch delete + function getEntity(result) { + return result.context; + } + + // call delete REST API + function deleteEntity(id){ + return meteos.deleteExperiment(id, true); + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/details/drawer.controller.js b/meteos_ui/static/dashboard/machine_learning/experiments/details/drawer.controller.js new file mode 100644 index 0000000..05f77aa --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/details/drawer.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name horizon.dashboard.machine_learning.experiments.DrawerController + * @description + * This is the controller for the drawer (summary) view. + * Its primary purpose is to provide the metadata definitions to + * the experiment via the ctrl.metadataDefs member. + */ + angular + .module('horizon.dashboard.machine_learning.experiments') + .controller('horizon.dashboard.machine_learning.experiments.DrawerController', controller); + + controller.$inject = [ + ]; + + function controller() { + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/details/drawer.html b/meteos_ui/static/dashboard/machine_learning/experiments/details/drawer.html new file mode 100644 index 0000000..ed623c6 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/details/drawer.html @@ -0,0 +1,14 @@ +
+
+ +
+
ID
+
{$ item.id $}
+
+
+
Description
+
{$ item.description $}
+
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/experiments.module.js b/meteos_ui/static/dashboard/machine_learning/experiments/experiments.module.js new file mode 100644 index 0000000..2c96614 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/experiments.module.js @@ -0,0 +1,168 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.experiments + * @ngModule + * @description + * Provides all the services and widgets require to display the experiment + * panel + */ + angular + .module('horizon.dashboard.machine_learning.experiments', [ + 'ngRoute', + 'horizon.dashboard.machine_learning.experiments.actions' + ]) + .constant('horizon.dashboard.machine_learning.experiments.events', events()) + .constant('horizon.dashboard.machine_learning.experiments.resourceType', 'OS::Meteos::Experiment') + .run(run) + .config(config); + + /** + * @ngdoc constant + * @name horizon.dashboard.machine_learning.experiments.events + * @description A list of events used by Experiment + */ + function events() { + return { + CREATE_SUCCESS: 'horizon.dashboard.machine_learning.experiments.CREATE_SUCCESS', + DELETE_SUCCESS: 'horizon.dashboard.machine_learning.experiments.DELETE_SUCCESS' + }; + } + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.dashboard.machine_learning.experiments.basePath', + 'horizon.dashboard.machine_learning.experiments.resourceType' + ]; + + function run(registry, meteos, basePath, resourceType) { + registry.getResourceType(resourceType) + .setNames(gettext('Machine Learning'), gettext('Experiments')) + // for detail summary view on table row. + .setSummaryTemplateUrl(basePath + 'details/drawer.html') + // for table row items and detail summary view. + .setProperty('name', { + label: gettext('Name') + }) + .setProperty('id', { + label: gettext('ID') + }) + .setProperty('status', { + label: gettext('Status') + }) + .setProperty('key_name', { + label: gettext('Key Name') + }) + .setProperty('management_network', { + label: gettext('Management Network') + }) + .setListFunction(listFunction) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + filters: ['noName'], + urlFunction: urlFunction + }) + .append({ + id: 'id', + priority: 3 + }) + .append({ + id: 'status', + priority: 2 + }) + .append({ + id: 'key_name', + priority: 2 + }) + .append({ + id: 'management_network', + priority: 2 + }) + // for magic-search + registry.getResourceType(resourceType).filterFacets + .append({ + 'label': gettext('Name'), + 'name': 'name', + 'singleton': true + }) + .append({ + 'label': gettext('ID'), + 'name': 'id', + 'singleton': true + }) + .append({ + 'label': gettext('Status'), + 'name': 'status', + 'singleton': true + }) + .append({ + 'label': gettext('Key Name'), + 'name': 'key_name', + 'singleton': true + }) + .append({ + 'label': gettext('Management Network'), + 'name': 'management_network', + 'singleton': true + }); + + function listFunction(params) { + return meteos.getExperiments(params).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(addTrackBy)}}; + + function addTrackBy(item) { + item.trackBy = item.id; + return item; + } + } + } + + function urlFunction(item) { + return 'project/ngdetails/OS::Meteos::Experiment/' + item.id; + } + } + + config.$inject = [ + '$provide', + '$windowProvider', + '$routeProvider' + ]; + + /** + * @name config + * @param {Object} $provide + * @param {Object} $windowProvider + * @param {Object} $routeProvider + * @description Routes used by this module. + * @returns {undefined} Returns nothing + */ + function config($provide, $windowProvider, $routeProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/machine_learning/experiments/'; + $provide.constant('horizon.dashboard.machine_learning.experiments.basePath', path); + $routeProvider.when('/project/machine_learning/experiments', { + templateUrl: path + 'panel.html' + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/experiments.module.spec.js b/meteos_ui/static/dashboard/machine_learning/experiments/experiments.module.spec.js new file mode 100644 index 0000000..842e78e --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/experiments.module.spec.js @@ -0,0 +1,23 @@ +/** + * 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. + */ +(function() { + 'use strict'; + + describe('horizon.dashboard.machine_learning.experiments', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.machine_learning.experiments')).toBeDefined(); + }); + }); + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/experiments.scss b/meteos_ui/static/dashboard/machine_learning/experiments/experiments.scss new file mode 100644 index 0000000..160d1aa --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/experiments.scss @@ -0,0 +1,3 @@ +.logs { + margin-top: 18px; +} \ No newline at end of file diff --git a/meteos_ui/static/dashboard/machine_learning/experiments/panel.html b/meteos_ui/static/dashboard/machine_learning/experiments/panel.html new file mode 100644 index 0000000..2dc607c --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/experiments/panel.html @@ -0,0 +1,4 @@ + + + diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/actions.module.js b/meteos_ui/static/dashboard/machine_learning/learnings/actions.module.js new file mode 100644 index 0000000..c1637cf --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/actions.module.js @@ -0,0 +1,66 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.machine_learning.learnings.actions + * + * @description + * Provides all of the actions for learnings. + */ + angular.module('horizon.dashboard.machine_learning.learnings.actions', ['horizon.framework', 'horizon.dashboard.machine_learning']) + .run(registerLearningActions); + + registerLearningActions.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.dashboard.machine_learning.learnings.create.service', + 'horizon.dashboard.machine_learning.learnings.delete.service', + 'horizon.dashboard.machine_learning.learnings.resourceType', + ]; + + function registerLearningActions( + registry, + gettext, + createLearningService, + deleteLearningService, + resourceType) + { + var learningsResourceType = registry.getResourceType(resourceType); + + learningsResourceType.globalActions + .append({ + id: 'createLearningAction', + service: createLearningService, + template: { + type: 'create', + text: gettext('Create Learning') + } + }); + + learningsResourceType.batchActions + .append({ + id: 'batchDeleteLearningAction', + service: deleteLearningService, + template: { + type: 'delete-selected', + text: gettext('Delete Learnings') + } + }); + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/create/create-workflow.service.js b/meteos_ui/static/dashboard/machine_learning/learnings/create/create-workflow.service.js new file mode 100644 index 0000000..898ccf7 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/create/create-workflow.service.js @@ -0,0 +1,56 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.learnings') + .factory('horizon.dashboard.machine_learning.learnings.workflow', learningWorkflow); + + learningWorkflow.$inject = [ + 'horizon.dashboard.machine_learning.basePath', + 'horizon.app.core.workflow.factory', + 'horizon.framework.util.i18n.gettext' + ]; + + function learningWorkflow(basePath, dashboardWorkflow, gettext) { + return dashboardWorkflow({ + title: gettext('Create Learning'), + + steps: [ + { + title: gettext('Info'), + templateUrl: basePath + 'learnings/create/info/info.html', + helpUrl: basePath + 'learnings/create/info/info.help.html', + formName: 'learningInfoForm' + }, + { + title: gettext('Spec'), + templateUrl: basePath + 'learnings/create/spec/spec.html', + helpUrl: basePath + 'learnings/create/spec/spec.help.html', + formName: 'learningSpecForm' + }, + ], + + btnText: { + finish: gettext('Create') + }, + + btnIcon: { + finish: 'fa fa-check' + } + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/create/create.service.js b/meteos_ui/static/dashboard/machine_learning/learnings/create/create.service.js new file mode 100644 index 0000000..96819ca --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/create/create.service.js @@ -0,0 +1,97 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.learnings.create.service + * @description Service for the learning create modal + */ + angular + .module('horizon.dashboard.machine_learning.learnings') + .factory('horizon.dashboard.machine_learning.learnings.create.service', createService); + + createService.$inject = [ + '$location', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.wizard-modal.service', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.learnings.learningModel', + 'horizon.dashboard.machine_learning.learnings.events', + 'horizon.dashboard.machine_learning.learnings.resourceType', + 'horizon.dashboard.machine_learning.learnings.workflow' + ]; + + function createService( + $location, policy, actionResult, gettext, $qExtensions, wizardModalService, toast, model, events, resourceType, createWorkflow + ) { + var scope; + var message = { + success: gettext('Learning %s was successfully created.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function perform(selected, newScope) { + scope = newScope; + scope.workflow = createWorkflow; + scope.model = model; + scope.model.init(); + // for creation according to selected item + scope.selected = selected; + return wizardModalService.modal({ + scope: scope, + workflow: createWorkflow, + submit: submit + }).result; + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + //return policy.ifAllowed({ rules: [['learning', 'add_learning']] }); + } + + function submit(){ + return model.createLearning().then(success); + } + + function success(response) { + response.data.id = response.data.id; + toast.add('success', interpolate(message.success, [response.data.id])); + var result = actionResult.getActionResult() + .created(resourceType, response.data.id); + if(result.result.failed.length == 0 && result.result.created.length > 0){ + $location.path('/project/machine_learning/learnings'); + }else{ + return result.result; + } + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/create/info/info.help.html b/meteos_ui/static/dashboard/machine_learning/learnings/create/info/info.help.html new file mode 100644 index 0000000..86a7188 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/create/info/info.help.html @@ -0,0 +1,8 @@ +
+
Learning Name
+
An arbitrary human-readable name.
+
Learning Description
+
An arbitrary human-readable description.
+
Template
+
Name or ID of learning template.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/create/info/info.html b/meteos_ui/static/dashboard/machine_learning/learnings/create/info/info.html new file mode 100644 index 0000000..59146f0 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/create/info/info.html @@ -0,0 +1,32 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/create/info/template.info.controller.js b/meteos_ui/static/dashboard/machine_learning/learnings/create/info/template.info.controller.js new file mode 100644 index 0000000..491d702 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/create/info/template.info.controller.js @@ -0,0 +1,34 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createLearningInfoController + * @ngController + * @description + * Controller for the learning info step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.learnings') + .controller('createLearningInfoController', createLearningInfoController); + + createLearningInfoController.$inject = [ + ]; + + function createLearningInfoController() { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/create/learning-model.js b/meteos_ui/static/dashboard/machine_learning/learnings/create/learning-model.js new file mode 100644 index 0000000..7543bca --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/create/learning-model.js @@ -0,0 +1,71 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.learnings') + .factory('horizon.dashboard.machine_learning.learnings.learningModel', learningModel); + + learningModel.$inject = [ + 'horizon.app.core.openstack-service-api.meteos' + ]; + + function learningModel(meteos) { + var model = { + newLearningSpec: {}, + + // API methods + init: init, + createLearning: createLearning + }; + + function initNewLearningSpec() { + model.newLearningSpec = { + display_name: null, + display_description: null, + model_id: null, + method: null, + args: null + }; + } + + function init() { + // Reset the new Learning spec + initNewLearningSpec(); + } + + function createLearning() { + var finalSpec = angular.copy(model.newLearningSpec); + + cleanNullProperties(finalSpec); + + return meteos.createLearning(finalSpec); + } + + function cleanNullProperties(finalSpec) { + // Initially clean fields that don't have any value. + // Not only "null", blank too. + for (var key in finalSpec) { + if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null + || finalSpec[key] === "") { + delete finalSpec[key]; + } + } + } + + return model; + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/create/spec/spec.help.html b/meteos_ui/static/dashboard/machine_learning/learnings/create/spec/spec.help.html new file mode 100644 index 0000000..f20f2f6 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/create/spec/spec.help.html @@ -0,0 +1,14 @@ +
+
Spark Version
+
Version of Apache Spark.
+
Master Nodes
+
Number of Master Node.
+
Master Flavor
+
Flavor ID of Master Node.
+
Worker Nodes
+
Number of Worker Node.
+
Worker Flavor
+
Flavor ID of Worker Node.
+
Floating IP Pool
+
The UUID of Neutron External Network.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/create/spec/spec.html b/meteos_ui/static/dashboard/machine_learning/learnings/create/spec/spec.html new file mode 100644 index 0000000..6b2a95c --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/create/spec/spec.html @@ -0,0 +1,28 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/create/spec/template.spec.controller.js b/meteos_ui/static/dashboard/machine_learning/learnings/create/spec/template.spec.controller.js new file mode 100644 index 0000000..bce75dd --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/create/spec/template.spec.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createLearningSpecController + * @ngController + * @description + * Controller for the learning spec step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.learnings') + .controller('createLearningSpecController', createLearningSpecController); + + createLearningSpecController.$inject = [ + '$scope', + 'horizon.framework.util.i18n.gettext' + ]; + + function createLearningSpecController($scope, gettext) { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/delete/delete.service.js b/meteos_ui/static/dashboard/machine_learning/learnings/delete/delete.service.js new file mode 100644 index 0000000..08ed432 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/delete/delete.service.js @@ -0,0 +1,149 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.learnings') + .factory('horizon.dashboard.machine_learning.learnings.delete.service', deleteService); + + deleteService.$inject = [ + '$location', + '$q', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.learnings.resourceType', + 'horizon.dashboard.machine_learning.learnings.events' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.machine_learning.learnings.delete.service + * @Description + * Brings up the delete learnings confirmation modal dialog. + * On submit, delete selected resources. + * On cancel, do nothing. + */ + function deleteService( + $location, $q, meteos, policy, actionResult, gettext, $qExtensions, deleteModal, toast, resourceType, events + ) { + var scope; + var context = { + labels: null, + deleteEntity: deleteEntity, + successEvent: events.DELETE_SUCCESS + }; + var service = { + initAction: initAction, + allowed: allowed, + perform: perform + }; + var notAllowedMessage = gettext("You are not allowed to delete learnings: %s"); + + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + // delete selected resource objects + function perform(selected, newScope) { + scope = newScope; + var selected = angular.isArray(selected) ? selected : [selected]; + context.labels = labelize(selected.length); + return $qExtensions.allSettled(selected.map(checkPermission)).then(afterCheck); + } + + function labelize(count){ + return { + title: ngettext('Confirm Delete Learning', + 'Confirm Delete Learnings', count), + /* eslint-disable max-len */ + message: ngettext('You have selected "%s". Please confirm your selection. Deleted learning is not recoverable.', + 'You have selected "%s". Please confirm your selection. Deleted learnings are not recoverable.', count), + /* eslint-enable max-len */ + submit: ngettext('Delete Learning', + 'Delete Learnings', count), + success: ngettext('Deleted Learning: %s.', + 'Deleted Learnings: %s.', count), + error: ngettext('Unable to delete Learning: %s.', + 'Unable to delete Learnings: %s.', count) + }; + } + + // for batch delete + function checkPermission(selected) { + return {promise: allowed(selected), context: selected}; + } + + // for batch delete + function afterCheck(result){ + var outcome = $q.reject(); // Reject the promise by default + if (result.fail.length > 0) { + toast.add('error', getMessage(notAllowedMessage, result.fail)); + outcome = $q.reject(result.fail); + } + if (result.pass.length > 0) { + outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult); + } + return outcome; + } + + function createResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var result = actionResult.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + result.deleted(resourceType, getEntity(item).id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + result.failed(resourceType, getEntity(item).id); + }); + if(result.result.failed.length == 0 && result.result.deleted.length > 0){ + $location.path('/project/machine_learning/learnings'); + }else{ + return result.result; + } + } + + function getMessage(message, entities) { + return interpolate(message, [entities.map(getName).join(", ")]); + } + + function getName(result) { + return getEntity(result).name; + } + + // for batch delete + function getEntity(result) { + return result.context; + } + + // call delete REST API + function deleteEntity(id){ + return meteos.deleteLearning(id, true); + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/details/drawer.controller.js b/meteos_ui/static/dashboard/machine_learning/learnings/details/drawer.controller.js new file mode 100644 index 0000000..0442533 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/details/drawer.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name horizon.dashboard.machine_learning.learnings.DrawerController + * @description + * This is the controller for the drawer (summary) view. + * Its primary purpose is to provide the metadata definitions to + * the learning via the ctrl.metadataDefs member. + */ + angular + .module('horizon.dashboard.machine_learning.learnings') + .controller('horizon.dashboard.machine_learning.learnings.DrawerController', controller); + + controller.$inject = [ + ]; + + function controller() { + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/details/drawer.html b/meteos_ui/static/dashboard/machine_learning/learnings/details/drawer.html new file mode 100644 index 0000000..85f0fc5 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/details/drawer.html @@ -0,0 +1,14 @@ +
+
+ +
+
ID
+
{$ item.id $}
+
+
+
Description
+
{$ item.description $}
+
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/learnings.module.js b/meteos_ui/static/dashboard/machine_learning/learnings/learnings.module.js new file mode 100644 index 0000000..ea1aef1 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/learnings.module.js @@ -0,0 +1,180 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.learnings + * @ngModule + * @description + * Provides all the services and widgets require to display the learning + * panel + */ + angular + .module('horizon.dashboard.machine_learning.learnings', [ + 'ngRoute', + 'horizon.dashboard.machine_learning.learnings.actions' + ]) + .constant('horizon.dashboard.machine_learning.learnings.events', events()) + .constant('horizon.dashboard.machine_learning.learnings.resourceType', 'OS::Meteos::Learning') + .run(run) + .config(config); + + /** + * @ngdoc constant + * @name horizon.dashboard.machine_learning.learnings.events + * @description A list of events used by Learning + */ + function events() { + return { + CREATE_SUCCESS: 'horizon.dashboard.machine_learning.learnings.CREATE_SUCCESS', + DELETE_SUCCESS: 'horizon.dashboard.machine_learning.learnings.DELETE_SUCCESS' + }; + } + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.dashboard.machine_learning.learnings.basePath', + 'horizon.dashboard.machine_learning.learnings.resourceType' + ]; + + function run(registry, meteos, basePath, resourceType) { + registry.getResourceType(resourceType) + .setNames(gettext('Machine Learning'), gettext('Learnings')) + // for detail summary view on table row. + .setSummaryTemplateUrl(basePath + 'details/drawer.html') + // for table row items and detail summary view. + .setProperty('name', { + label: gettext('Name') + }) + .setProperty('id', { + label: gettext('ID') + }) + .setProperty('status', { + label: gettext('Status') + }) + .setProperty('type', { + label: gettext('Model Type') + }) + .setProperty('args', { + label: gettext('Input Data') + }) + .setProperty('stdout', { + label: gettext('Result') + }) + .setListFunction(listFunction) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + filters: ['noName'], + urlFunction: urlFunction + }) + .append({ + id: 'id', + priority: 3 + }) + .append({ + id: 'status', + priority: 2 + }) + .append({ + id: 'type', + priority: 2 + }) + .append({ + id: 'args', + priority: 2 + }) + .append({ + id: 'stdout', + priority: 2 + }) + // for magic-search + registry.getResourceType(resourceType).filterFacets + .append({ + 'label': gettext('Name'), + 'name': 'name', + 'singleton': true + }) + .append({ + 'label': gettext('ID'), + 'name': 'id', + 'singleton': true + }) + .append({ + 'label': gettext('Status'), + 'name': 'status', + 'singleton': true + }) + .append({ + 'label': gettext('Model Type'), + 'name': 'type', + 'singleton': true + }) + .append({ + 'label': gettext('Input Data'), + 'name': 'args', + 'singleton': true + }) + .append({ + 'label': gettext('Result'), + 'name': 'stdout', + 'singleton': true + }); + + function listFunction(params) { + return meteos.getLearnings(params).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(addTrackBy)}}; + + function addTrackBy(item) { + item.trackBy = item.id; + return item; + } + } + } + + function urlFunction(item) { + return 'project/ngdetails/OS::Meteos::Learning/' + item.id; + } + } + + config.$inject = [ + '$provide', + '$windowProvider', + '$routeProvider' + ]; + + /** + * @name config + * @param {Object} $provide + * @param {Object} $windowProvider + * @param {Object} $routeProvider + * @description Routes used by this module. + * @returns {undefined} Returns nothing + */ + function config($provide, $windowProvider, $routeProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/machine_learning/learnings/'; + $provide.constant('horizon.dashboard.machine_learning.learnings.basePath', path); + $routeProvider.when('/project/machine_learning/learnings', { + templateUrl: path + 'panel.html' + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/learnings.module.spec.js b/meteos_ui/static/dashboard/machine_learning/learnings/learnings.module.spec.js new file mode 100644 index 0000000..4f37021 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/learnings.module.spec.js @@ -0,0 +1,23 @@ +/** + * 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. + */ +(function() { + 'use strict'; + + describe('horizon.dashboard.machine_learning.learnings', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.machine_learning.learnings')).toBeDefined(); + }); + }); + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/learnings.scss b/meteos_ui/static/dashboard/machine_learning/learnings/learnings.scss new file mode 100644 index 0000000..160d1aa --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/learnings.scss @@ -0,0 +1,3 @@ +.logs { + margin-top: 18px; +} \ No newline at end of file diff --git a/meteos_ui/static/dashboard/machine_learning/learnings/panel.html b/meteos_ui/static/dashboard/machine_learning/learnings/panel.html new file mode 100644 index 0000000..da2027a --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/learnings/panel.html @@ -0,0 +1,4 @@ + + + diff --git a/meteos_ui/static/dashboard/machine_learning/machine_learning.module.js b/meteos_ui/static/dashboard/machine_learning/machine_learning.module.js new file mode 100644 index 0000000..477f2e0 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/machine_learning.module.js @@ -0,0 +1,41 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning + * @description + * Dashboard module to host various machine_learning panels. + */ + angular + .module('horizon.dashboard.machine_learning', [ + 'horizon.dashboard.machine_learning.templates', + 'horizon.dashboard.machine_learning.experiments', + 'horizon.dashboard.machine_learning.datasets', + 'horizon.dashboard.machine_learning.models', + 'horizon.dashboard.machine_learning.model_evaluations', + 'horizon.dashboard.machine_learning.learnings' + ]) + .config(config) + + config.$inject = ['$provide', '$windowProvider']; + + function config($provide, $windowProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/machine_learning/'; + $provide.constant('horizon.dashboard.machine_learning.basePath', path); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/machine_learning.module.spec.js b/meteos_ui/static/dashboard/machine_learning/machine_learning.module.spec.js new file mode 100644 index 0000000..b33d606 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/machine_learning.module.spec.js @@ -0,0 +1,23 @@ +/** + * 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. + */ +(function() { + 'use strict'; + + describe('horizon.dashboard.machine_learning', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.machine_learning')).toBeDefined(); + }); + }); + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/machine_learning.scss b/meteos_ui/static/dashboard/machine_learning/machine_learning.scss new file mode 100644 index 0000000..5adc787 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/machine_learning.scss @@ -0,0 +1,8 @@ +@import "templates/templates"; + +.batch-action { + float: right; + action-list { + padding-left: 0.3em; + } +} diff --git a/meteos_ui/static/dashboard/machine_learning/meteos.service.js b/meteos_ui/static/dashboard/machine_learning/meteos.service.js new file mode 100644 index 0000000..4e680a7 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/meteos.service.js @@ -0,0 +1,340 @@ +/** + * 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. + */ +(function () { + 'use strict'; + + angular + .module('horizon.app.core.openstack-service-api') + .factory('horizon.app.core.openstack-service-api.meteos', MeteosAPI); + + MeteosAPI.$inject = [ + 'horizon.framework.util.http.service', + 'horizon.framework.widgets.toast.service', + 'horizon.framework.util.i18n.gettext' + ]; + + function MeteosAPI(apiService, toastService, gettext) { + var service = { + createTemplate: createTemplate, + getTemplate: getTemplate, + getTemplates: getTemplates, + deleteTemplate: deleteTemplate, + deleteTemplates: deleteTemplates, + createExperiment: createExperiment, + getExperiment: getExperiment, + getExperiments: getExperiments, + deleteExperiment: deleteExperiment, + deleteExperiments: deleteExperiments, + createDataset: createDataset, + getDataset: getDataset, + getDatasets: getDatasets, + deleteDataset: deleteDataset, + deleteDatasets: deleteDatasets, + createModel: createModel, + getModel: getModel, + getModels: getModels, + deleteModel: deleteModel, + deleteModels: deleteModels, + createModelEvaluation: createModelEvaluation, + getModelEvaluation: getModelEvaluation, + getModelEvaluations: getModelEvaluations, + deleteModelEvaluation: deleteModelEvaluation, + deleteModelEvaluations: deleteModelEvaluations, + createLearning: createLearning, + getLearning: getLearning, + getLearnings: getLearnings, + deleteLearning: deleteLearning, + deleteLearnings: deleteLearnings, + }; + + return service; + + /////////////// + // Templates // + /////////////// + + function createTemplate(params) { + return apiService.post('/api/meteos/templates/', params) + .error(function() { + toastService.add('error', gettext('Unable to create Template')); + }); + } + + function getTemplate(id) { + return apiService.get('/api/meteos/templates/' + id) + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Template.')); + }); + } + + function getTemplates() { + return apiService.get('/api/meteos/templates/') + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Templates.')); + }); + } + + function deleteTemplate(id, suppressError) { + var promise = apiService.delete('/api/meteos/templates/', [id]); + return suppressError ? promise : promise.error(function() { + var msg = gettext('Unable to delete the Template with id: %(id)s'); + toastService.add('error', interpolate(msg, { id: id }, true)); + }); + } + + function deleteTemplates(ids) { + return apiService.delete('/api/meteos/templates/', ids) + .error(function() { + toastService.add('error', gettext('Unable to delete the Templates.')); + }); + } + + ///////////////// + // Experiments // + ///////////////// + + function createExperiment(params) { + return apiService.post('/api/meteos/experiments/', params) + .error(function() { + toastService.add('error', gettext('Unable to create Experiment')); + }); + } + + function getExperiment(id) { + return apiService.get('/api/meteos/experiments/' + id) + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Experiment.')); + }); + } + + function getExperiments() { + return apiService.get('/api/meteos/experiments/') + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Experiments.')); + }); + } + + function deleteExperiment(id, suppressError) { + var promise = apiService.delete('/api/meteos/experiments/', [id]); + return suppressError ? promise : promise.error(function() { + var msg = gettext('Unable to delete the Experiment with id: %(id)s'); + toastService.add('error', interpolate(msg, { id: id }, true)); + }); + } + + function deleteExperiments(ids) { + return apiService.delete('/api/meteos/experiments/', ids) + .error(function() { + toastService.add('error', gettext('Unable to delete the Experiments.')); + }); + } + + ////////////// + // Datasets // + ////////////// + + function createDataset(params) { + return apiService.post('/api/meteos/datasets/', params) + .error(function() { + toastService.add('error', gettext('Unable to create Dataset')); + }); + } + + function getDataset(id) { + return apiService.get('/api/meteos/datasets/' + id) + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Dataset.')); + }); + } + + function getDatasets() { + return apiService.get('/api/meteos/datasets/') + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Datasets.')); + }); + } + + function deleteDataset(id, suppressError) { + var promise = apiService.delete('/api/meteos/datasets/', [id]); + return suppressError ? promise : promise.error(function() { + var msg = gettext('Unable to delete the Dataset with id: %(id)s'); + toastService.add('error', interpolate(msg, { id: id }, true)); + }); + } + + function deleteDatasets(ids) { + return apiService.delete('/api/meteos/datasets/', ids) + .error(function() { + toastService.add('error', gettext('Unable to delete the Datasets.')); + }); + } + + //////////// + // Models // + //////////// + + function createModel(params) { + return apiService.post('/api/meteos/models/', params) + .error(function() { + toastService.add('error', gettext('Unable to create Model')); + }); + } + + function getModel(id) { + return apiService.get('/api/meteos/models/' + id) + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Model.')); + }); + } + + function getModels() { + return apiService.get('/api/meteos/models/') + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Models.')); + }); + } + + function deleteModel(id, suppressError) { + var promise = apiService.delete('/api/meteos/models/', [id]); + return suppressError ? promise : promise.error(function() { + var msg = gettext('Unable to delete the Model with id: %(id)s'); + toastService.add('error', interpolate(msg, { id: id }, true)); + }); + } + + function deleteModels(ids) { + return apiService.delete('/api/meteos/models/', ids) + .error(function() { + toastService.add('error', gettext('Unable to delete the Models.')); + }); + } + + ////////////////////// + // ModelEvaluations // + ////////////////////// + + function createModelEvaluation(params) { + return apiService.post('/api/meteos/model_evaluations/', params) + .error(function() { + toastService.add('error', gettext('Unable to create ModelEvaluation')); + }); + } + + function getModelEvaluation(id) { + return apiService.get('/api/meteos/model_evaluations/' + id) + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the ModelEvaluation.')); + }); + } + + function getModelEvaluations() { + return apiService.get('/api/meteos/model_evaluations/') + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the ModelEvaluations.')); + }); + } + + function deleteModelEvaluation(id, suppressError) { + var promise = apiService.delete('/api/meteos/model_evaluations/', [id]); + return suppressError ? promise : promise.error(function() { + var msg = gettext('Unable to delete the ModelEvaluation with id: %(id)s'); + toastService.add('error', interpolate(msg, { id: id }, true)); + }); + } + + function deleteModelEvaluations(ids) { + return apiService.delete('/api/meteos/model_evaluations/', ids) + .error(function() { + toastService.add('error', gettext('Unable to delete the ModelEvaluations.')); + }); + } + + /////////////// + // Learnings // + /////////////// + + function createLearning(params) { + return apiService.post('/api/meteos/learnings/', params) + .error(function() { + toastService.add('error', gettext('Unable to create Learning')); + }); + } + + function getLearning(id) { + return apiService.get('/api/meteos/learnings/' + id) + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Learning.')); + }); + } + + function getLearnings() { + return apiService.get('/api/meteos/learnings/') + .success(function(data, status, headers, config) { + return data; + }) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the Learnings.')); + }); + } + + function deleteLearning(id, suppressError) { + var promise = apiService.delete('/api/meteos/learnings/', [id]); + return suppressError ? promise : promise.error(function() { + var msg = gettext('Unable to delete the Learning with id: %(id)s'); + toastService.add('error', interpolate(msg, { id: id }, true)); + }); + } + + function deleteLearnings(ids) { + return apiService.delete('/api/meteos/learnings/', ids) + .error(function() { + toastService.add('error', gettext('Unable to delete the Learnings.')); + }); + } + + } +}()); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/actions.module.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/actions.module.js new file mode 100644 index 0000000..1e0fe40 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/actions.module.js @@ -0,0 +1,66 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.machine_learning.model_evaluations.actions + * + * @description + * Provides all of the actions for model_evaluations. + */ + angular.module('horizon.dashboard.machine_learning.model_evaluations.actions', ['horizon.framework', 'horizon.dashboard.machine_learning']) + .run(registerModelEvaluationActions); + + registerModelEvaluationActions.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.dashboard.machine_learning.model_evaluations.create.service', + 'horizon.dashboard.machine_learning.model_evaluations.delete.service', + 'horizon.dashboard.machine_learning.model_evaluations.resourceType', + ]; + + function registerModelEvaluationActions( + registry, + gettext, + createModelEvaluationService, + deleteModelEvaluationService, + resourceType) + { + var model_evaluationsResourceType = registry.getResourceType(resourceType); + + model_evaluationsResourceType.globalActions + .append({ + id: 'createModelEvaluationAction', + service: createModelEvaluationService, + template: { + type: 'create', + text: gettext('Create ModelEvaluation') + } + }); + + model_evaluationsResourceType.batchActions + .append({ + id: 'batchDeleteModelEvaluationAction', + service: deleteModelEvaluationService, + template: { + type: 'delete-selected', + text: gettext('Delete ModelEvaluations') + } + }); + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/create-workflow.service.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/create-workflow.service.js new file mode 100644 index 0000000..05a6969 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/create-workflow.service.js @@ -0,0 +1,56 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.model_evaluations') + .factory('horizon.dashboard.machine_learning.model_evaluations.workflow', model_evaluationWorkflow); + + model_evaluationWorkflow.$inject = [ + 'horizon.dashboard.machine_learning.basePath', + 'horizon.app.core.workflow.factory', + 'horizon.framework.util.i18n.gettext' + ]; + + function model_evaluationWorkflow(basePath, dashboardWorkflow, gettext) { + return dashboardWorkflow({ + title: gettext('Create ModelEvaluation'), + + steps: [ + { + title: gettext('Info'), + templateUrl: basePath + 'model_evaluations/create/info/info.html', + helpUrl: basePath + 'model_evaluations/create/info/info.help.html', + formName: 'model_evaluationInfoForm' + }, + { + title: gettext('Spec'), + templateUrl: basePath + 'model_evaluations/create/spec/spec.html', + helpUrl: basePath + 'model_evaluations/create/spec/spec.help.html', + formName: 'model_evaluationSpecForm' + }, + ], + + btnText: { + finish: gettext('Create') + }, + + btnIcon: { + finish: 'fa fa-check' + } + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/create.service.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/create.service.js new file mode 100644 index 0000000..1ee2d8d --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/create.service.js @@ -0,0 +1,97 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.model_evaluations.create.service + * @description Service for the model_evaluation create modal + */ + angular + .module('horizon.dashboard.machine_learning.model_evaluations') + .factory('horizon.dashboard.machine_learning.model_evaluations.create.service', createService); + + createService.$inject = [ + '$location', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.wizard-modal.service', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.model_evaluations.model_evaluationModel', + 'horizon.dashboard.machine_learning.model_evaluations.events', + 'horizon.dashboard.machine_learning.model_evaluations.resourceType', + 'horizon.dashboard.machine_learning.model_evaluations.workflow' + ]; + + function createService( + $location, policy, actionResult, gettext, $qExtensions, wizardModalService, toast, model, events, resourceType, createWorkflow + ) { + var scope; + var message = { + success: gettext('ModelEvaluation %s was successfully created.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function perform(selected, newScope) { + scope = newScope; + scope.workflow = createWorkflow; + scope.model = model; + scope.model.init(); + // for creation according to selected item + scope.selected = selected; + return wizardModalService.modal({ + scope: scope, + workflow: createWorkflow, + submit: submit + }).result; + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + //return policy.ifAllowed({ rules: [['model_evaluation', 'add_model_evaluation']] }); + } + + function submit(){ + return model.createModelEvaluation().then(success); + } + + function success(response) { + response.data.id = response.data.id; + toast.add('success', interpolate(message.success, [response.data.id])); + var result = actionResult.getActionResult() + .created(resourceType, response.data.id); + if(result.result.failed.length == 0 && result.result.created.length > 0){ + $location.path('/project/machine_learning/model_evaluations'); + }else{ + return result.result; + } + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/info/info.help.html b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/info/info.help.html new file mode 100644 index 0000000..8132a6a --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/info/info.help.html @@ -0,0 +1,8 @@ +
+
ModelEvaluation Name
+
An arbitrary human-readable name.
+
ModelEvaluation Description
+
An arbitrary human-readable description.
+
Template
+
Name or ID of model_evaluation template.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/info/info.html b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/info/info.html new file mode 100644 index 0000000..cba60e7 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/info/info.html @@ -0,0 +1,24 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/info/template.info.controller.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/info/template.info.controller.js new file mode 100644 index 0000000..3133003 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/info/template.info.controller.js @@ -0,0 +1,34 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createModelEvaluationInfoController + * @ngController + * @description + * Controller for the model_evaluation info step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.model_evaluations') + .controller('createModelEvaluationInfoController', createModelEvaluationInfoController); + + createModelEvaluationInfoController.$inject = [ + ]; + + function createModelEvaluationInfoController() { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/model_evaluation-model.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/model_evaluation-model.js new file mode 100644 index 0000000..bbb6b4a --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/model_evaluation-model.js @@ -0,0 +1,73 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.model_evaluations') + .factory('horizon.dashboard.machine_learning.model_evaluations.model_evaluationModel', model_evaluationModel); + + model_evaluationModel.$inject = [ + 'horizon.app.core.openstack-service-api.meteos' + ]; + + function model_evaluationModel(meteos) { + var model = { + newModelEvaluationSpec: {}, + + // API methods + init: init, + createModelEvaluation: createModelEvaluation + }; + + function initNewModelEvaluationSpec() { + model.newModelEvaluationSpec = { + display_name: null, + model_id: null, + source_dataset_url: null, + dataset_format: null, + swift_tenant: null, + swift_username: null, + swift_password: null + }; + } + + function init() { + // Reset the new ModelEvaluation spec + initNewModelEvaluationSpec(); + } + + function createModelEvaluation() { + var finalSpec = angular.copy(model.newModelEvaluationSpec); + + cleanNullProperties(finalSpec); + + return meteos.createModelEvaluation(finalSpec); + } + + function cleanNullProperties(finalSpec) { + // Initially clean fields that don't have any value. + // Not only "null", blank too. + for (var key in finalSpec) { + if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null + || finalSpec[key] === "") { + delete finalSpec[key]; + } + } + } + + return model; + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/spec/spec.help.html b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/spec/spec.help.html new file mode 100644 index 0000000..f20f2f6 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/spec/spec.help.html @@ -0,0 +1,14 @@ +
+
Spark Version
+
Version of Apache Spark.
+
Master Nodes
+
Number of Master Node.
+
Master Flavor
+
Flavor ID of Master Node.
+
Worker Nodes
+
Number of Worker Node.
+
Worker Flavor
+
Flavor ID of Worker Node.
+
Floating IP Pool
+
The UUID of Neutron External Network.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/spec/spec.html b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/spec/spec.html new file mode 100644 index 0000000..81ba0de --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/spec/spec.html @@ -0,0 +1,62 @@ +
+
+ +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/spec/template.spec.controller.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/spec/template.spec.controller.js new file mode 100644 index 0000000..253cca1 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/create/spec/template.spec.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createModelEvaluationSpecController + * @ngController + * @description + * Controller for the model_evaluation spec step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.model_evaluations') + .controller('createModelEvaluationSpecController', createModelEvaluationSpecController); + + createModelEvaluationSpecController.$inject = [ + '$scope', + 'horizon.framework.util.i18n.gettext' + ]; + + function createModelEvaluationSpecController($scope, gettext) { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/delete/delete.service.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/delete/delete.service.js new file mode 100644 index 0000000..29aefc8 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/delete/delete.service.js @@ -0,0 +1,149 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.model_evaluations') + .factory('horizon.dashboard.machine_learning.model_evaluations.delete.service', deleteService); + + deleteService.$inject = [ + '$location', + '$q', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.model_evaluations.resourceType', + 'horizon.dashboard.machine_learning.model_evaluations.events' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.machine_learning.model_evaluations.delete.service + * @Description + * Brings up the delete model_evaluations confirmation modal dialog. + * On submit, delete selected resources. + * On cancel, do nothing. + */ + function deleteService( + $location, $q, meteos, policy, actionResult, gettext, $qExtensions, deleteModal, toast, resourceType, events + ) { + var scope; + var context = { + labels: null, + deleteEntity: deleteEntity, + successEvent: events.DELETE_SUCCESS + }; + var service = { + initAction: initAction, + allowed: allowed, + perform: perform + }; + var notAllowedMessage = gettext("You are not allowed to delete model_evaluations: %s"); + + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + // delete selected resource objects + function perform(selected, newScope) { + scope = newScope; + var selected = angular.isArray(selected) ? selected : [selected]; + context.labels = labelize(selected.length); + return $qExtensions.allSettled(selected.map(checkPermission)).then(afterCheck); + } + + function labelize(count){ + return { + title: ngettext('Confirm Delete ModelEvaluation', + 'Confirm Delete ModelEvaluations', count), + /* eslint-disable max-len */ + message: ngettext('You have selected "%s". Please confirm your selection. Deleted model_evaluation is not recoverable.', + 'You have selected "%s". Please confirm your selection. Deleted model_evaluations are not recoverable.', count), + /* eslint-enable max-len */ + submit: ngettext('Delete ModelEvaluation', + 'Delete ModelEvaluations', count), + success: ngettext('Deleted ModelEvaluation: %s.', + 'Deleted ModelEvaluations: %s.', count), + error: ngettext('Unable to delete ModelEvaluation: %s.', + 'Unable to delete ModelEvaluations: %s.', count) + }; + } + + // for batch delete + function checkPermission(selected) { + return {promise: allowed(selected), context: selected}; + } + + // for batch delete + function afterCheck(result){ + var outcome = $q.reject(); // Reject the promise by default + if (result.fail.length > 0) { + toast.add('error', getMessage(notAllowedMessage, result.fail)); + outcome = $q.reject(result.fail); + } + if (result.pass.length > 0) { + outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult); + } + return outcome; + } + + function createResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var result = actionResult.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + result.deleted(resourceType, getEntity(item).id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + result.failed(resourceType, getEntity(item).id); + }); + if(result.result.failed.length == 0 && result.result.deleted.length > 0){ + $location.path('/project/machine_learning/model_evaluations'); + }else{ + return result.result; + } + } + + function getMessage(message, entities) { + return interpolate(message, [entities.map(getName).join(", ")]); + } + + function getName(result) { + return getEntity(result).name; + } + + // for batch delete + function getEntity(result) { + return result.context; + } + + // call delete REST API + function deleteEntity(id){ + return meteos.deleteModelEvaluation(id, true); + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/details/drawer.controller.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/details/drawer.controller.js new file mode 100644 index 0000000..9f81610 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/details/drawer.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name horizon.dashboard.machine_learning.model_evaluations.DrawerController + * @description + * This is the controller for the drawer (summary) view. + * Its primary purpose is to provide the metadata definitions to + * the model_evaluation via the ctrl.metadataDefs member. + */ + angular + .module('horizon.dashboard.machine_learning.model_evaluations') + .controller('horizon.dashboard.machine_learning.model_evaluations.DrawerController', controller); + + controller.$inject = [ + ]; + + function controller() { + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/details/drawer.html b/meteos_ui/static/dashboard/machine_learning/model_evaluations/details/drawer.html new file mode 100644 index 0000000..9ad941c --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/details/drawer.html @@ -0,0 +1,14 @@ +
+
+ +
+
ID
+
{$ item.id $}
+
+
+
Description
+
{$ item.description $}
+
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/model_evaluations.module.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/model_evaluations.module.js new file mode 100644 index 0000000..96f7006 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/model_evaluations.module.js @@ -0,0 +1,180 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.model_evaluations + * @ngModule + * @description + * Provides all the services and widgets require to display the model_evaluation + * panel + */ + angular + .module('horizon.dashboard.machine_learning.model_evaluations', [ + 'ngRoute', + 'horizon.dashboard.machine_learning.model_evaluations.actions' + ]) + .constant('horizon.dashboard.machine_learning.model_evaluations.events', events()) + .constant('horizon.dashboard.machine_learning.model_evaluations.resourceType', 'OS::Meteos::ModelEvaluation') + .run(run) + .config(config); + + /** + * @ngdoc constant + * @name horizon.dashboard.machine_learning.model_evaluations.events + * @description A list of events used by ModelEvaluation + */ + function events() { + return { + CREATE_SUCCESS: 'horizon.dashboard.machine_learning.model_evaluations.CREATE_SUCCESS', + DELETE_SUCCESS: 'horizon.dashboard.machine_learning.model_evaluations.DELETE_SUCCESS' + }; + } + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.dashboard.machine_learning.model_evaluations.basePath', + 'horizon.dashboard.machine_learning.model_evaluations.resourceType' + ]; + + function run(registry, meteos, basePath, resourceType) { + registry.getResourceType(resourceType) + .setNames(gettext('Machine Learning'), gettext('ModelEvaluations')) + // for detail summary view on table row. + .setSummaryTemplateUrl(basePath + 'details/drawer.html') + // for table row items and detail summary view. + .setProperty('name', { + label: gettext('Name') + }) + .setProperty('id', { + label: gettext('ID') + }) + .setProperty('status', { + label: gettext('Status') + }) + .setProperty('model_type', { + label: gettext('Model Type') + }) + .setProperty('source_dataset_url', { + label: gettext('Source Dataset URL') + }) + .setProperty('stdout', { + label: gettext('Result') + }) + .setListFunction(listFunction) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + filters: ['noName'], + urlFunction: urlFunction + }) + .append({ + id: 'id', + priority: 3 + }) + .append({ + id: 'status', + priority: 2 + }) + .append({ + id: 'model_type', + priority: 2 + }) + .append({ + id: 'source_dataset_url', + priority: 2 + }) + .append({ + id: 'stdout', + priority: 2 + }) + // for magic-search + registry.getResourceType(resourceType).filterFacets + .append({ + 'label': gettext('Name'), + 'name': 'name', + 'singleton': true + }) + .append({ + 'label': gettext('ID'), + 'name': 'id', + 'singleton': true + }) + .append({ + 'label': gettext('Status'), + 'name': 'status', + 'singleton': true + }) + .append({ + 'label': gettext('Model Type'), + 'name': 'model_type', + 'singleton': true + }) + .append({ + 'label': gettext('Source Dataset URL'), + 'name': 'source_dataset_url', + 'singleton': true + }) + .append({ + 'label': gettext('Result'), + 'name': 'stdout', + 'singleton': true + }); + + function listFunction(params) { + return meteos.getModelEvaluations(params).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(addTrackBy)}}; + + function addTrackBy(item) { + item.trackBy = item.id; + return item; + } + } + } + + function urlFunction(item) { + return 'project/ngdetails/OS::Meteos::ModelEvaluation/' + item.id; + } + } + + config.$inject = [ + '$provide', + '$windowProvider', + '$routeProvider' + ]; + + /** + * @name config + * @param {Object} $provide + * @param {Object} $windowProvider + * @param {Object} $routeProvider + * @description Routes used by this module. + * @returns {undefined} Returns nothing + */ + function config($provide, $windowProvider, $routeProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/machine_learning/model_evaluations/'; + $provide.constant('horizon.dashboard.machine_learning.model_evaluations.basePath', path); + $routeProvider.when('/project/machine_learning/model_evaluations', { + templateUrl: path + 'panel.html' + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/model_evaluations.module.spec.js b/meteos_ui/static/dashboard/machine_learning/model_evaluations/model_evaluations.module.spec.js new file mode 100644 index 0000000..a1135c0 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/model_evaluations.module.spec.js @@ -0,0 +1,23 @@ +/** + * 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. + */ +(function() { + 'use strict'; + + describe('horizon.dashboard.machine_learning.model_evaluations', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.machine_learning.model_evaluations')).toBeDefined(); + }); + }); + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/model_evaluations.scss b/meteos_ui/static/dashboard/machine_learning/model_evaluations/model_evaluations.scss new file mode 100644 index 0000000..160d1aa --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/model_evaluations.scss @@ -0,0 +1,3 @@ +.logs { + margin-top: 18px; +} \ No newline at end of file diff --git a/meteos_ui/static/dashboard/machine_learning/model_evaluations/panel.html b/meteos_ui/static/dashboard/machine_learning/model_evaluations/panel.html new file mode 100644 index 0000000..d8e5185 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/model_evaluations/panel.html @@ -0,0 +1,4 @@ + + + diff --git a/meteos_ui/static/dashboard/machine_learning/models/actions.module.js b/meteos_ui/static/dashboard/machine_learning/models/actions.module.js new file mode 100644 index 0000000..73e5587 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/actions.module.js @@ -0,0 +1,66 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.machine_learning.models.actions + * + * @description + * Provides all of the actions for models. + */ + angular.module('horizon.dashboard.machine_learning.models.actions', ['horizon.framework', 'horizon.dashboard.machine_learning']) + .run(registerModelActions); + + registerModelActions.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.dashboard.machine_learning.models.create.service', + 'horizon.dashboard.machine_learning.models.delete.service', + 'horizon.dashboard.machine_learning.models.resourceType', + ]; + + function registerModelActions( + registry, + gettext, + createModelService, + deleteModelService, + resourceType) + { + var modelsResourceType = registry.getResourceType(resourceType); + + modelsResourceType.globalActions + .append({ + id: 'createModelAction', + service: createModelService, + template: { + type: 'create', + text: gettext('Create Model') + } + }); + + modelsResourceType.batchActions + .append({ + id: 'batchDeleteModelAction', + service: deleteModelService, + template: { + type: 'delete-selected', + text: gettext('Delete Models') + } + }); + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/create/create-workflow.service.js b/meteos_ui/static/dashboard/machine_learning/models/create/create-workflow.service.js new file mode 100644 index 0000000..a9744b7 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/create/create-workflow.service.js @@ -0,0 +1,56 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.models') + .factory('horizon.dashboard.machine_learning.models.workflow', modelWorkflow); + + modelWorkflow.$inject = [ + 'horizon.dashboard.machine_learning.basePath', + 'horizon.app.core.workflow.factory', + 'horizon.framework.util.i18n.gettext' + ]; + + function modelWorkflow(basePath, dashboardWorkflow, gettext) { + return dashboardWorkflow({ + title: gettext('Create Model'), + + steps: [ + { + title: gettext('Info'), + templateUrl: basePath + 'models/create/info/info.html', + helpUrl: basePath + 'models/create/info/info.help.html', + formName: 'modelInfoForm' + }, + { + title: gettext('Spec'), + templateUrl: basePath + 'models/create/spec/spec.html', + helpUrl: basePath + 'models/create/spec/spec.help.html', + formName: 'modelSpecForm' + }, + ], + + btnText: { + finish: gettext('Create') + }, + + btnIcon: { + finish: 'fa fa-check' + } + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/create/create.service.js b/meteos_ui/static/dashboard/machine_learning/models/create/create.service.js new file mode 100644 index 0000000..d1cc733 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/create/create.service.js @@ -0,0 +1,97 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.models.create.service + * @description Service for the model create modal + */ + angular + .module('horizon.dashboard.machine_learning.models') + .factory('horizon.dashboard.machine_learning.models.create.service', createService); + + createService.$inject = [ + '$location', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.wizard-modal.service', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.models.modelModel', + 'horizon.dashboard.machine_learning.models.events', + 'horizon.dashboard.machine_learning.models.resourceType', + 'horizon.dashboard.machine_learning.models.workflow' + ]; + + function createService( + $location, policy, actionResult, gettext, $qExtensions, wizardModalService, toast, model, events, resourceType, createWorkflow + ) { + var scope; + var message = { + success: gettext('Model %s was successfully created.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function perform(selected, newScope) { + scope = newScope; + scope.workflow = createWorkflow; + scope.model = model; + scope.model.init(); + // for creation according to selected item + scope.selected = selected; + return wizardModalService.modal({ + scope: scope, + workflow: createWorkflow, + submit: submit + }).result; + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + //return policy.ifAllowed({ rules: [['model', 'add_model']] }); + } + + function submit(){ + return model.createModel().then(success); + } + + function success(response) { + response.data.id = response.data.id; + toast.add('success', interpolate(message.success, [response.data.id])); + var result = actionResult.getActionResult() + .created(resourceType, response.data.id); + if(result.result.failed.length == 0 && result.result.created.length > 0){ + $location.path('/project/machine_learning/models'); + }else{ + return result.result; + } + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/create/info/info.help.html b/meteos_ui/static/dashboard/machine_learning/models/create/info/info.help.html new file mode 100644 index 0000000..af8ce56 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/create/info/info.help.html @@ -0,0 +1,8 @@ +
+
Model Name
+
An arbitrary human-readable name.
+
Model Description
+
An arbitrary human-readable description.
+
Template
+
Name or ID of model template.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/models/create/info/info.html b/meteos_ui/static/dashboard/machine_learning/models/create/info/info.html new file mode 100644 index 0000000..0dfc596 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/create/info/info.html @@ -0,0 +1,32 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/models/create/info/template.info.controller.js b/meteos_ui/static/dashboard/machine_learning/models/create/info/template.info.controller.js new file mode 100644 index 0000000..84bf88e --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/create/info/template.info.controller.js @@ -0,0 +1,34 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createModelInfoController + * @ngController + * @description + * Controller for the model info step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.models') + .controller('createModelInfoController', createModelInfoController); + + createModelInfoController.$inject = [ + ]; + + function createModelInfoController() { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/create/model-model.js b/meteos_ui/static/dashboard/machine_learning/models/create/model-model.js new file mode 100644 index 0000000..2b22661 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/create/model-model.js @@ -0,0 +1,76 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.models') + .factory('horizon.dashboard.machine_learning.models.modelModel', modelModel); + + modelModel.$inject = [ + 'horizon.app.core.openstack-service-api.meteos' + ]; + + function modelModel(meteos) { + var model = { + newModelSpec: {}, + + // API methods + init: init, + createModel: createModel + }; + + function initNewModelSpec() { + model.newModelSpec = { + display_name: null, + display_description: null, + source_dataset_url: null, + dataset_format: null, + experiment_id: null, + model_type: null, + model_params: null, + swift_tenant: null, + swift_username: null, + swift_password: null + }; + } + + function init() { + // Reset the new Model spec + initNewModelSpec(); + } + + function createModel() { + var finalSpec = angular.copy(model.newModelSpec); + + cleanNullProperties(finalSpec); + + return meteos.createModel(finalSpec); + } + + function cleanNullProperties(finalSpec) { + // Initially clean fields that don't have any value. + // Not only "null", blank too. + for (var key in finalSpec) { + if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null + || finalSpec[key] === "") { + delete finalSpec[key]; + } + } + } + + return model; + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/create/spec/spec.help.html b/meteos_ui/static/dashboard/machine_learning/models/create/spec/spec.help.html new file mode 100644 index 0000000..f20f2f6 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/create/spec/spec.help.html @@ -0,0 +1,14 @@ +
+
Spark Version
+
Version of Apache Spark.
+
Master Nodes
+
Number of Master Node.
+
Master Flavor
+
Flavor ID of Master Node.
+
Worker Nodes
+
Number of Worker Node.
+
Worker Flavor
+
Flavor ID of Worker Node.
+
Floating IP Pool
+
The UUID of Neutron External Network.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/models/create/spec/spec.html b/meteos_ui/static/dashboard/machine_learning/models/create/spec/spec.html new file mode 100644 index 0000000..f0cd621 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/create/spec/spec.html @@ -0,0 +1,84 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/models/create/spec/template.spec.controller.js b/meteos_ui/static/dashboard/machine_learning/models/create/spec/template.spec.controller.js new file mode 100644 index 0000000..94df2ef --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/create/spec/template.spec.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createModelSpecController + * @ngController + * @description + * Controller for the model spec step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.models') + .controller('createModelSpecController', createModelSpecController); + + createModelSpecController.$inject = [ + '$scope', + 'horizon.framework.util.i18n.gettext' + ]; + + function createModelSpecController($scope, gettext) { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/delete/delete.service.js b/meteos_ui/static/dashboard/machine_learning/models/delete/delete.service.js new file mode 100644 index 0000000..14daff0 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/delete/delete.service.js @@ -0,0 +1,149 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.models') + .factory('horizon.dashboard.machine_learning.models.delete.service', deleteService); + + deleteService.$inject = [ + '$location', + '$q', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.models.resourceType', + 'horizon.dashboard.machine_learning.models.events' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.machine_learning.models.delete.service + * @Description + * Brings up the delete models confirmation modal dialog. + * On submit, delete selected resources. + * On cancel, do nothing. + */ + function deleteService( + $location, $q, meteos, policy, actionResult, gettext, $qExtensions, deleteModal, toast, resourceType, events + ) { + var scope; + var context = { + labels: null, + deleteEntity: deleteEntity, + successEvent: events.DELETE_SUCCESS + }; + var service = { + initAction: initAction, + allowed: allowed, + perform: perform + }; + var notAllowedMessage = gettext("You are not allowed to delete models: %s"); + + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + // delete selected resource objects + function perform(selected, newScope) { + scope = newScope; + var selected = angular.isArray(selected) ? selected : [selected]; + context.labels = labelize(selected.length); + return $qExtensions.allSettled(selected.map(checkPermission)).then(afterCheck); + } + + function labelize(count){ + return { + title: ngettext('Confirm Delete Model', + 'Confirm Delete Models', count), + /* eslint-disable max-len */ + message: ngettext('You have selected "%s". Please confirm your selection. Deleted model is not recoverable.', + 'You have selected "%s". Please confirm your selection. Deleted models are not recoverable.', count), + /* eslint-enable max-len */ + submit: ngettext('Delete Model', + 'Delete Models', count), + success: ngettext('Deleted Model: %s.', + 'Deleted Models: %s.', count), + error: ngettext('Unable to delete Model: %s.', + 'Unable to delete Models: %s.', count) + }; + } + + // for batch delete + function checkPermission(selected) { + return {promise: allowed(selected), context: selected}; + } + + // for batch delete + function afterCheck(result){ + var outcome = $q.reject(); // Reject the promise by default + if (result.fail.length > 0) { + toast.add('error', getMessage(notAllowedMessage, result.fail)); + outcome = $q.reject(result.fail); + } + if (result.pass.length > 0) { + outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult); + } + return outcome; + } + + function createResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var result = actionResult.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + result.deleted(resourceType, getEntity(item).id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + result.failed(resourceType, getEntity(item).id); + }); + if(result.result.failed.length == 0 && result.result.deleted.length > 0){ + $location.path('/project/machine_learning/models'); + }else{ + return result.result; + } + } + + function getMessage(message, entities) { + return interpolate(message, [entities.map(getName).join(", ")]); + } + + function getName(result) { + return getEntity(result).name; + } + + // for batch delete + function getEntity(result) { + return result.context; + } + + // call delete REST API + function deleteEntity(id){ + return meteos.deleteModel(id, true); + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/details/drawer.controller.js b/meteos_ui/static/dashboard/machine_learning/models/details/drawer.controller.js new file mode 100644 index 0000000..f11c2e7 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/details/drawer.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name horizon.dashboard.machine_learning.models.DrawerController + * @description + * This is the controller for the drawer (summary) view. + * Its primary purpose is to provide the metadata definitions to + * the model via the ctrl.metadataDefs member. + */ + angular + .module('horizon.dashboard.machine_learning.models') + .controller('horizon.dashboard.machine_learning.models.DrawerController', controller); + + controller.$inject = [ + ]; + + function controller() { + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/details/drawer.html b/meteos_ui/static/dashboard/machine_learning/models/details/drawer.html new file mode 100644 index 0000000..b611105 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/details/drawer.html @@ -0,0 +1,14 @@ +
+
+ +
+
ID
+
{$ item.id $}
+
+
+
Description
+
{$ item.description $}
+
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/models/models.module.js b/meteos_ui/static/dashboard/machine_learning/models/models.module.js new file mode 100644 index 0000000..ba07749 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/models.module.js @@ -0,0 +1,168 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.models + * @ngModule + * @description + * Provides all the services and widgets require to display the model + * panel + */ + angular + .module('horizon.dashboard.machine_learning.models', [ + 'ngRoute', + 'horizon.dashboard.machine_learning.models.actions' + ]) + .constant('horizon.dashboard.machine_learning.models.events', events()) + .constant('horizon.dashboard.machine_learning.models.resourceType', 'OS::Meteos::Model') + .run(run) + .config(config); + + /** + * @ngdoc constant + * @name horizon.dashboard.machine_learning.models.events + * @description A list of events used by Model + */ + function events() { + return { + CREATE_SUCCESS: 'horizon.dashboard.machine_learning.models.CREATE_SUCCESS', + DELETE_SUCCESS: 'horizon.dashboard.machine_learning.models.DELETE_SUCCESS' + }; + } + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.dashboard.machine_learning.models.basePath', + 'horizon.dashboard.machine_learning.models.resourceType' + ]; + + function run(registry, meteos, basePath, resourceType) { + registry.getResourceType(resourceType) + .setNames(gettext('Machine Learning'), gettext('Models')) + // for detail summary view on table row. + .setSummaryTemplateUrl(basePath + 'details/drawer.html') + // for table row items and detail summary view. + .setProperty('name', { + label: gettext('Name') + }) + .setProperty('id', { + label: gettext('ID') + }) + .setProperty('status', { + label: gettext('Status') + }) + .setProperty('key_name', { + label: gettext('Key Name') + }) + .setProperty('management_network', { + label: gettext('Management Network') + }) + .setListFunction(listFunction) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + filters: ['noName'], + urlFunction: urlFunction + }) + .append({ + id: 'id', + priority: 3 + }) + .append({ + id: 'status', + priority: 2 + }) + .append({ + id: 'key_name', + priority: 2 + }) + .append({ + id: 'management_network', + priority: 2 + }) + // for magic-search + registry.getResourceType(resourceType).filterFacets + .append({ + 'label': gettext('Name'), + 'name': 'name', + 'singleton': true + }) + .append({ + 'label': gettext('ID'), + 'name': 'id', + 'singleton': true + }) + .append({ + 'label': gettext('Status'), + 'name': 'status', + 'singleton': true + }) + .append({ + 'label': gettext('Key Name'), + 'name': 'key_name', + 'singleton': true + }) + .append({ + 'label': gettext('Management Network'), + 'name': 'management_network', + 'singleton': true + }); + + function listFunction(params) { + return meteos.getModels(params).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(addTrackBy)}}; + + function addTrackBy(item) { + item.trackBy = item.id; + return item; + } + } + } + + function urlFunction(item) { + return 'project/ngdetails/OS::Meteos::Model/' + item.id; + } + } + + config.$inject = [ + '$provide', + '$windowProvider', + '$routeProvider' + ]; + + /** + * @name config + * @param {Object} $provide + * @param {Object} $windowProvider + * @param {Object} $routeProvider + * @description Routes used by this module. + * @returns {undefined} Returns nothing + */ + function config($provide, $windowProvider, $routeProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/machine_learning/models/'; + $provide.constant('horizon.dashboard.machine_learning.models.basePath', path); + $routeProvider.when('/project/machine_learning/models', { + templateUrl: path + 'panel.html' + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/models.module.spec.js b/meteos_ui/static/dashboard/machine_learning/models/models.module.spec.js new file mode 100644 index 0000000..199a7e3 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/models.module.spec.js @@ -0,0 +1,23 @@ +/** + * 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. + */ +(function() { + 'use strict'; + + describe('horizon.dashboard.machine_learning.models', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.machine_learning.models')).toBeDefined(); + }); + }); + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/models/models.scss b/meteos_ui/static/dashboard/machine_learning/models/models.scss new file mode 100644 index 0000000..160d1aa --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/models.scss @@ -0,0 +1,3 @@ +.logs { + margin-top: 18px; +} \ No newline at end of file diff --git a/meteos_ui/static/dashboard/machine_learning/models/panel.html b/meteos_ui/static/dashboard/machine_learning/models/panel.html new file mode 100644 index 0000000..830c6d5 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/models/panel.html @@ -0,0 +1,4 @@ + + + diff --git a/meteos_ui/static/dashboard/machine_learning/templates/actions.module.js b/meteos_ui/static/dashboard/machine_learning/templates/actions.module.js new file mode 100644 index 0000000..27cf79f --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/actions.module.js @@ -0,0 +1,66 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.machine_learning.templates.actions + * + * @description + * Provides all of the actions for templates. + */ + angular.module('horizon.dashboard.machine_learning.templates.actions', ['horizon.framework', 'horizon.dashboard.machine_learning']) + .run(registerTemplateActions); + + registerTemplateActions.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.dashboard.machine_learning.templates.create.service', + 'horizon.dashboard.machine_learning.templates.delete.service', + 'horizon.dashboard.machine_learning.templates.resourceType', + ]; + + function registerTemplateActions( + registry, + gettext, + createTemplateService, + deleteTemplateService, + resourceType) + { + var templatesResourceType = registry.getResourceType(resourceType); + + templatesResourceType.globalActions + .append({ + id: 'createTemplateAction', + service: createTemplateService, + template: { + type: 'create', + text: gettext('Create Template') + } + }); + + templatesResourceType.batchActions + .append({ + id: 'batchDeleteTemplateAction', + service: deleteTemplateService, + template: { + type: 'delete-selected', + text: gettext('Delete Templates') + } + }); + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/create/create-workflow.service.js b/meteos_ui/static/dashboard/machine_learning/templates/create/create-workflow.service.js new file mode 100644 index 0000000..be55a7e --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/create/create-workflow.service.js @@ -0,0 +1,56 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.templates') + .factory('horizon.dashboard.machine_learning.templates.workflow', templateWorkflow); + + templateWorkflow.$inject = [ + 'horizon.dashboard.machine_learning.basePath', + 'horizon.app.core.workflow.factory', + 'horizon.framework.util.i18n.gettext' + ]; + + function templateWorkflow(basePath, dashboardWorkflow, gettext) { + return dashboardWorkflow({ + title: gettext('Create Template'), + + steps: [ + { + title: gettext('Info'), + templateUrl: basePath + 'templates/create/info/info.html', + helpUrl: basePath + 'templates/create/info/info.help.html', + formName: 'templateInfoForm' + }, + { + title: gettext('Spec'), + templateUrl: basePath + 'templates/create/spec/spec.html', + helpUrl: basePath + 'templates/create/spec/spec.help.html', + formName: 'templateSpecForm' + }, + ], + + btnText: { + finish: gettext('Create') + }, + + btnIcon: { + finish: 'fa fa-check' + } + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/create/create.service.js b/meteos_ui/static/dashboard/machine_learning/templates/create/create.service.js new file mode 100644 index 0000000..bd421e3 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/create/create.service.js @@ -0,0 +1,97 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.templates.create.service + * @description Service for the template create modal + */ + angular + .module('horizon.dashboard.machine_learning.templates') + .factory('horizon.dashboard.machine_learning.templates.create.service', createService); + + createService.$inject = [ + '$location', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.wizard-modal.service', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.templates.templateModel', + 'horizon.dashboard.machine_learning.templates.events', + 'horizon.dashboard.machine_learning.templates.resourceType', + 'horizon.dashboard.machine_learning.templates.workflow' + ]; + + function createService( + $location, policy, actionResult, gettext, $qExtensions, wizardModalService, toast, model, events, resourceType, createWorkflow + ) { + var scope; + var message = { + success: gettext('Template %s was successfully created.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function perform(selected, newScope) { + scope = newScope; + scope.workflow = createWorkflow; + scope.model = model; + scope.model.init(); + // for creation according to selected item + scope.selected = selected; + return wizardModalService.modal({ + scope: scope, + workflow: createWorkflow, + submit: submit + }).result; + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + //return policy.ifAllowed({ rules: [['template', 'add_template']] }); + } + + function submit(){ + return model.createTemplate().then(success); + } + + function success(response) { + response.data.id = response.data.id; + toast.add('success', interpolate(message.success, [response.data.id])); + var result = actionResult.getActionResult() + .created(resourceType, response.data.id); + if(result.result.failed.length == 0 && result.result.created.length > 0){ + $location.path('/project/machine_learning/templates'); + }else{ + return result.result; + } + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/create/info/info.help.html b/meteos_ui/static/dashboard/machine_learning/templates/create/info/info.help.html new file mode 100644 index 0000000..30f7e7d --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/create/info/info.help.html @@ -0,0 +1,8 @@ +
+
Template Name
+
An arbitrary human-readable name.
+
Template Description
+
An arbitrary human-readable description.
+
Image
+
Name or ID of template image.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/templates/create/info/info.html b/meteos_ui/static/dashboard/machine_learning/templates/create/info/info.html new file mode 100644 index 0000000..b83f4ed --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/create/info/info.html @@ -0,0 +1,32 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/templates/create/info/template.info.controller.js b/meteos_ui/static/dashboard/machine_learning/templates/create/info/template.info.controller.js new file mode 100644 index 0000000..12bd57f --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/create/info/template.info.controller.js @@ -0,0 +1,34 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createTemplateInfoController + * @ngController + * @description + * Controller for the template info step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.templates') + .controller('createTemplateInfoController', createTemplateInfoController); + + createTemplateInfoController.$inject = [ + ]; + + function createTemplateInfoController() { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/create/spec/spec.help.html b/meteos_ui/static/dashboard/machine_learning/templates/create/spec/spec.help.html new file mode 100644 index 0000000..f20f2f6 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/create/spec/spec.help.html @@ -0,0 +1,14 @@ +
+
Spark Version
+
Version of Apache Spark.
+
Master Nodes
+
Number of Master Node.
+
Master Flavor
+
Flavor ID of Master Node.
+
Worker Nodes
+
Number of Worker Node.
+
Worker Flavor
+
Flavor ID of Worker Node.
+
Floating IP Pool
+
The UUID of Neutron External Network.
+
diff --git a/meteos_ui/static/dashboard/machine_learning/templates/create/spec/spec.html b/meteos_ui/static/dashboard/machine_learning/templates/create/spec/spec.html new file mode 100644 index 0000000..c12bb7b --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/create/spec/spec.html @@ -0,0 +1,76 @@ +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/templates/create/spec/template.spec.controller.js b/meteos_ui/static/dashboard/machine_learning/templates/create/spec/template.spec.controller.js new file mode 100644 index 0000000..d3c1e50 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/create/spec/template.spec.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name createTemplateSpecController + * @ngController + * @description + * Controller for the template spec step in create workflow + */ + angular + .module('horizon.dashboard.machine_learning.templates') + .controller('createTemplateSpecController', createTemplateSpecController); + + createTemplateSpecController.$inject = [ + '$scope', + 'horizon.framework.util.i18n.gettext' + ]; + + function createTemplateSpecController($scope, gettext) { + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/create/template-model.js b/meteos_ui/static/dashboard/machine_learning/templates/create/template-model.js new file mode 100644 index 0000000..8a0f82c --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/create/template-model.js @@ -0,0 +1,75 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.templates') + .factory('horizon.dashboard.machine_learning.templates.templateModel', templateModel); + + templateModel.$inject = [ + 'horizon.app.core.openstack-service-api.meteos' + ]; + + function templateModel(meteos) { + var model = { + newTemplateSpec: {}, + + // API methods + init: init, + createTemplate: createTemplate + }; + + function initNewTemplateSpec() { + model.newTemplateSpec = { + display_name: null, + display_description: null, + image_id: null, + master_nodes_num: null, + master_flavor_id: null, + worker_nodes_num: null, + worker_flavor_id: null, + spark_version: null, + floating_ip_pool: null + }; + } + + function init() { + // Reset the new Template spec + initNewTemplateSpec(); + } + + function createTemplate() { + var finalSpec = angular.copy(model.newTemplateSpec); + + cleanNullProperties(finalSpec); + + return meteos.createTemplate(finalSpec); + } + + function cleanNullProperties(finalSpec) { + // Initially clean fields that don't have any value. + // Not only "null", blank too. + for (var key in finalSpec) { + if (finalSpec.hasOwnProperty(key) && finalSpec[key] === null + || finalSpec[key] === "") { + delete finalSpec[key]; + } + } + } + + return model; + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/delete/delete.service.js b/meteos_ui/static/dashboard/machine_learning/templates/delete/delete.service.js new file mode 100644 index 0000000..788c349 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/delete/delete.service.js @@ -0,0 +1,149 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.templates') + .factory('horizon.dashboard.machine_learning.templates.delete.service', deleteService); + + deleteService.$inject = [ + '$location', + '$q', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.framework.widgets.toast.service', + 'horizon.dashboard.machine_learning.templates.resourceType', + 'horizon.dashboard.machine_learning.templates.events' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.machine_learning.templates.delete.service + * @Description + * Brings up the delete templates confirmation modal dialog. + * On submit, delete selected resources. + * On cancel, do nothing. + */ + function deleteService( + $location, $q, meteos, policy, actionResult, gettext, $qExtensions, deleteModal, toast, resourceType, events + ) { + var scope; + var context = { + labels: null, + deleteEntity: deleteEntity, + successEvent: events.DELETE_SUCCESS + }; + var service = { + initAction: initAction, + allowed: allowed, + perform: perform + }; + var notAllowedMessage = gettext("You are not allowed to delete templates: %s"); + + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + // delete selected resource objects + function perform(selected, newScope) { + scope = newScope; + var selected = angular.isArray(selected) ? selected : [selected]; + context.labels = labelize(selected.length); + return $qExtensions.allSettled(selected.map(checkPermission)).then(afterCheck); + } + + function labelize(count){ + return { + title: ngettext('Confirm Delete Template', + 'Confirm Delete Templates', count), + /* eslint-disable max-len */ + message: ngettext('You have selected "%s". Please confirm your selection. Deleted template is not recoverable.', + 'You have selected "%s". Please confirm your selection. Deleted templates are not recoverable.', count), + /* eslint-enable max-len */ + submit: ngettext('Delete Template', + 'Delete Templates', count), + success: ngettext('Deleted Template: %s.', + 'Deleted Templates: %s.', count), + error: ngettext('Unable to delete Template: %s.', + 'Unable to delete Templates: %s.', count) + }; + } + + // for batch delete + function checkPermission(selected) { + return {promise: allowed(selected), context: selected}; + } + + // for batch delete + function afterCheck(result){ + var outcome = $q.reject(); // Reject the promise by default + if (result.fail.length > 0) { + toast.add('error', getMessage(notAllowedMessage, result.fail)); + outcome = $q.reject(result.fail); + } + if (result.pass.length > 0) { + outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult); + } + return outcome; + } + + function createResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var result = actionResult.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + result.deleted(resourceType, getEntity(item).id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + result.failed(resourceType, getEntity(item).id); + }); + if(result.result.failed.length == 0 && result.result.deleted.length > 0){ + $location.path('/project/machine_learning/templates'); + }else{ + return result.result; + } + } + + function getMessage(message, entities) { + return interpolate(message, [entities.map(getName).join(", ")]); + } + + function getName(result) { + return getEntity(result).name; + } + + // for batch delete + function getEntity(result) { + return result.context; + } + + // call delete REST API + function deleteEntity(id){ + return meteos.deleteTemplate(id, true); + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/details/drawer.controller.js b/meteos_ui/static/dashboard/machine_learning/templates/details/drawer.controller.js new file mode 100644 index 0000000..31a2375 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/details/drawer.controller.js @@ -0,0 +1,36 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc controller + * @name horizon.dashboard.machine_learning.templates.DrawerController + * @description + * This is the controller for the drawer (summary) view. + * Its primary purpose is to provide the metadata definitions to + * the template via the ctrl.metadataDefs member. + */ + angular + .module('horizon.dashboard.machine_learning.templates') + .controller('horizon.dashboard.machine_learning.templates.DrawerController', controller); + + controller.$inject = [ + ]; + + function controller() { + } + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/details/drawer.html b/meteos_ui/static/dashboard/machine_learning/templates/details/drawer.html new file mode 100644 index 0000000..acffa8d --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/details/drawer.html @@ -0,0 +1,22 @@ +
+
+ +
+
ID
+
{$ item.id $}
+
+
+
Description
+
{$ item.description $}
+
+
+
Master Flavor
+
{$ item.master_flavor $}
+
+
+
Worker Flavor
+
{$ item.worker_flavor $}
+
+
+
+
diff --git a/meteos_ui/static/dashboard/machine_learning/templates/operations/pause.service.js b/meteos_ui/static/dashboard/machine_learning/templates/operations/pause.service.js new file mode 100644 index 0000000..7e499a4 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/operations/pause.service.js @@ -0,0 +1,69 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use self 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. + */ + +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.machine_learning.templates') + .factory('horizon.dashboard.machine_learning.templates.pause.service', pauseService); + + pauseService.$inject = [ + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.toast.service', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.framework.widgets.modal-wait-spinner.service' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.machine_learning.templates.pause.service + * @Description + * pause template. + */ + function pauseService($qExtensions, toast, meteos, modalWaitSpinnerService) { + + var message = { + success: gettext('Template %s was successfully paused.') + }; + + var service = { + initAction: initAction, + allowed: allowed, + perform: perform + }; + + return service; + + ////////////// + + // include this function in your service + // if you plan to emit events to the parent controller + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + function perform(selected) { + // pause selected template + return meteos.pauseTemplate(selected.id).then(success); + + function success(response) { + toast.add('success', interpolate(message.success, [selected.name])); + }; + } + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/panel.html b/meteos_ui/static/dashboard/machine_learning/templates/panel.html new file mode 100644 index 0000000..558646f --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/panel.html @@ -0,0 +1,4 @@ + + + diff --git a/meteos_ui/static/dashboard/machine_learning/templates/templates.module.js b/meteos_ui/static/dashboard/machine_learning/templates/templates.module.js new file mode 100644 index 0000000..221c6bb --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/templates.module.js @@ -0,0 +1,180 @@ +/** + * 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. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @name horizon.dashboard.machine_learning.templates + * @ngModule + * @description + * Provides all the services and widgets require to display the template + * panel + */ + angular + .module('horizon.dashboard.machine_learning.templates', [ + 'ngRoute', + 'horizon.dashboard.machine_learning.templates.actions' + ]) + .constant('horizon.dashboard.machine_learning.templates.events', events()) + .constant('horizon.dashboard.machine_learning.templates.resourceType', 'OS::Meteos::Template') + .run(run) + .config(config); + + /** + * @ngdoc constant + * @name horizon.dashboard.machine_learning.templates.events + * @description A list of events used by Template + */ + function events() { + return { + CREATE_SUCCESS: 'horizon.dashboard.machine_learning.templates.CREATE_SUCCESS', + DELETE_SUCCESS: 'horizon.dashboard.machine_learning.templates.DELETE_SUCCESS' + }; + } + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.openstack-service-api.meteos', + 'horizon.dashboard.machine_learning.templates.basePath', + 'horizon.dashboard.machine_learning.templates.resourceType' + ]; + + function run(registry, meteos, basePath, resourceType) { + registry.getResourceType(resourceType) + .setNames(gettext('Machine Learning'), gettext('Templates')) + // for detail summary view on table row. + .setSummaryTemplateUrl(basePath + 'details/drawer.html') + // for table row items and detail summary view. + .setProperty('name', { + label: gettext('Name') + }) + .setProperty('id', { + label: gettext('ID') + }) + .setProperty('status', { + label: gettext('Status') + }) + .setProperty('master_nodes', { + label: gettext('Master Nodes') + }) + .setProperty('worker_nodes', { + label: gettext('Worker Nodes') + }) + .setProperty('spark_version', { + label: gettext('Spark Version') + }) + .setListFunction(listFunction) + .tableColumns + .append({ + id: 'name', + priority: 1, + sortDefault: true, + filters: ['noName'], + urlFunction: urlFunction + }) + .append({ + id: 'id', + priority: 3 + }) + .append({ + id: 'status', + priority: 2 + }) + .append({ + id: 'master_nodes', + priority: 2 + }) + .append({ + id: 'worker_nodes', + priority: 2 + }) + .append({ + id: 'spark_version', + priority: 2 + }); + // for magic-search + registry.getResourceType(resourceType).filterFacets + .append({ + 'label': gettext('Name'), + 'name': 'name', + 'singleton': true + }) + .append({ + 'label': gettext('ID'), + 'name': 'id', + 'singleton': true + }) + .append({ + 'label': gettext('Status'), + 'name': 'status', + 'singleton': true + }) + .append({ + 'label': gettext('Master Nodes'), + 'name': 'master_nodes', + 'singleton': true + }) + .append({ + 'label': gettext('Worker Nodes'), + 'name': 'worker_nodes', + 'singleton': true + }) + .append({ + 'label': gettext('Spark version'), + 'name': 'spark_version', + 'singleton': true + }); + + function listFunction(params) { + return meteos.getTemplates(params).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(addTrackBy)}}; + + function addTrackBy(item) { + item.trackBy = item.id; + return item; + } + } + } + + function urlFunction(item) { + return 'project/ngdetails/OS::Meteos::Template/' + item.id; + } + } + + config.$inject = [ + '$provide', + '$windowProvider', + '$routeProvider' + ]; + + /** + * @name config + * @param {Object} $provide + * @param {Object} $windowProvider + * @param {Object} $routeProvider + * @description Routes used by this module. + * @returns {undefined} Returns nothing + */ + function config($provide, $windowProvider, $routeProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/machine_learning/templates/'; + $provide.constant('horizon.dashboard.machine_learning.templates.basePath', path); + $routeProvider.when('/project/machine_learning/templates', { + templateUrl: path + 'panel.html' + }); + } +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/templates.module.spec.js b/meteos_ui/static/dashboard/machine_learning/templates/templates.module.spec.js new file mode 100644 index 0000000..bb43d29 --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/templates.module.spec.js @@ -0,0 +1,23 @@ +/** + * 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. + */ +(function() { + 'use strict'; + + describe('horizon.dashboard.machine_learning.templates', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.machine_learning.templates')).toBeDefined(); + }); + }); + +})(); diff --git a/meteos_ui/static/dashboard/machine_learning/templates/templates.scss b/meteos_ui/static/dashboard/machine_learning/templates/templates.scss new file mode 100644 index 0000000..160d1aa --- /dev/null +++ b/meteos_ui/static/dashboard/machine_learning/templates/templates.scss @@ -0,0 +1,3 @@ +.logs { + margin-top: 18px; +} \ No newline at end of file diff --git a/meteos_ui/test/__init__.py b/meteos_ui/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/meteos_ui/test/settings.py b/meteos_ui/test/settings.py new file mode 100644 index 0000000..47923b1 --- /dev/null +++ b/meteos_ui/test/settings.py @@ -0,0 +1,37 @@ +# 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. + +# Default to Horizons test settings to avoid any missing keys +from horizon.test.settings import * # noqa +from openstack_dashboard.test.settings import * # noqa + +# pop these keys to avoid log warnings about deprecation +# update_dashboards will populate them anyway +HORIZON_CONFIG.pop('dashboards', None) +HORIZON_CONFIG.pop('default_dashboard', None) + +# Update the dashboards with meteos_ui +import openstack_dashboard.enabled +from openstack_dashboard.utils import settings +import meteos_ui.enabled + +settings.update_dashboards( + [ + meteos_ui.enabled, + openstack_dashboard.enabled, + ], + HORIZON_CONFIG, + INSTALLED_APPS +) + +# Ensure any duplicate apps are removed after the update_dashboards call +INSTALLED_APPS = list(set(INSTALLED_APPS)) diff --git a/meteos_ui/version.py b/meteos_ui/version.py new file mode 100644 index 0000000..984063f --- /dev/null +++ b/meteos_ui/version.py @@ -0,0 +1,3 @@ +import pbr.version + +version_info = pbr.version.VersionInfo('meteos-ui') diff --git a/package.json b/package.json new file mode 100644 index 0000000..32a7a3e --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "version": "0.0.0", + "private": true, + "name": "meteos-ui", + "description": "OpenStack Meteos-UI", + "repository": "none", + "license": "Apache 2.0", + "devDependencies": { + "eslint": "1.10.3", + "eslint-config-openstack": "1.2.4", + "eslint-plugin-angular": "1.0.1", + "jasmine-core": "2.4.1", + "karma": "1.1.2", + "karma-chrome-launcher": "1.0.1", + "karma-cli": "1.0.1", + "karma-coverage": "1.1.1", + "karma-jasmine": "1.0.2", + "karma-ng-html2js-preprocessor": "1.0.0", + "karma-threshold-reporter": "0.1.15" + }, + "scripts": { + "postinstall": "if [ ! -d .venv ]; then tox -epy27 --notest; fi", + "test": "karma start meteos_ui/karma.conf.js --single-run", + "lint": "eslint --no-color meteos_ui/static", + "lintq": "eslint --quiet meteos_ui/static" + }, + "dependencies": {} +} diff --git a/releasenotes/source/_static/.placeholder b/releasenotes/source/_static/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/releasenotes/source/_templates/.placeholder b/releasenotes/source/_templates/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py new file mode 100644 index 0000000..d4c963d --- /dev/null +++ b/releasenotes/source/conf.py @@ -0,0 +1,276 @@ +# -*- coding: utf-8 -*- +# 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. + +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'oslosphinx', + 'reno.sphinxext', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Meteos UI Release Notes' +copyright = u'2016, Meteos UI Developers' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +from meteos_ui.version import version_info as meteos_ui_version +# The full version, including alpha/beta/rc tags. +release = meteos_ui_version.version_string_with_vcs() +# The short X.Y version. +version = meteos_ui_version.canonical_version_string() + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'MeteosUIReleaseNotesDoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'MeteosUIReleaseNotes.tex', + u'Meteos UI Release Notes Documentation', + u'Meteos UI Developers', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'MeteosUIReleaseNotes', + u'Meteos UI Release Notes Documentation', + [u'Meteos UI Developers'], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'MeteosUIReleaseNotes', + u'Meteos UI Release Notes Documentation', + u'Meteos UI Developers', 'MeteosUIReleaseNotes', + 'User Interface for Meteos.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# texinfo_no_detailmenu = False + +# -- Options for Internationalization output ------------------------------ +locale_dirs = ['locale/'] diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst new file mode 100644 index 0000000..e825cdb --- /dev/null +++ b/releasenotes/source/index.rst @@ -0,0 +1,16 @@ +Welcome to Meteos-UI Release Notes documentation! +============================================== + +Contents +======== + +.. toctree:: + :maxdepth: 2 + + unreleased + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst new file mode 100644 index 0000000..875030f --- /dev/null +++ b/releasenotes/source/unreleased.rst @@ -0,0 +1,5 @@ +============================ +Current Series Release Notes +============================ + +.. release-notes:: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..16f8928 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,40 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +# Order matters to the pip dependency resolver, so sorting this file +# changes how packages are installed. New dependencies should be +# added in alphabetical order, however, some dependencies may need to +# be installed in a specific order. +# +# PBR should always appear first +pbr>=1.6 # Apache-2.0 +# If python-meteosclient will be released, we will use it. +#python-meteosclient>=0.0.1 # Apache-2.0 +Babel>=2.3.4 # BSD +Django<1.9,>=1.8 # BSD +django-babel>=0.5.1 # BSD +django-compressor>=2.0 # MIT +django-openstack-auth>=2.2.0 # Apache-2.0 +django-pyscss>=2.0.2 # BSD License (2 clause) +XStatic>=1.0.0 # MIT License +XStatic-Angular>=1.3.7 # MIT License +XStatic-Angular-Bootstrap>=0.11.0.2 # MIT License +XStatic-Angular-Gettext>=2.1.0.2 # MIT License +XStatic-Angular-lrdragndrop>=1.0.2.2 # MIT License +XStatic-Bootstrap-Datepicker>=1.3.1.0 # Apache 2.0 License +XStatic-Bootstrap-SCSS>=3 # Apache 2.0 License +XStatic-D3>=3.1.6.2 # BSD License (3 clause) +XStatic-Hogan>=2.0.0.2 # Apache 2.0 License +XStatic-Font-Awesome>=4.3.0 # SIL OFL 1.1 License, MIT License +XStatic-Jasmine>=2.1.2.0 # MIT License +XStatic-jQuery>=1.7.2 # MIT License +XStatic-JQuery-Migrate>=1.2.1.1 # MIT License +XStatic-JQuery.quicksearch>=2.0.3.1 # MIT License +XStatic-JQuery.TableSorter>=2.14.5.1 # MIT License +XStatic-jquery-ui>=1.10.1 # MIT License +XStatic-JSEncrypt>=2.0.0.2 # MIT License +XStatic-Magic-Search>=0.2.5.1 # Apache 2.0 License +XStatic-Rickshaw>=1.5.0 # BSD License (prior) +XStatic-smart-table>=1.4.5.3 # MIT License +XStatic-Spin>=1.2.5.2 # MIT License +XStatic-term.js>=0.0.4 # MIT License diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..cbb4cb6 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,583 @@ +#!/bin/bash + +set -o errexit + +function usage { + echo "Usage: $0 [OPTION]..." + echo "Run Horizon's test suite(s)" + echo "" + echo " -V, --virtual-env Always use virtualenv. Install automatically" + echo " if not present" + echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local" + echo " environment" + echo " -c, --coverage Generate reports using Coverage" + echo " -f, --force Force a clean re-build of the virtual" + echo " environment. Useful when dependencies have" + echo " been added." + echo " -m, --manage Run a Django management command." + echo " --makemessages Create/Update English translation files." + echo " --compilemessages Compile all translation files." + echo " --check-only Do not update translation files (--makemessages only)." + echo " --pseudo Pseudo translate a language." + echo " -p, --pep8 Just run pep8" + echo " -8, --pep8-changed []" + echo " Just run PEP8 and HACKING compliance check" + echo " on files changed since HEAD~1 (or )" + echo " -P, --no-pep8 Don't run pep8 by default" + echo " -t, --tabs Check for tab characters in files." + echo " -y, --pylint Just run pylint" + echo " -e, --eslint Just run eslint" + echo " -k, --karma Just run karma" + echo " -q, --quiet Run non-interactively. (Relatively) quiet." + echo " Implies -V if -N is not set." + echo " --only-selenium Run only the Selenium unit tests" + echo " --with-selenium Run unit tests including Selenium tests" + echo " --selenium-headless Run Selenium tests headless" + echo " --integration Run the integration tests (requires a running " + echo " OpenStack environment)" + echo " --runserver Run the Django development server for" + echo " openstack_dashboard in the virtual" + echo " environment." + echo " --docs Just build the documentation" + echo " --backup-environment Make a backup of the environment on exit" + echo " --restore-environment Restore the environment before running" + echo " --destroy-environment Destroy the environment and exit" + echo " -h, --help Print this usage message" + echo "" + echo "Note: with no options specified, the script will try to run the tests in" + echo " a virtual environment, If no virtualenv is found, the script will ask" + echo " if you would like to create one. If you prefer to run tests NOT in a" + echo " virtual environment, simply pass the -N option." + exit +} + +# DEFAULTS FOR RUN_TESTS.SH +# +root=`pwd -P` +venv=$root/.venv +venv_env_version=$venv/environments +with_venv=tools/with_venv.sh +included_dirs="meteos_ui" + +always_venv=0 +backup_env=0 +command_wrapper="" +destroy=0 +force=0 +just_pep8=0 +just_pep8_changed=0 +no_pep8=0 +just_pylint=0 +just_docs=0 +just_tabs=0 +just_eslint=0 +just_karma=0 +never_venv=0 +quiet=0 +restore_env=0 +runserver=0 +only_selenium=0 +with_selenium=0 +selenium_headless=0 +integration=0 +testopts="" +testargs="" +with_coverage=0 +makemessages=0 +compilemessages=0 +check_only=0 +pseudo=0 +manage=0 + +# Jenkins sets a "JOB_NAME" variable, if it's not set, we'll make it "default" +[ "$JOB_NAME" ] || JOB_NAME="default" + +function process_option { + # If running manage command, treat the rest of options as arguments. + if [ $manage -eq 1 ]; then + testargs="$testargs $1" + return 0 + fi + + case "$1" in + -h|--help) usage;; + -V|--virtual-env) always_venv=1; never_venv=0;; + -N|--no-virtual-env) always_venv=0; never_venv=1;; + -p|--pep8) just_pep8=1;; + -8|--pep8-changed) just_pep8_changed=1;; + -P|--no-pep8) no_pep8=1;; + -y|--pylint) just_pylint=1;; + -e|--eslint) just_eslint=1;; + -k|--karma) just_karma=1;; + -f|--force) force=1;; + -t|--tabs) just_tabs=1;; + -q|--quiet) quiet=1;; + -c|--coverage) with_coverage=1;; + -m|--manage) manage=1;; + --makemessages) makemessages=1;; + --compilemessages) compilemessages=1;; + --check-only) check_only=1;; + --pseudo) pseudo=1;; + --only-selenium) only_selenium=1;; + --with-selenium) with_selenium=1;; + --selenium-headless) selenium_headless=1;; + --integration) integration=1;; + --docs) just_docs=1;; + --runserver) runserver=1;; + --backup-environment) backup_env=1;; + --restore-environment) restore_env=1;; + --destroy-environment) destroy=1;; + -*) testopts="$testopts $1";; + *) testargs="$testargs $1" + esac +} + +function run_management_command { + ${command_wrapper} python $root/manage.py $testopts $testargs +} + +function run_server { + echo "Starting Django development server..." + ${command_wrapper} python $root/manage.py runserver $testopts $testargs + echo "Server stopped." +} + +function run_pylint { + echo "Running pylint ..." + PYTHONPATH=$root ${command_wrapper} pylint --rcfile=.pylintrc -f parseable $included_dirs > pylint.txt || true + CODE=$? + grep Global -A2 pylint.txt + if [ $CODE -lt 32 ]; then + echo "Completed successfully." + exit 0 + else + echo "Completed with problems." + exit $CODE + fi +} + +function run_eslint { + echo "Running eslint ..." + if [ "`which npm`" == '' ] ; then + echo "npm is not present; please install, e.g. sudo apt-get install npm" + else + npm install + npm run lint + fi +} + +function run_karma { + echo "Running karma ..." + npm install + npm run test +} + +function warn_on_flake8_without_venv { + set +o errexit + ${command_wrapper} python -c "import hacking" 2>/dev/null + no_hacking=$? + set -o errexit + if [ $never_venv -eq 1 -a $no_hacking -eq 1 ]; then + echo "**WARNING**:" >&2 + echo "OpenStack hacking is not installed on your host. Its detection will be missed." >&2 + echo "Please install or use virtual env if you need OpenStack hacking detection." >&2 + fi +} + +function run_pep8 { + echo "Running flake8 ..." + warn_on_flake8_without_venv + DJANGO_SETTINGS_MODULE=meteos_ui.test.settings ${command_wrapper} flake8 +} + +function run_pep8_changed { + # NOTE(gilliard) We want use flake8 to check the entirety of every file that has + # a change in it. Unfortunately the --filenames argument to flake8 only accepts + # file *names* and there are no files named (eg) "nova/compute/manager.py". The + # --diff argument behaves surprisingly as well, because although you feed it a + # diff, it actually checks the file on disk anyway. + local base_commit=${testargs:-HEAD~1} + files=$(git diff --name-only $base_commit | tr '\n' ' ') + echo "Running flake8 on ${files}" + warn_on_flake8_without_venv + diff -u --from-file /dev/null ${files} | DJANGO_SETTINGS_MODULE=meteos_ui.test.settings ${command_wrapper} flake8 --diff + exit +} + +function run_sphinx { + echo "Building sphinx..." + DJANGO_SETTINGS_MODULE=meteos_ui.test.settings ${command_wrapper} python setup.py build_sphinx + echo "Build complete." +} + +function tab_check { + TAB_VIOLATIONS=`find $included_dirs -type f -regex ".*\.\(css\|js\|py\|html\)" -print0 | xargs -0 awk '/\t/' | wc -l` + if [ $TAB_VIOLATIONS -gt 0 ]; then + echo "TABS! $TAB_VIOLATIONS of them! Oh no!" + HORIZON_FILES=`find $included_dirs -type f -regex ".*\.\(css\|js\|py|\html\)"` + for TABBED_FILE in $HORIZON_FILES + do + TAB_COUNT=`awk '/\t/' $TABBED_FILE | wc -l` + if [ $TAB_COUNT -gt 0 ]; then + echo "$TABBED_FILE: $TAB_COUNT" + fi + done + fi + return $TAB_VIOLATIONS; +} + +function destroy_venv { + echo "Cleaning environment..." + echo "Removing virtualenv..." + rm -rf $venv + echo "Virtualenv removed." +} + +function environment_check { + echo "Checking environment." + if [ -f $venv_env_version ]; then + set +o errexit + cat requirements.txt test-requirements.txt | cmp $venv_env_version - > /dev/null + local env_check_result=$? + set -o errexit + if [ $env_check_result -eq 0 ]; then + # If the environment exists and is up-to-date then set our variables + command_wrapper="${root}/${with_venv}" + echo "Environment is up to date." + return 0 + fi + fi + + if [ $always_venv -eq 1 ]; then + install_venv + else + if [ ! -e ${venv} ]; then + echo -e "Environment not found. Install? (Y/n) \c" + else + echo -e "Your environment appears to be out of date. Update? (Y/n) \c" + fi + read update_env + if [ "x$update_env" = "xY" -o "x$update_env" = "x" -o "x$update_env" = "xy" ]; then + install_venv + else + # Set our command wrapper anyway. + command_wrapper="${root}/${with_venv}" + fi + fi +} + +function sanity_check { + # Anything that should be determined prior to running the tests, server, etc. + # Don't sanity-check anything environment-related in -N flag is set + if [ $never_venv -eq 0 ]; then + if [ ! -e ${venv} ]; then + echo "Virtualenv not found at $venv. Did install_venv.py succeed?" + exit 1 + fi + fi + # Remove .pyc files. This is sanity checking because they can linger + # after old files are deleted. + find . -name "*.pyc" -exec rm -rf {} \; +} + +function backup_environment { + if [ $backup_env -eq 1 ]; then + echo "Backing up environment \"$JOB_NAME\"..." + if [ ! -e ${venv} ]; then + echo "Environment not installed. Cannot back up." + return 0 + fi + if [ -d /tmp/.horizon_environment/$JOB_NAME ]; then + mv /tmp/.horizon_environment/$JOB_NAME /tmp/.horizon_environment/$JOB_NAME.old + rm -rf /tmp/.horizon_environment/$JOB_NAME + fi + mkdir -p /tmp/.horizon_environment/$JOB_NAME + cp -r $venv /tmp/.horizon_environment/$JOB_NAME/ + # Remove the backup now that we've completed successfully + rm -rf /tmp/.horizon_environment/$JOB_NAME.old + echo "Backup completed" + fi +} + +function restore_environment { + if [ $restore_env -eq 1 ]; then + echo "Restoring environment from backup..." + if [ ! -d /tmp/.horizon_environment/$JOB_NAME ]; then + echo "No backup to restore from." + return 0 + fi + + cp -r /tmp/.horizon_environment/$JOB_NAME/.venv ./ || true + echo "Environment restored successfully." + fi +} + +function install_venv { + # Install with install_venv.py + export PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE-/tmp/.pip_download_cache} + export PIP_USE_MIRRORS=true + if [ $quiet -eq 1 ]; then + export PIP_NO_INPUT=true + fi + echo "Fetching new src packages..." + rm -rf $venv/src + python tools/install_venv.py + command_wrapper="$root/${with_venv}" + # Make sure it worked and record the environment version + sanity_check + chmod -R 754 $venv + cat requirements.txt test-requirements.txt > $venv_env_version +} + +function run_tests { + sanity_check + + if [ $with_selenium -eq 1 ]; then + export WITH_SELENIUM=1 + elif [ $only_selenium -eq 1 ]; then + export WITH_SELENIUM=1 + export SKIP_UNITTESTS=1 + fi + + if [ $with_selenium -eq 0 -a $integration -eq 0 ]; then + testopts="$testopts --exclude-dir=meteos_ui/test/integration_tests" + fi + + if [ $selenium_headless -eq 1 ]; then + export SELENIUM_HEADLESS=1 + fi + + if [ -z "$testargs" ]; then + run_tests_all + else + run_tests_subset + fi +} + +function run_tests_subset { + project=`echo $testargs | awk -F. '{print $1}'` + ${command_wrapper} python $root/manage.py test --settings=$project.test.settings $testopts $testargs +} + +function run_tests_all { + echo "Running meteos UI tests" + export NOSE_XUNIT_FILE=meteos_ui/nosetests.xml + if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then + export NOSE_HTML_OUT_FILE='dashboard_nose_results.html' + fi + ${command_wrapper} ${coverage_run} $root/manage.py test meteos_ui --settings=openstack_dashboard.test.settings $testopts + # get results of the openstack_dashboard tests + DASHBOARD_RESULT=$? + + if [ $with_coverage -eq 1 ]; then + echo "Generating coverage reports" + ${command_wrapper} python -m coverage.__main__ combine + ${command_wrapper} python -m coverage.__main__ xml -i --include="horizon/*,openstack_dashboard/*" --omit='/usr*,setup.py,*egg*,.venv/*' + ${command_wrapper} python -m coverage.__main__ html -i --include="horizon/*,openstack_dashboard/*" --omit='/usr*,setup.py,*egg*,.venv/*' -d reports + fi + # Remove the leftover coverage files from the -p flag earlier. + rm -f .coverage.* + + PEP8_RESULT=0 + if [ $no_pep8 -eq 0 ] && [ $only_selenium -eq 0 ]; then + run_pep8 + PEP8_RESULT=$? + fi + + TEST_RESULT=$(($DASHBOARD_RESULT || $PEP8_RESULT)) + if [ $TEST_RESULT -eq 0 ]; then + echo "Tests completed successfully." + else + echo "Tests failed." + fi + exit $TEST_RESULT +} + +function run_integration_tests { + export INTEGRATION_TESTS=1 + + if [ $selenium_headless -eq 1 ]; then + export SELENIUM_HEADLESS=1 + fi + + echo "Running Horizon integration tests..." + if [ -z "$testargs" ]; then + ${command_wrapper} nosetests openstack_dashboard/test/integration_tests/tests + else + ${command_wrapper} nosetests $testargs + fi + exit 0 +} + +function babel_extract { + DOMAIN=$1 + KEYWORDS="-k gettext_noop -k gettext_lazy -k ngettext_lazy:1,2" + KEYWORDS+=" -k ugettext_noop -k ugettext_lazy -k ungettext_lazy:1,2" + KEYWORDS+=" -k npgettext:1c,2,3 -k pgettext_lazy:1c,2 -k npgettext_lazy:1c,2,3" + + ${command_wrapper} pybabel extract -F ../babel-${DOMAIN}.cfg -o locale/${DOMAIN}.pot $KEYWORDS . +} + +function run_makemessages { + + echo -n "meteos ui: " + cd meteos_ui + babel_extract django + meteos_PY_RESULT=$? + + echo -n "meteos ui javascript: " + babel_extract djangojs + meteos_JS_RESULT=$? + + cd .. + if [ $check_only -eq 1 ]; then + rm meteos_ui/locale/django*.pot + fi + + exit $(($meteos_PY_RESULT || $meteos_JS_RESULT)) +} + +function run_compilemessages { + cd horizon + ${command_wrapper} $root/manage.py compilemessages + HORIZON_PY_RESULT=$? + cd ../openstack_dashboard + ${command_wrapper} $root/manage.py compilemessages + DASHBOARD_RESULT=$? + exit $(($HORIZON_PY_RESULT || $DASHBOARD_RESULT)) +} + +function run_pseudo { + for lang in $testargs + # Use English pot file as the source file/pot file just like real Horizon translations + do + ${command_wrapper} $root/tools/pseudo.py openstack_dashboard/locale/django.pot openstack_dashboard/locale/$lang/LC_MESSAGES/django.po $lang + ${command_wrapper} $root/tools/pseudo.py openstack_dashboard/locale/djangojs.pot openstack_dashboard/locale/$lang/LC_MESSAGES/djangojs.po $lang + ${command_wrapper} $root/tools/pseudo.py horizon/locale/django.pot horizon/locale/$lang/LC_MESSAGES/django.po $lang + ${command_wrapper} $root/tools/pseudo.py horizon/locale/djangojs.pot horizon/locale/$lang/LC_MESSAGES/djangojs.po $lang + done + exit $? +} + + +# ---------PREPARE THE ENVIRONMENT------------ # + +# PROCESS ARGUMENTS, OVERRIDE DEFAULTS +for arg in "$@"; do + process_option $arg +done + +if [ $quiet -eq 1 ] && [ $never_venv -eq 0 ] && [ $always_venv -eq 0 ] +then + always_venv=1 +fi + +# If destroy is set, just blow it away and exit. +if [ $destroy -eq 1 ]; then + destroy_venv + exit 0 +fi + +# Ignore all of this if the -N flag was set +if [ $never_venv -eq 0 ]; then + + # Restore previous environment if desired + if [ $restore_env -eq 1 ]; then + restore_environment + fi + + # Remove the virtual environment if --force used + if [ $force -eq 1 ]; then + destroy_venv + fi + + # Then check if it's up-to-date + environment_check + + # Create a backup of the up-to-date environment if desired + if [ $backup_env -eq 1 ]; then + backup_environment + fi +fi + +# ---------EXERCISE THE CODE------------ # + +# Run management commands +if [ $manage -eq 1 ]; then + run_management_command + exit $? +fi + +# Build the docs +if [ $just_docs -eq 1 ]; then + run_sphinx + exit $? +fi + +# Update translation files +if [ $makemessages -eq 1 ]; then + run_makemessages + exit $? +fi + +# Compile translation files +if [ $compilemessages -eq 1 ]; then + run_compilemessages + exit $? +fi + +# Generate Pseudo translation +if [ $pseudo -eq 1 ]; then + run_pseudo + exit $? +fi + +# PEP8 +if [ $just_pep8 -eq 1 ]; then + run_pep8 + exit $? +fi + +if [ $just_pep8_changed -eq 1 ]; then + run_pep8_changed + exit $? +fi + +# Pylint +if [ $just_pylint -eq 1 ]; then + run_pylint + exit $? +fi + +# ESLint +if [ $just_eslint -eq 1 ]; then + run_eslint + exit $? +fi + +# Karma +if [ $just_karma -eq 1 ]; then + run_karma + exit $? +fi + +# Tab checker +if [ $just_tabs -eq 1 ]; then + tab_check + exit $? +fi + +# Integration tests +if [ $integration -eq 1 ]; then + run_integration_tests + exit $? +fi + +# Django development server +if [ $runserver -eq 1 ]; then + run_server + exit $? +fi + +# Full test suite +run_tests || exit diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..75756f2 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,28 @@ +[metadata] +name = meteos-ui +summary = Meteos User Interface +description-file = + README.rst +author = OpenStack +author-email = openstack-dev@lists.openstack.org +home-page = https://wiki.openstack.org/wiki/Meteos +classifier = + Environment :: OpenStack + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.4 + +[files] +packages = + meteos_ui + +[build_sphinx] +all_files = 1 +build-dir = doc/build +source-dir = doc/source diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ddd1771 --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +# 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. + +# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT +import setuptools + +# In python < 2.7.4, a lazy loading of package `pbr` will break +# setuptools if some other modules registered functions in `atexit`. +# solution from: http://bugs.python.org/issue15881#msg170215 +try: + import multiprocessing # noqa +except ImportError: + pass + +setuptools.setup( + setup_requires=['pbr>=1.8'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..610eb97 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,30 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. +# Order matters to the pip dependency resolver, so sorting this file +# changes how packages are installed. New dependencies should be +# added in alphabetical order, however, some dependencies may need to +# be installed in a specific order. +# +# Hacking should appear first in case something else depends on pep8 +hacking<0.11,>=0.10.2 +# +coverage>=3.6 # Apache-2.0 +django-nose>=1.2 # BSD +mock>=2.0 # BSD +mox3>=0.7.0 # Apache-2.0 +nodeenv>=0.9.4 # BSD License # BSD +nose # LGPL +nose-exclude # LGPL +nosehtmloutput>=0.0.3 # Apache-2.0 +nosexcover # BSD +openstack.nose-plugin>=0.7 # Apache-2.0 +oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 +reno>=1.8.0 # Apache2 +selenium>=2.50.1 # Apache-2.0 +sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD +testtools>=1.4.0 # MIT +# This also needs xvfb library installed on your OS +xvfbwrapper>=0.1.3 #license: MIT +# Include horizon as test requirement +http://tarballs.openstack.org/horizon/horizon-master.tar.gz#egg=horizon diff --git a/test-shim.js b/test-shim.js new file mode 100644 index 0000000..5b364ba --- /dev/null +++ b/test-shim.js @@ -0,0 +1,96 @@ +/* + * Shim for Javascript unit tests; supplying expected global features. + * This should be removed from the codebase once i18n services are provided. + * Taken from default i18n file provided by Django. + */ + +var horizonPlugInModules = []; + + +(function (globals) { + + var django = globals.django || (globals.django = {}); + + + django.pluralidx = function (count) { return (count == 1) ? 0 : 1; }; + + /* gettext identity library */ + + django.gettext = function (msgid) { return msgid; }; + django.ngettext = function (singular, plural, count) { return (count == 1) ? singular : plural; }; + django.gettext_noop = function (msgid) { return msgid; }; + django.pgettext = function (context, msgid) { return msgid; }; + django.npgettext = function (context, singular, plural, count) { return (count == 1) ? singular : plural; }; + + + django.interpolate = function (fmt, obj, named) { + if (named) { + return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])}); + } else { + return fmt.replace(/%s/g, function(match){return String(obj.shift())}); + } + }; + + + /* formatting library */ + + django.formats = { + "DATETIME_FORMAT": "N j, Y, P", + "DATETIME_INPUT_FORMATS": [ + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d %H:%M:%S.%f", + "%Y-%m-%d %H:%M", + "%Y-%m-%d", + "%m/%d/%Y %H:%M:%S", + "%m/%d/%Y %H:%M:%S.%f", + "%m/%d/%Y %H:%M", + "%m/%d/%Y", + "%m/%d/%y %H:%M:%S", + "%m/%d/%y %H:%M:%S.%f", + "%m/%d/%y %H:%M", + "%m/%d/%y" + ], + "DATE_FORMAT": "N j, Y", + "DATE_INPUT_FORMATS": [ + "%Y-%m-%d", + "%m/%d/%Y", + "%m/%d/%y" + ], + "DECIMAL_SEPARATOR": ".", + "FIRST_DAY_OF_WEEK": "0", + "MONTH_DAY_FORMAT": "F j", + "NUMBER_GROUPING": "3", + "SHORT_DATETIME_FORMAT": "m/d/Y P", + "SHORT_DATE_FORMAT": "m/d/Y", + "THOUSAND_SEPARATOR": ",", + "TIME_FORMAT": "P", + "TIME_INPUT_FORMATS": [ + "%H:%M:%S", + "%H:%M:%S.%f", + "%H:%M" + ], + "YEAR_MONTH_FORMAT": "F Y" + }; + + django.get_format = function (format_type) { + var value = django.formats[format_type]; + if (typeof(value) == 'undefined') { + return format_type; + } else { + return value; + } + }; + + /* add to global namespace */ + globals.pluralidx = django.pluralidx; + globals.gettext = django.gettext; + globals.ngettext = django.ngettext; + globals.gettext_noop = django.gettext_noop; + globals.pgettext = django.pgettext; + globals.npgettext = django.npgettext; + globals.interpolate = django.interpolate; + globals.get_format = django.get_format; + globals.STATIC_URL = '/static/'; + globals.WEBROOT = '/'; + +}(this)); diff --git a/tools/install_venv.py b/tools/install_venv.py new file mode 100644 index 0000000..e96521e --- /dev/null +++ b/tools/install_venv.py @@ -0,0 +1,71 @@ +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Copyright 2010 OpenStack Foundation +# Copyright 2013 IBM Corp. +# +# 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. + +import os +import sys + +import install_venv_common as install_venv # noqa + + +def print_help(venv, root): + help = """ + OpenStack development environment setup is complete. + + OpenStack development uses virtualenv to track and manage Python + dependencies while in development and testing. + + To activate the OpenStack virtualenv for the extent of your current shell + session you can run: + + $ source %s/bin/activate + + Or, if you prefer, you can run commands in the virtualenv on a case by case + basis by running: + + $ %s/tools/with_venv.sh + + Also, make test will automatically use the virtualenv. + """ + print(help % (venv, root)) + + +def main(argv): + root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + + if os.environ.get('tools_path'): + root = os.environ['tools_path'] + venv = os.path.join(root, '.venv') + if os.environ.get('venv'): + venv = os.environ['venv'] + + pip_requires = os.path.join(root, 'requirements.txt') + test_requires = os.path.join(root, 'test-requirements.txt') + py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) + project = 'OpenStack' + install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, + py_version, project) + options = install.parse_args(argv) + install.check_python_version() + install.check_dependencies() + install.create_virtualenv(no_site_packages=options.no_site_packages) + install.install_dependencies() + print_help(venv, root) + +if __name__ == '__main__': + main(sys.argv) diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py new file mode 100644 index 0000000..e279159 --- /dev/null +++ b/tools/install_venv_common.py @@ -0,0 +1,172 @@ +# Copyright 2013 OpenStack Foundation +# Copyright 2013 IBM Corp. +# +# 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. + +"""Provides methods needed by installation script for OpenStack development +virtual environments. + +Since this script is used to bootstrap a virtualenv from the system's Python +environment, it should be kept strictly compatible with Python 2.6. + +Synced in from openstack-common +""" + +from __future__ import print_function + +import optparse +import os +import subprocess +import sys + + +class InstallVenv(object): + + def __init__(self, root, venv, requirements, + test_requirements, py_version, + project): + self.root = root + self.venv = venv + self.requirements = requirements + self.test_requirements = test_requirements + self.py_version = py_version + self.project = project + + def die(self, message, *args): + print(message % args, file=sys.stderr) + sys.exit(1) + + def check_python_version(self): + if sys.version_info < (2, 6): + self.die("Need Python Version >= 2.6") + + def run_command_with_code(self, cmd, redirect_output=True, + check_exit_code=True): + """Runs a command in an out-of-process shell. + + Returns the output of that command. Working directory is self.root. + """ + if redirect_output: + stdout = subprocess.PIPE + else: + stdout = None + + proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) + output = proc.communicate()[0] + if check_exit_code and proc.returncode != 0: + self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) + return (output, proc.returncode) + + def run_command(self, cmd, redirect_output=True, check_exit_code=True): + return self.run_command_with_code(cmd, redirect_output, + check_exit_code)[0] + + def get_distro(self): + if (os.path.exists('/etc/fedora-release') or + os.path.exists('/etc/redhat-release')): + return Fedora( + self.root, self.venv, self.requirements, + self.test_requirements, self.py_version, self.project) + else: + return Distro( + self.root, self.venv, self.requirements, + self.test_requirements, self.py_version, self.project) + + def check_dependencies(self): + self.get_distro().install_virtualenv() + + def create_virtualenv(self, no_site_packages=True): + """Creates the virtual environment and installs PIP. + + Creates the virtual environment and installs PIP only into the + virtual environment. + """ + if not os.path.isdir(self.venv): + print('Creating venv...', end=' ') + if no_site_packages: + self.run_command(['virtualenv', '-q', '--no-site-packages', + self.venv]) + else: + self.run_command(['virtualenv', '-q', self.venv]) + print('done.') + else: + print("venv already exists...") + pass + + def pip_install(self, *args): + self.run_command(['tools/with_venv.sh', + 'pip', 'install', '--upgrade'] + list(args), + redirect_output=False) + + def install_dependencies(self): + print('Installing dependencies with pip (this can take a while)...') + + # First things first, make sure our venv has the latest pip and + # setuptools and pbr + self.pip_install('pip>=1.4') + self.pip_install('setuptools') + self.pip_install('pbr') + + self.pip_install('-r', self.requirements, '-r', self.test_requirements) + + def parse_args(self, argv): + """Parses command-line arguments.""" + parser = optparse.OptionParser() + parser.add_option('-n', '--no-site-packages', + action='store_true', + help="Do not inherit packages from global Python " + "install.") + return parser.parse_args(argv[1:])[0] + + +class Distro(InstallVenv): + + def check_cmd(self, cmd): + return bool(self.run_command(['which', cmd], + check_exit_code=False).strip()) + + def install_virtualenv(self): + if self.check_cmd('virtualenv'): + return + + if self.check_cmd('easy_install'): + print('Installing virtualenv via easy_install...', end=' ') + if self.run_command(['easy_install', 'virtualenv']): + print('Succeeded') + return + else: + print('Failed') + + self.die('ERROR: virtualenv not found.\n\n%s development' + ' requires virtualenv, please install it using your' + ' favorite package management tool' % self.project) + + +class Fedora(Distro): + """This covers all Fedora-based distributions. + + Includes: Fedora, RHEL, CentOS, Scientific Linux + """ + + def check_pkg(self, pkg): + return self.run_command_with_code(['rpm', '-q', pkg], + check_exit_code=False)[1] == 0 + + def install_virtualenv(self): + if self.check_cmd('virtualenv'): + return + + if not self.check_pkg('python-virtualenv'): + self.die("Please install 'python-virtualenv'.") + + super(Fedora, self).install_virtualenv() diff --git a/tools/with_venv.sh b/tools/with_venv.sh new file mode 100755 index 0000000..7303990 --- /dev/null +++ b/tools/with_venv.sh @@ -0,0 +1,7 @@ +#!/bin/bash +TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)} +VENV_PATH=${VENV_PATH:-${TOOLS_PATH}} +VENV_DIR=${VENV_NAME:-/../.venv} +TOOLS=${TOOLS_PATH} +VENV=${VENV:-${VENV_PATH}/${VENV_DIR}} +source ${VENV}/bin/activate && "$@" diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..40af987 --- /dev/null +++ b/tox.ini @@ -0,0 +1,74 @@ +[tox] +envlist = py34,py27,py27dj18,pep8 +minversion = 2.0 +skipsdist = True + +[testenv] +usedevelop = True +setenv = VIRTUAL_ENV={envdir} + NOSE_WITH_OPENSTACK=1 + NOSE_OPENSTACK_COLOR=1 + NOSE_OPENSTACK_RED=0.05 + NOSE_OPENSTACK_YELLOW=0.025 + NOSE_OPENSTACK_SHOW_ELAPSED=1 + PYTHONWARNINGS=default::DeprecationWarning +install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -U {opts} {packages} +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = python manage.py test {posargs} --settings=meteos_ui.test.settings + +# Until python-meteosclient released, install from github temporary. +[testenv:py27] +basepython = python2.7 +commands = + pip install git+https://github.com/openstack/python-meteosclient.git + python manage.py test {posargs} --settings=meteos_ui.test.settings + +[testenv:pep8] +commands = flake8 {posargs} + +[testenv:venv] +commands = {posargs} + +[testenv:cover] +commands = python setup.py test --coverage --testr-args='{posargs}' + +[testenv:py27dj18] +basepython = python2.7 +commands = + pip install django>=1.8,<1.9 + python manage.py test {posargs} --settings=meteos_ui.test.settings + +[testenv:docs] +setenv = DJANGO_SETTINGS_MODULE=meteos_ui.test.settings +commands = python setup.py build_sphinx + +[testenv:releasenotes] +commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html + +[flake8] +exclude = .venv,.git,.tox,dist,*lib/python*,*egg,build,panel_template,dash_template,local_settings.py,*/local/*,*/test/test_plugins/*,.ropeproject +max-complexity = 20 + +[hacking] +import_exceptions = collections.defaultdict, + django.conf.settings, + django.conf.urls.include, + django.conf.urls.patterns, + django.conf.urls.url, + django.core.urlresolvers.reverse, + django.core.urlresolvers.reverse_lazy, + django.template.loader.render_to_string, + django.test.utils.override_settings, + django.utils.datastructures.SortedDict, + django.utils.encoding.force_text, + django.utils.html.conditional_escape, + django.utils.html.escape, + django.utils.http.urlencode, + django.utils.safestring.mark_safe, + django.utils.translation.npgettext_lazy, + django.utils.translation.pgettext_lazy, + django.utils.translation.ugettext_lazy, + django.utils.translation.ungettext_lazy, + operator.attrgetter, + StringIO.StringIO