Source code for seamm.dashboard_handler

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
The interface for submitting SEAMM jobs.

A job in SEAMM is composed of a flowchart and any other files that the
flowchart requires. This module provides the JobHandler class, which
provides a use interface and the machinery to gather the necessary
files and submit the job to a dashboard.
"""

import configparser
import logging
from pathlib import Path
import pkg_resources

from .seammrc import SEAMMrc
import seamm_dashboard_client
import seamm_util

logger = logging.getLogger(__name__)


[docs] def safe_filename(filename): if filename[0] == "~": path = Path(filename).expanduser() else: path = Path(filename) if path.anchor == "": result = "_".join(path.parts) else: result = "_".join(path.parts[1:]) return "job:data/" + result
[docs] class DashboardHandler(object): def __init__(self, user_agent=None): """Setup the handler for Dashboards. Parameters ---------- """ self.config = configparser.ConfigParser() if user_agent is None: import seamm self.user_agent = f"SEAMM/{seamm.__version__}" else: self.user_agent = user_agent self._credentials = SEAMMrc() self._current_dashboard = None self.resource_path = Path(pkg_resources.resource_filename(__name__, "data/")) # Get the location of the dashboards configuration file parser = seamm_util.seamm_parser() options = parser.get_options() if "dashboards" in options: self.configfile = Path(options["dashboards"]).expanduser() else: self.configfile = Path.home() / "SEAMM" / "dashboards.ini" # Create the file if it doesn't exist if not self.configfile.exists(): self.configfile.parent.mkdir(parents=True, exist_ok=True) path = self.resource_path / "dashboards.ini" text = path.read_text() self.configfile.write_text(text) # Read in the dashboard information if present self.get_configuration() self.current_dashboard = self.config.get( "GENERAL", "current_dashboard", fallback=None ) self.timeout = self.config.get("GENERAL", "timeout", fallback=1) @property def credentials(self): """The data Dashboard from ~/.seamm.d/seammrc.""" return self._credentials @property def current_dashboard(self): "The currently selected dashboard" if self._current_dashboard is None: dashboards = self.dashboards dashboard = self.config.get( "GENERAL", "current_dashboard", fallback=dashboards[0] ) if dashboard not in dashboards: dashboard = dashboards[0] self._current_dashboard = self.get_dashboard(dashboard) self.save_configuration() return self._current_dashboard @current_dashboard.setter def current_dashboard(self, dashboard): if isinstance(dashboard, str): self._current_dashboard = self.get_dashboard(dashboard) else: if dashboard.name in self.dashboards: self._current_dashboard = dashboard else: raise ValueError(f"Dashboard {dashboard.name} does not exist!") @property def dashboards(self): """The list of dashboards.""" result = [] for section in self.config.sections(): if section not in ("GENERAL", self.config.default_section): result.append(section) return sorted(result)
[docs] def add_dashboard(self, name, url, protocol): "Add a new dashboard to the config file" self.config[name] = {"url": url, "protocol": protocol} self.save_configuration()
[docs] def get_all_status(self): """Get the status of all the dashboards. Parameters ---------- """ result = [] for dashboard in self.dashboards: status = self.get_dashboard(dashboard).status() result.append((dashboard, status)) return result
[docs] def get_configuration(self): """Get the list of dashboards from the config file.""" # The path to the configfile if self.configfile.exists(): self.config.read(self.configfile) else: self.config.clear()
[docs] def get_credentials(self, dashboard, ask=None): """The user for the dashboard. Parameters ---------- dashboard : str The name of the dashboard to use. Returns ------- str, str The user name and password ask : function A function or method to call to get the user name and passwd. """ self.credentials.re_read() user = None password = None section = f"Dashboard: {dashboard}" if section not in self.credentials: self.credentials.add_section(section) user = self.credentials.get(section, "user", fallback=None) password = self.credentials.get(section, "password", fallback=None) if ask is not None and (user is None or password is None): user, password = ask(dashboard, user=user, password=password) if user is not None and password is not None: self.credentials.set(section, "user", user) self.credentials.set(section, "password", password) return user, password
[docs] def get_dashboard(self, name): """Get the given dashboard object. Parameters ---------- name : str The name of the Dashboard Returns ------- seamm_dashboard_client.Dashboard : The Dashboard client object. """ user, passwd = self.get_credentials(name) url = self.config[name]["url"] return seamm_dashboard_client.Dashboard( name, url, username=user, password=passwd, user_agent=self.user_agent )
[docs] def rename_dashboard(self, old, new): "Rename a dashboard from 'old' to 'new'." tmp = {} for key, value in self.config[old].items(): tmp[key] = value self.config.remove_section(old) self.config[new] = tmp
[docs] def save_configuration(self): """Save the list of dashboards to disk.""" # Make sure the directory exists self.configfile.parent.mkdir(exist_ok=True) # Update the current dashboard if self.current_dashboard is not None: if "GENERAL" not in self.config: self.config["GENERAL"] = {} defaults = self.config["GENERAL"] defaults["current_dashboard"] = self.current_dashboard.name with self.configfile.open("w") as fd: self.config.write(fd)
[docs] def update(self, dashboard): "Update the dashboard with that given." self.config[dashboard.name] = { "url": dashboard.url, "protocol": "http", } self.save_configuration()