# Given an array arr, replace every element in that array with the greatest element among the elements to its right, and replace the last element with -1.

def replaceElements(arr):
    maxOfRight = -1
    for i in reversed(range(len(arr))):
        arr[i], maxOfRight = maxOfRight, max(maxOfRight, arr[i])
    return arr

test1 = [400]
print(replaceElements(test1))

test2 = [17,18,5,4,6,1]
print(replaceElements(test2)) #[18,6,6,6,1,-1]


import pandas as pd
import numpy as np
import os, sys
from tqdm import tqdm
from treelib import Tree
import math
import random
import copy
import seaborn as sns
from matplotlib import pyplot as plt
import glob
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)

def read_swc(path):
    data = pd.read_csv(path, sep=' ', header=None, comment='#')
    data.columns = ['id', 'type', 'x', 'y', 'z', 'radius', 'parent']
    data.index = np.arange(1, len(data)+1)
    return data

def swc_preprocess(path, save_path=None, save=False, check_validity=True, flip=True, dimension=[13200, 8000, 11400]):
    data = read_swc(path)

    if flip==True and float(data.loc[1, 'z'])>(11400/2):
        data.z = dimension[2] - data.z

    if check_validity==True:
        if len(data.loc[data.parent==-1])>1:
            print(path + ': more than one soma detected -> parent=-1.')
            child_list = list(data.loc[data.parent==-1].index[1:])
            while len(child_list)>0:
                data = data.loc[~data.index.isin(child_list), :]
                child_list = list(data[data.parent.isin(child_list)].index)

        elif len(data.loc[data.type==1])>1:
            print(path + ': more than one soma detected -> type=1.')
            data.loc[data[data.type==1].index[1:],'type'] = 0

    if data.x.max()>dimension[0]:
        data.x = [item if item<dimension[0] else dimension[0]-1 for item in data.x]
        print('X axis exceeds boundary.')
    if data.y.max()>dimension[1]:
        data.y = [item if item<dimension[1] else dimension[1]-1 for item in data.y]
        print('Y axis exceeds boundary.')
    if data.z.max()>dimension[2]:
        data.z = [item if item<dimension[2] else dimension[2]-1 for item in data.z]
        print('Z axis exceeds boundary.')

    if save==True:
        data.to_csv(save_path, sep=" ", header=None, index=None)
    return data

def swc_tree(path):
    tree = Tree()
    data = read_swc(path)
    for _, row in data.iterrows():
        tree.create_node(
            tag=int(row.id),
            identifier=int(row.id),
            data=row.loc[['x', 'y', 'z']].to_dict(),
            parent=row.parent if row.parent != -1 else None
        )
    return tree

def total_length(path):
    length = 0
    data = read_swc(path)
    for idx in data.index[1:]:
        parent_idx = data.loc[idx, 'parent']
        _is_axon = data.loc[idx, 'type']
        _parent_is_axon = data.loc[parent_idx, 'type']
        if _is_axon not in [3, 4] and _parent_is_axon not in [3, 4]:
            length += math.dist(data.loc[idx, 'x':'z'], data.loc[parent_idx, 'x':'z'])
    return length

def find_longest_stem(path):
    tree = swc_tree(path)
    weight_dict = {}
    for node in list(tree.nodes.keys())[1:]:
        weight_dict[node] = math.dist(tree.nodes[node].data.values(), tree.parent(node).data.values())
    paths = tree.paths_to_leaves()
    stem_list = paths[0].copy()
    mark = np.sum([weight_dict[item] for item in stem_list[1:]])
    for _path in paths:
        path_length = np.sum([weight_dict[item] for item in _path[1:]])
        if path_length > mark:
            stem_list = _path.copy()
            mark = path_length
    data = read_swc(path)
    return data.loc[stem_list, :]

def find_skeleton_tree(path):
    tree = swc_tree(path=path)
    # node_list = list(tree.nodes.keys())
    root = tree.root
    skeleton_tree = copy.deepcopy(tree)
    for node in tree.nodes.keys():
        if node != root and len(tree.children(node)) == 1:
            skeleton_tree.link_past_node(node)
    return skeleton_tree

def find_path_to_parent(node_id, parent_id, tree):
    par = node_id
    path = [node_id]
    while par != parent_id:
        par = tree.parent(par).identifier
        path.append(par)
    path.reverse()
    return path

def downsample(path, eps=1, save_path=None):
    from rdp import rdp
    tree = swc_tree(path)
    data = read_swc(path)
    skeleton_tree = find_skeleton_tree(path)
    new_data = pd.DataFrame(columns=data.columns)
    # add root
    ref_dict = {}
    ref_dict[data.iloc[0, 0]] = 1
    new_data = new_data.append(data.iloc[0,:])

    for node in list(skeleton_tree.nodes.keys())[1:]:
        parent = skeleton_tree.parent(node).tag
        # add parent
        if parent not in list(ref_dict.keys()):
            ref_dict[parent] = new_data.id.max()+1
        if ref_dict[parent] not in list(new_data.id):
            new_data = new_data.append({
                'id':ref_dict[parent],
                'x':data.loc[parent, 'x'],
                'y':data.loc[parent, 'y'],
                'z':data.loc[parent, 'z'],
                'radius': data.loc[parent, 'radius'],
                'parent': data.loc[parent, 'parent']
                }, ignore_index=True)

        # downsample branch points
        branch_path = find_path_to_parent(node_id=node, parent_id=parent, tree=tree)
        bp = data.loc[branch_path[1:-1], :]
        rdp_coords = rdp(bp[['x', 'y', 'z']], epsilon=eps)
        if len(rdp_coords) > 0:
            new_bp = pd.DataFrame(columns=data.columns)
            new_bp[['x','y','z']] = rdp_coords
            new_bp.id = np.arange(new_data.id.max()+1, new_data.id.max()+1+len(rdp_coords), 1)
            new_bp.iloc[1:, 6] = list(new_bp.id)[:-1]
            new_bp.iloc[0, 6] = ref_dict[parent] # parent

        # add end node
            new_bp = new_bp.append(data.loc[node, :])
            # end node id
            new_bp.iloc[-1, 0] = new_bp.iloc[-2, 0]+1
            ref_dict[node] = new_bp.iloc[-1, 0]
            # edn node parent
            new_bp.iloc[-1, 6] = new_bp.iloc[-2, 0]

            new_data = pd.concat([new_data, new_bp], axis=0)
        else:
            ref_dict[node] = new_data.id.max()+1
            new_data = new_data.append({
                'id': ref_dict[node],
                'type': data.loc[node, 'type'],
                'x': data.loc[node, 'x'],
                'y': data.loc[node, 'y'],
                'z': data.loc[node, 'z'],
                'radius': data.loc[node, 'radius'],
                'parent': ref_dict[parent]
                }, ignore_index=True)
    new_data = new_data.fillna(0)
    new_data.index = list(new_data.id)

    if save_path != None:
        new_data.to_csv(save_path, sep=" ", header=None, index=None)
    return new_data

def read_neuron_path(data_path):
    path_list = glob.glob(os.path.join(data_path, '**/*.swc'), recursive=True)
    return path_list

def plot_soma_distribution(data_path, **kwargs):
    path_list = read_neuron_path(data_path)
    soma_info = pd.DataFrame()
    for path in tqdm(path_list):
        data = read_swc(path)
        # soma_info = soma_info.append(data.loc[1, 'x':'z'])
        soma_info = pd.concat([soma_info, data.loc[1, 'x':'z']], axis=0)
    fig, _ = plt.subplots(nrows=3, sharex=False, sharey=False)
    cnt = 1
    for axis in ['x', 'y', 'z']:
        plt.subplot(310+cnt)
        sns.kdeplot(list(soma_info[axis]), **kwargs)
        plt.ylabel('')
        ax = plt.gca()
        if axis == 'x':
            ax.set_title('Anterior - Posterior')
        elif axis == 'y':
            ax.set_title('Dorsal - Ventral')
        else:
            ax.set_title('Medial - Lateral')
        cnt += 1
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
    plt.subplots_adjust(hspace=1)
    plt.subplots_adjust(wspace=0)
    return fig


import sqlite3, logging, sys

from flask import Flask, jsonify, json, render_template, request, url_for, redirect, flash, Response
from werkzeug.exceptions import abort

# Function to get a database connection.
# This function connects to database with the name `database.db`
def get_db_connection():
    connection = sqlite3.connect('database.db')
    connection.row_factory = sqlite3.Row
    return connection

# Function to get a post using its ID
def get_post(post_id):
    connection = get_db_connection()
    post = connection.execute('SELECT * FROM posts WHERE id = ?',
                        (post_id,)).fetchone()
    connection.close()
    return post

# Function to get total posts
def get_total_posts():
    connection = get_db_connection()
    total_posts = connection.execute('SELECT count(*) FROM posts').fetchone()
    connection.close()
    return total_posts[0]

# Function to get total db connections
def set_new_db_connection():
    connection = get_db_connection()
    connection.execute('UPDATE db_transactions set tx_count = tx_count + 1 WHERE id = 1')
    connection.commit()
    connection.close()

# Function to get total db connections
def get_total_db_connections():
    connection = get_db_connection()
    total_connections = connection.execute('SELECT tx_count FROM db_transactions where id = 1').fetchone()
    connection.close()
    return total_connections[0]

# Define the Flask application
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your secret key'

# Define the main route of the web application
@app.route('/')
def index():
    connection = get_db_connection()
    posts = connection.execute('SELECT * FROM posts').fetchall()
    connection.close()
    return render_template('index.html', posts=posts)

# Define how each individual article is rendered
# If the post ID is not found a 404 page is shown
@app.route('/<int:post_id>')
def post(post_id):
    post = get_post(post_id)
    if post is None:
      app.logger.info('Post does not exist')
      return render_template('404.html'), 404
    else:
      app.logger.info("Article " + post['title'] + " retrieved!")
      set_new_db_connection()
      return render_template('post.html', post=post)

# Define the About Us page
@app.route('/about')
def about():
    app.logger.info('About us page was reached')
    return render_template('about.html')

# Define the Healthz page
@app.route("/healthz")
def healthz():
    app.logger.info('Healthz endpoint was reached')
    return Response(json.dumps({"result": "OK healthy"}), status = 200, mimetype='application/json')

# Define the Metrics page
@app.route("/metrics")
def metrics():
    total_posts = get_total_posts()
    total_connections = get_total_db_connections()
    app.logger.info('Metrics endpoint was reached')
    return Response(json.dumps({"status":"success","code":0, "data":{"db_connection_count": total_connections,"post_count": total_posts}}), status=200, mimetype='application/json')

# Define the post creation functionality
@app.route('/create', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']

        if not title:
            flash('Title is required!')
        else:
            connection = get_db_connection()
            connection.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
                         (title, content))
            connection.commit()
            connection.close()
            set_new_db_connection()
            app.logger.info('A new post was created. The title is ' + title)
            return redirect(url_for('index'))

    return render_template('create.html')

# set logger to handle STDOUT and STDERR
stdout_handler = logging.StreamHandler(sys.stdout)
stderr_handler = logging.StreamHandler(sys.stderr)
handlers = [stderr_handler, stdout_handler]

# format output
format_output = '%(levelname)s:%(name)s %(asctime)s : %(message)s'

logging.basicConfig(format=format_output, level=logging.DEBUG, handlers=handlers)

# start the application on port 3111
if __name__ == "__main__":
   app.run(host='0.0.0.0', port='3111', debug=True)


"""Class for loading in modules to OpsDroid."""

# pylint: disable=too-many-branches

import contextlib
import importlib
import importlib.util
import json
import logging
import os
import shutil
import subprocess
import sys
import tempfile
import urllib.request
from collections.abc import Mapping

from pkg_resources import iter_entry_points

from opsdroid.configuration import validate_configuration
from opsdroid.const import (
    DEFAULT_GIT_URL,
    DEFAULT_MODULE_BRANCH,
    DEFAULT_MODULE_DEPS_PATH,
    DEFAULT_MODULES_PATH,
    MODULES_DIRECTORY,
)
from opsdroid.helper import (
    convert_ipynb_to_script,
    extract_gist_id,
    file_is_ipython_notebook,
)

_LOGGER = logging.getLogger(__name__)

class Loader:
    """Class to load in config and modules."""

    def __init__(self, opsdroid):
        """Create object with opsdroid instance."""
        self.opsdroid = opsdroid
        self.modules_directory = None
        self.current_import_config = None
        _LOGGER.debug(_("Loaded loader."))

    @staticmethod
    def import_module_from_spec(module_spec):
        """Import from a given module spec and return imported module.

        Args:
            module_spec: ModuleSpec object containing name, loader, origin,
                submodule_search_locations, cached, and parent

        Returns:
            Module: Module imported from spec

        """
        module = importlib.util.module_from_spec(module_spec)
        module_spec.loader.exec_module(module)
        return module

    @staticmethod
    def import_module(config):
        """Import module namespace as variable and return it.

        Args:
            config: dict of config information related to the module

        Returns:
            Module: Module imported from config

        """
        # Try to import the module from various locations, return the first
        # successful import, or None if they all failed
        #
        # 1. check for entry point for installed module
        # 2. try to import the module directly off PYTHONPATH
        # 3. try to import a module with the given name in the module_path
        # 4. try to import the module_path itself

        if config.get("entrypoint"):
            _LOGGER.debug(
                _("Loading entry point-defined module for %s."), config["name"]
            )
            return config["entrypoint"].load()

        module_spec = None
        namespaces = [
            config["module"],
            config["module_path"] + "." + config["name"],
            config["module_path"],
        ]

        for namespace in namespaces:
            try:
                module_spec = importlib.util.find_spec(namespace)
                if module_spec:
                    break
            except (ImportError, AttributeError, ValueError) as e:
                _LOGGER.debug(
                    _(
                        f"Unable to import {namespace} from {namespaces} - Reason: {str(e)}"
                    )
                )
                continue

        if module_spec:
            try:
                module = Loader.import_module_from_spec(module_spec)
            except Exception as e:
                _LOGGER.error(
                    _("The following exception was raised while importing %s %s"),
                    config["type"],
                    config["module_path"],
                )
                _LOGGER.error(str(e))
            else:
                _LOGGER.debug(
                    _("Loaded %s: %s."), config["type"], config["module_path"]
                )
                return module

        _LOGGER.error(
            _("Failed to load %s: %s."), config["type"], config["module_path"]
        )
        return None

    @classmethod
    def check_cache(cls, config):
        """Remove module if 'no-cache' set in config.

        Args:
            config: dict of config information related to the module

        """
        if "no-cache" in config and config["no-cache"]:
            _LOGGER.debug(_("'no-cache' set, removing %s."), config["install_path"])
            cls.remove_cache(config)

        if "no-cache" not in config and cls._is_local_module(config):
            _LOGGER.debug(
                _(
                    "Removing cache for local module %s, set 'no-cache: false' to disable this."
                ),
                config["install_path"],
            )
            cls.remove_cache(config)

    @staticmethod
    def remove_cache(config):
        """Remove module cache.

        Args:
            config: dict of config information related to the module

        """
        if os.path.isdir(config["install_path"]):
            shutil.rmtree(config["install_path"])
        if os.path.isfile(config["install_path"] + ".py"):
            os.remove(config["install_path"] + ".py")

    @staticmethod
    def is_builtin_module(config):
        """Check if a module is a builtin.

        Args:
            config: dict of config information related to the module

        Returns:
            bool: False if the module is not builtin

        """
        try:
            return importlib.util.find_spec(
                "opsdroid.{module_type}.{module_name}".format(
                    module_type=config["type"], module_name=config["name"].lower()
                )
            )
        except ImportError:
            return False

    @staticmethod
    def build_module_import_path(config):
        """Generate the module import path from name and type.

        Args:
            config: dict of config information related to the module

        Returns:
            string: module import path

        """
        if config["is_builtin"]:
            return "opsdroid" + "." + config["type"] + "." + config["name"].lower()
        return MODULES_DIRECTORY + "." + config["type"] + "." + config["name"]

    def build_module_install_path(self, config):
        """Generate the module install path from name and type.

        Args:
            self: instance method
            config: dict of config information related to the module

        Returns:
            string: module install directory

        """
        return os.path.join(self.modules_directory, config["type"], config["name"])

    @staticmethod
    def git_clone(git_url, install_path, branch, key_path=None):
        """Clone a git repo to a location and wait for finish.

        Args:
            git_url: The url to the git repository
            install_path: Location where the git repository will be cloned
            branch: The branch to be cloned
            key_path: SSH Key for git repository

        """
        git_env = os.environ.copy()
        if key_path:
            git_env[
                "GIT_SSH_COMMAND"
            ] = f"ssh -i {key_path} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

        process = subprocess.Popen(
            ["git", "clone", "-b", branch, git_url, install_path],
            shell=False,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=git_env,
        )
        Loader._communicate_process(process)

    @staticmethod
    def git_pull(repository_path):
        """Pull the current branch of git repo forcing fast forward.

        Args:
            repository_path: Path to the module's local repository

        """
        process = subprocess.Popen(
            ["git", "-C", repository_path, "pull", "--ff-only"],
            shell=False,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        Loader._communicate_process(process)

    @staticmethod
    def pip_install_deps(requirements_path):
        """Pip install a requirements.txt file and wait for finish.

        Args:
            requirements_path: string holding the path to the requirements.txt
                file located in the module's local repository

        Returns:
            bool: True if the requirements.txt installs successfully

        """
        process = None
        command = [
            "pip",
            "install",
            "--target={}".format(DEFAULT_MODULE_DEPS_PATH),
            "--ignore-installed",
            "-r",
            requirements_path,
        ]

        try:
            process = subprocess.Popen(
                command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )

        except FileNotFoundError:
            _LOGGER.debug(
                _("Couldn't find the command 'pip', trying again with command 'pip3'.")
            )

        try:
            command[0] = "pip3"
            process = subprocess.Popen(
                command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )
        except FileNotFoundError:
            _LOGGER.debug(
                _("Couldn't find the command 'pip3', install of %s will be skipped."),
                str(requirements_path),
            )

        if not process:
            raise OSError(_("Pip and pip3 not found, exiting..."))

        Loader._communicate_process(process)
        return True

    @staticmethod
    def _communicate_process(process):
        for output in process.communicate():
            for line in output.splitlines():
                _LOGGER.debug(str(line).strip())

    @staticmethod
    def _load_intents(config):
        intent_file = os.path.join(config["install_path"], "intents.yml")
        if os.path.isfile(intent_file):
            with open(intent_file, "r") as intent_file_handle:
                intents = intent_file_handle.read()
                return intents
        else:
            return None

    def setup_modules_directory(self, config):
        """Create and configure the modules directory.

        Args:
            self: instance method
            config: dict of fields from configuration.yaml

        """
        module_path = config.get("module-path", DEFAULT_MODULES_PATH)
        sys.path.append(module_path)

        if not os.path.isdir(module_path):
            os.makedirs(module_path, exist_ok=True)

        self.modules_directory = os.path.join(module_path, MODULES_DIRECTORY)

        # Create modules directory if doesn't exist
        if not os.path.isdir(self.modules_directory):
            os.makedirs(self.modules_directory)

    def load_modules_from_config(self, config):
        """Load all module types based on config.

        Args:
            self: instance method
            config: dict of fields from configuration.yaml

        Returns:
            dict: containing connector, database, and skills
                fields from configuration.yaml

        """
        _LOGGER.debug(_("Loading modules from config..."))

        self.setup_modules_directory(config)

        connectors, databases, parsers, skills = None, None, None, None

        if "databases" in config.keys() and config["databases"]:
            databases = self._load_modules("database", config["databases"])
        else:
            _LOGGER.warning(
                _(
                    "No databases in configuration. This will cause skills which store things in "
                    "memory to lose data when opsdroid is restarted."
                )
            )
        if "parsers" in config.keys() and config["parsers"]:
            parsers = self._load_modules("parsers", config["parsers"])

        if "skills" in config.keys() and config["skills"]:
            skills = self._load_modules("skill", config["skills"])

        if "connectors" in config.keys() and config["connectors"]:
            connectors = self._load_modules("connector", config["connectors"])
        else:
            self.opsdroid.critical(
                _("No connectors in configuration, at least 1 required"), 1
            )

        return {
            "connectors": connectors,
            "databases": databases,
            "parsers": parsers,
            "skills": skills,
        }

    def setup_module_config(self, modules, module, modules_type, entry_points):
        """Set up configuration for module.

        When setting up the configuration for a module we assign a lot
        of key:value pairs into a config dictionary.

        Also we might want to load from a configuration file an item that
        is just a string rather than a mapping object so we do a check and
        update the config as appropriate.

        We also need to update the config file with the rest of the config params.
        Since modules can be Key: { key: value } or key: None, we suppress the
        TypeError exception which is given when we try to use .get() on a None type,
        .

        Args:
            modules (dict): Dictionary containing all modules
            module (dict): Module to be configured
            modules_type (str): Type of module being loaded
            entry_points (dict): name of possible entry points.

        Returns:
            dict: configuration containing all the set key:values

        """
        config = module
        config = {} if config is None else config

        if not isinstance(config, Mapping):
            config = {"name": module, "module": ""}
        else:
            config["name"] = module["name"]
            config["module"] = module.get("module", "")

        with contextlib.suppress(TypeError, AttributeError):
            config.update(modules.get(module))

        config["type"] = modules_type
        if config.get("enabled") is not False:
            config["enabled"] = True
        config["entrypoint"] = entry_points.get(config["name"], None)
        config["is_builtin"] = self.is_builtin_module(config)
        config["module_path"] = self.build_module_import_path(config)
        config["install_path"] = self.build_module_install_path(config)
        if "branch" not in config:
            config["branch"] = DEFAULT_MODULE_BRANCH

        return config

    def _load_modules(self, modules_type, modules):
        """Install and load modules.

        Args:
            self: instance method
            modules_type (str): Type of module being loaded
            modules (dict): Dictionary containing all modules

        Returns:
            list: modules and their config information

        """
        _LOGGER.debug(_("Loading %s modules..."), modules_type)
        loaded_modules = list()

        if not os.path.isdir(DEFAULT_MODULE_DEPS_PATH):
            os.makedirs(DEFAULT_MODULE_DEPS_PATH)
        sys.path.append(DEFAULT_MODULE_DEPS_PATH)

        # entry point group naming scheme: opsdroid_ + module type plural,
        # eg. "opsdroid_databases"
        epname = "opsdroid_{}s".format(modules_type)
        entry_points = {ep.name: ep for ep in iter_entry_points(group=epname)}
        for epname in entry_points:
            _LOGGER.debug(
                _("Found installed package for %s '%s' support."), modules_type, epname
            )

        for module in modules:
            config = self.setup_module_config(
                modules, module, modules_type, entry_points
            )

            # If the module isn't builtin, or isn't already on the
            # python path, install it
            if not (config["is_builtin"] or config["module"] or config["entrypoint"]):
                # Remove module for reinstall if no-cache set
                self.check_cache(config)

                # Install or update module
                if not self._is_module_installed(config):
                    self._install_module(config)
                else:
                    self._update_module(config)

            # Import module
            self.current_import_config = config
            module = self.import_module(config)

            # Suppress exception if module doesn't contain CONFIG_SCHEMA
            with contextlib.suppress(AttributeError):
                config = validate_configuration(config, module.CONFIG_SCHEMA)

            # Load intents
            intents = self._load_intents(config)

            if module is not None:
                loaded_modules.append(
                    {"module": module, "config": config, "intents": intents}
                )
            else:
                _LOGGER.error(_("Module %s failed to import."), config["name"])
        return loaded_modules

    def _install_module(self, config):
        """Install a module.

        Args:
            self: instance method
            config: dict of module config fields

        """
        _LOGGER.debug(_("Installing %s..."), config["name"])

        if self._is_local_module(config):
            self._install_local_module(config)
        elif self._is_gist_module(config):
            self._install_gist_module(config)
        else:
            self._install_git_module(config)

        if self._is_module_installed(config):
            _LOGGER.debug(
                _("Installed %s to %s."), config["name"], config["install_path"]
            )
        else:
            _LOGGER.error(_("Install of %s failed."), config["name"])

        self._install_module_dependencies(config)

    def _update_module(self, config):
        """Update a module.

        Args:
            self: instance method
            config: dict of module config fields

        """
        _LOGGER.debug(_("Updating %s..."), config["name"])

        if self._is_local_module(config):
            _LOGGER.debug(_("Local modules are not updated, skipping."))
            return

        self.git_pull(config["install_path"])
        self._install_module_dependencies(config)

    @staticmethod
    def _is_module_installed(config):
        return os.path.isdir(config["install_path"]) or os.path.isfile(
            config["install_path"] + ".py"
        )

    @staticmethod
    def _is_local_module(config):
        return "path" in config

    @staticmethod
    def _is_gist_module(config):
        return "gist" in config

    def _install_module_dependencies(self, config):
        """Install the dependencies of the module.

        Args:
            self: instance method
            config: dict of the module config fields

        Returns:
            bool: True if installation succeeds

        """
        if config.get("no-dep", False):
            _LOGGER.debug(
                _(
                    "'no-dep' set in configuration, skipping the install of dependencies."
                )
            )
            return None

        if os.path.isfile(os.path.join(config["install_path"], "requirements.txt")):
            self.pip_install_deps(
                os.path.join(config["install_path"], "requirements.txt")
            )
            return True

        _LOGGER.debug(_("Couldn't find the file requirements.txt, skipping."))
        return None

    def _install_git_module(self, config):
        """Install a module from a git repository.

        Args:
            self: instance method
            config: dict of module config fields

        """
        if config is not None and "repo" in config:
            git_url = config["repo"]
        else:
            git_url = DEFAULT_GIT_URL + config["type"] + "-" + config["name"] + ".git"

        if any(prefix in git_url for prefix in ["http", "https", "ssh", "git@"]):
            # TODO Test if url or ssh path exists
            # TODO Handle github authentication
            _LOGGER.info(_("Cloning %s from remote repository."), config["name"])
            key_path = config.get("key_path", None)
            self.git_clone(git_url, config["install_path"], config["branch"], key_path)
        else:
            if os.path.isdir(git_url):
                _LOGGER.debug(_("Cloning %s from local repository."), config["name"])
                self.git_clone(git_url, config["install_path"], config["branch"])
            else:
                _LOGGER.error(_("Could not find local git repo %s."), git_url)

    def _install_local_module(self, config):
        """Install a module from a local path.

        Args:
            config: dict of module config fields

        """
        installed = False
        config["path"] = os.path.expanduser(config["path"])

        installdir, _ = os.path.split(config["install_path"])
        if not os.path.isdir(installdir):
            os.makedirs(installdir, exist_ok=True)

        if os.path.isdir(config["path"]):
            shutil.copytree(config["path"], config["install_path"])
            installed = True

        if os.path.isfile(config["path"]):
            os.makedirs(config["install_path"], exist_ok=True)
            init_path = os.path.join(config["install_path"], "__init__.py")
            if file_is_ipython_notebook(config["path"]):
                convert_ipynb_to_script(config["path"], init_path)
            else:
                shutil.copyfile(config["path"], init_path)
            installed = True

        if not installed:
            _LOGGER.error("Failed to install from %s.", str(config["path"]))
        else:
            self.opsdroid.reload_paths.append(config["path"])

    def _install_gist_module(self, config):
        """Install a module from gist path.

        Args:
            self: instance method
            config: dict of module config fields

        """
        gist_id = extract_gist_id(config["gist"])

        # Get the content of the gist
        req = urllib.request.Request("https://api.github.com/gists/{}".format(gist_id))
        cont = json.loads(urllib.request.urlopen(req).read().decode("utf-8"))
        python_files = [
            cont["files"][file]
            for file in cont["files"]
            if ".ipynb" in cont["files"][file]["filename"]
            or ".py" in cont["files"][file]["filename"]
        ]

        # We only support one skill file in a gist for now.
        #
        # TODO: Add support for multiple files. Could be particularly
        # useful for including a requirements.txt file.
        skill_content = python_files[0]["content"]
        extension = os.path.splitext(python_files[0]["filename"])[1]

        with tempfile.NamedTemporaryFile(
            "w", delete=False, suffix=extension
        ) as skill_file:
            skill_file.write(skill_content)
            skill_file.flush()

            # Set the path in the config
            config["path"] = skill_file.name

            # Run local install
            self._install_local_module(config)


from selenium import webdriver
from depot.manager import DepotManager
from PIL import Image
import os
import sys
sys.path.append("../app")
from settings import *

class Screenshot():

    def __init__(self, url, element):
        self.url = url
        self.element = element

    def get_original_diagram(self):
        depot = DepotManager.get()
        self.driver = webdriver.PhantomJS()
        self.driver.maximize_window()  # set the window size that you need
        self.driver.get(self.url)
        self.ele = self.driver.find_element_by_id(self.element)

        self.loc1 = self.ele.location
        # self.size1 = ele.size

        screenshot_name = self.generate_name()
        print(APP_ROOT + "/static/images")
        self.screenshot_file_location = APP_ROOT + \
            "/static/images/" + screenshot_name + '.png'
        self.driver.save_screenshot(self.screenshot_file_location)

    def crop_screenshot(self):
        image2 = Image.open(self.screenshot_file_location)

        # size is 700x400
        left = self.loc1['x']
        top = self.loc1['y']
        right = self.loc1['x'] + 700
        bottom1 = self.loc1['y'] + 400

        image2 = image2.crop((left, top, right, bottom1))
        os.remove(self.screenshot_file_location)
        image2.save(self.screenshot_file_location)

    def finish(self):
        self.driver.quit()

    def generate_name(self):
        tokens = self.url.split("/")
        print(tokens)
        return tokens[len(tokens) - 1]

    # scatterplot
    # radar_graph
    # lda_graph
    # ingredient_freq
    # word_cloud

if __name__ == "__main__":
    s = Screenshot("http://localhost:5000/lda_graph", "lda")
    s.get_original_diagram()
    s.crop_screenshot()
    s.finish()


import logging
import sys

class class_logger(object):
    """descriptor, used to make logger for attribute in first usage"""
    def __init__(self):
        pass

    def __get__(self, instance, owner):
        log = getattr(owner, '__log__', None)
        if log is None:
            name = owner.__module__ + '.' + owner.__name__
            log = logging.getLogger(name)
            owner.__log__ = log
            return log
        return log

def setup_log(args):
    root = logging.getLogger()
    if args.verbose:
        fs = "%(levelname)s:%(name)s:%(message)s"
        root.setLevel(logging.DEBUG)
    else:
        fs = "%(levelname)s %(message)s"
        root.setLevel(logging.INFO)
    handler = logging.StreamHandler(sys.stdout)
    fmt = logging.Formatter(fs)
    handler.setFormatter(fmt)
    root.addHandler(handler)
    #logging.basicConfig(level=logging.DEBUG)


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

"""
d2lstat.py

python d2lstat.py usage_data.csv full_time.csv part_time.csv semester_name number_of_courses_running

Given a correct data set and lists of full- and part-time teachers, this program will generate usage statistics on a
given template, which can be edited under the generate_document function.

Authors:
Dan Ricker <daniel.ricker@scranton.edu>
Sean Batzel <sean.batzel@scranton.edu>

This program is the property of the UofS-CTLE.
"""
from __future__ import division

import csv
import os

from django.conf import settings

# GLOBAL SETTINGS - DO NOT CHANGE UNLESS ABSOLUTELY NECESSARY.
DELIMITER = '|'  # Determines what character the program wil break data rows on.
ASSIGNMENTS = 13  # The column that assignments data is located in.
GRADE = 15  # The column that grade item data is located in.
GRADED = 16  # The column that graded grade item data is located in.
DISCUSSION = 18  # The column for discussion post usage.
USAGE_ROYAL = 3  # The column where the instructor's R number is located in the usage file.
FAC_ROYAL = 0  # Where the faculty member's R number is located in the full-/part-time CSV files.

def filter_for_semester(files_data, semester):
    """
    Used to filter the entire set of usage data for a given semester.

    :param files_data: The list of all course data taken from the usage CSV file.
    :param semester: The year and semester (e.g. 2018_Fall) to search for in the usage data.
    :return: The list of courses pertaining to the given semester.
    """
    final = list()
    for x in files_data:
        y = x.split(DELIMITER)
        if semester in y[9]:  # NOTE This will always work, provided the semester string is given correctly.
            final.append(x)
    return final

def get_rows_with_usage(files_data):
    """
    Filters the semester's courses on which ones had activity in D2L.

    :param files_data: The filtered list of courses for the semester.
    :return: The list of courses that had relevant usage in D2L.
    """
    final = list()
    for x in files_data:
        y = x.split(DELIMITER)
        if int(y[ASSIGNMENTS]) > 0 or int(y[GRADE]) > 2 or int(y[GRADED]) > 0 or int(y[DISCUSSION]) > 0:
            final.append(x)
    return final

def remove_duplicate_crn(files_data):
    """
    Makes sure that none of the courses in the list repeat.

    :param files_data: Filtered data passed in from get_rows_with_usage.
    :return: The list of coures without any repeated.
    """
    seen_crns = []
    ret_val = []
    for x in files_data:
        y = x.split(DELIMITER)
        # This checks the last 5 characters of y[9] for a CRN.
        # Make sure this is where the CRN is still located before running.
        if y[9][-5:] not in seen_crns:
            seen_crns.append(y[9][-5:])
            ret_val.append(x)
    return ret_val

def remove_duplicate_royal(files_data):
    """
    Filters and removes duplicate instructor RIDs.

    :param files_data: Filtered data passed in from get_rows_with_usage.
    :return: The list of courses with no instructor R numbers duplicated.
    """
    seen_royal = []
    ret_val = []
    for x in files_data:
        y = x.split(DELIMITER)
        if y[USAGE_ROYAL] not in seen_royal:
            seen_royal.append(y[USAGE_ROYAL])
            ret_val.append(x)
    return ret_val

def parse_files(usage, full_time, part_time, semester, total_courses):
    """
    Reads the given CSV files and splits up the contents into digestible data structures.

    :param usage: The name of the usage CSV file.
    :param full_time: The name of the CSV of full-time faculty members.
    :param part_time: The name of the CSV of part-time faculty members.
    :param semester: The semester in the form YYYY_Session (e.g. 2018_Fall).
    :param total_courses: (Provided externally) The number of courses running for the given semester.
    :return: A data structure containing all of the data required for the rest of the program.
    """
    with open(usage, mode='rU') as infile:
        reader = csv.reader(infile, dialect='excel', delimiter=',', quotechar='"')
        rows = list()
        for row in reader:
            rows.append(row)
        with open(usage, mode='w') as outfile:
            writer = csv.writer(outfile, delimiter=DELIMITER)
            writer.writerows(rows)
    with open(full_time, mode='rU') as infile:
        reader = csv.reader(infile, dialect='excel', delimiter=',', quotechar='"')
        rows = list()
        for row in reader:
            rows.append(row)
        with open(full_time, mode='w') as outfile:
            writer = csv.writer(outfile, delimiter=DELIMITER)
            writer.writerows(rows)
    with open(part_time, mode='rU') as infile:
        reader = csv.reader(infile, dialect='excel', delimiter=',', quotechar='"')
        rows = list()
        for row in reader:
            rows.append(row)
        with open(part_time, mode='w') as outfile:
            writer = csv.writer(outfile, delimiter=DELIMITER)
            writer.writerows(rows)
    one = filter_for_semester(open(usage, 'r').readlines(), semester)
    two = get_rows_with_usage(one)
    usage_file = remove_duplicate_crn(two)
    no_dup_r = remove_duplicate_royal(two)
    full_time_file = open(full_time, 'r').readlines()
    full_r = list()
    part_r = list()
    for x in full_time_file:
        y = x.split(DELIMITER)
        full_r.append(y[FAC_ROYAL])
    part_time_file = open(part_time, 'r').readlines()
    for x in part_time_file:
        y = x.split(DELIMITER)
        part_r.append(y[FAC_ROYAL])
    full = list()
    part = list()
    staff = list()
    for x in range(len(part_r)):
        part_r[x] = part_r[x].strip("\"")
    for x in range(len(full_r)):
        full_r[x] = full_r[x].strip("\"")
    for x in no_dup_r:
        y = x.split(DELIMITER)
        if y[USAGE_ROYAL] in full_r:
            full.append(y)
        elif y[USAGE_ROYAL] in part_r:
            part.append(y)
        else:
            staff.append(y)
    return {'semester_no_dup_crn': usage_file,
            'semester_no_dup_r': no_dup_r,
            'semester': two,
            'full_time': full,
            'len_full': len(full_time_file),
            'part_time': part,
            'len_part': len(part_time_file),
            'staff': staff,
            'total_courses': total_courses}

def calculate_stats(file_data):
    """
    Carries out the actual logic for generating the numbers that will be included in the report.

    :param file_data: The data produced by the parse function.
    :return: The statistics data required for the report generator.
    """
    specifics = {
        'assignments': 0,
        'grade': 0,
        'graded': 0,
        'discussion': 0
    }
    for course in file_data['semester_no_dup_crn']:
        x = course.split(DELIMITER)
        if int(x[ASSIGNMENTS]) > 0:
            specifics['assignments'] += 1
        if int(x[GRADE]) > 2:
            specifics['grade'] += 1
        if int(x[GRADED]) > 0:
            specifics['graded'] += 1
        if int(x[DISCUSSION]) > 0:
            specifics['discussion'] += 1
    return {'semester': file_data['semester'],
            'courses_with_usage': len(file_data['semester_no_dup_crn']),
            'faculty_with_usage': len(file_data['semester_no_dup_r']),
            'full_time': len(file_data['full_time']),
            'total_full_time': file_data['len_full'],
            'part_time': len(file_data['part_time']),
            'total_part_time': file_data['len_part'],
            'staff': len(file_data['staff']),
            'specifics': specifics,
            'total_courses': file_data['total_courses']}

def generate_document(stats, semester):
    """
    Generates an HTML document and PDF with the requested stats for the semester's D2L usage.

    :param stats: The dictionary returned by the generate_stats function.
    :param semester: The semester value passed in as a command-line argument.
    """
    with open(os.path.join(settings.BASE_DIR, 'd2lstat/templates/d2lstat/raw_html.html'), 'r') as f:
        string = f.read()
    string = string.format(semester,
                           stats['faculty_with_usage'],
                           stats['full_time'],
                           stats['total_full_time'],
                           round((stats['full_time'] / stats['total_full_time']) * 100, 1),
                           stats['part_time'],
                           stats['total_part_time'],
                           round((stats['part_time'] / stats['total_part_time']) * 100, 1),
                           stats['staff'],
                           stats['courses_with_usage'],
                           stats['total_courses'],
                           round((stats['courses_with_usage'] / int(stats['total_courses'])) * 100, 1),
                           stats['specifics']['assignments'],
                           stats['specifics']['grade'],
                           stats['specifics']['graded'],
                           stats['specifics']['discussion'])
    with open(os.path.join(settings.BASE_DIR, 'd2lstat/templates/d2lstat/report.html'), 'w') as f:
        f.write(string)
        f.close()

def process_file(usage, full_time, part_time, semester, total_courses):
    """

    :param usage:
    :param full_time:
    :param part_time:
    :param total_courses
    :param semester:
    """
    res = parse_files(usage, full_time, part_time, semester, total_courses)
    res = calculate_stats(res)
    generate_document(res, semester)

def calculateVirtualClassroomStats(usage, fullTime, partTime, VCDataFile):
    resultList = []
    # read in the data from the lti (learning tools integration) file
    virtualClassroomDataFile = open(VCDataFile, 'rU')
    virtualClassroomDataReader = csv.reader(virtualClassroomDataFile)
    virtualClassroomData = []
    seenVirtualClassRoomOrgUnitIds = []
    for row in virtualClassroomDataReader:
        if ('youseeu' in row[5] and row[
            1] not in seenVirtualClassRoomOrgUnitIds):  # get the org unit ids of the courses in which there was created at least one virtual classroom meeting
            virtualClassroomData.append(row)
            seenVirtualClassRoomOrgUnitIds.append(row[1])
    # read in the instructor usage data file that was obtained from desire 2 learn data hub
    instructorUsageDataFile = open(usage, 'rU')
    instructorUsageDataReader = csv.reader(instructorUsageDataFile)
    instructorUsageData = []
    seenRIds = []
    numberOfFacultyMembersUsingVirtualClassroom = 0
    fullDataOnInstructors = []
    # print('Instructors That Have Created at Least 1 Virtual Classroom Meeting:')
    resultList.append('Instructors That Have Created at Least 1 Virtual Classroom Meeting:')
    for row in instructorUsageDataReader:
        if (row[
            10] in seenVirtualClassRoomOrgUnitIds):  # filter the rows to just be the rows for faculty members that have created at least one virtual classroom meeting
            if (row[3] not in seenRIds):  # make sure that each instructor is onlt accounted for once
                seenRIds.append(row[3])
                fullDataOnInstructors.append([row[3], row[1], row[2]])
                # print(row[3] + ': ' + row[1] + ', ' + row[2])
                resultList.append(row[3] + ': ' + row[1] + ', ' + row[2])
    numberOfFacultyMembersUsingVirtualClassroom = len(seenRIds)

    # print('Number of Instructors That Have Created at Least 1 Virtual Classroom Meeting: ' + str(
    #     numberOfFacultyMembersUsingVirtualClassroom))
    resultList.append('Number of Instructors That Have Created at Least 1 Virtual Classroom Meeting: ' + str(
        numberOfFacultyMembersUsingVirtualClassroom))
    seenFullAndPartTimeRIds = []  # this is needed to keep track of the Rids that belong to either full or part time faculty members in order to determine which rids are left over, the left over rids are the rids of staff members teaching part time
    # Full time faculty members that have created at least one virtual classroom meeting
    fullTimeFacultyDataFile = open(fullTime, 'rU')
    fullTimeFacultyDataReader = csv.reader(fullTimeFacultyDataFile)
    fullTimeFacultyUsingVirtualClassroomRids = []
    for row in fullTimeFacultyDataReader:
        if (row[0] in seenRIds):
            fullTimeFacultyUsingVirtualClassroomRids.append(row[0])
            seenFullAndPartTimeRIds.append(row[0])
    # Part time faculty members that have created at least one virtual classroom meeting
    partTimeFacultyDataFile = open(partTime, 'rU')
    partTimeFacultyDataReader = csv.reader(partTimeFacultyDataFile)
    partTimeFacultyUsingVirtualClassroomRids = []
    for row in partTimeFacultyDataReader:
        if (row[0] in seenRIds):
            partTimeFacultyUsingVirtualClassroomRids.append(row[0])
            seenFullAndPartTimeRIds.append(row[0])
    staffTeachingPartTimeRids = []
    for rid in seenRIds:
        if (rid in seenFullAndPartTimeRIds):
            staffTeachingPartTimeRids.append(rid)
    # sort full instructor data based upon the rids in each category
    fullTimeFacultyUsingVC = []
    partTimeFacultyUsingVC = []
    staffUsingVC = []
    # sort out full time faculty
    for rid in fullTimeFacultyUsingVirtualClassroomRids:
        for row in fullDataOnInstructors:
            if (row[0] == rid):
                fullTimeFacultyUsingVC.append(row)
    # sort out part time faculty
    for rid in partTimeFacultyUsingVirtualClassroomRids:
        for row in fullDataOnInstructors:
            if (row[0] == rid):
                partTimeFacultyUsingVC.append(row)
    # sort out staff
    for row in fullDataOnInstructors:
        if (row[0] not in seenFullAndPartTimeRIds):
            staffUsingVC.append(row)
    resultList.append("Full Time Faculty Using Virtual Classroom:")
    # print("Full Time Faculty Using Virtual Classroom:")
    for row in fullTimeFacultyUsingVC:
        # print(row[0] + ': ' + row[2] + ', ' + row[1])
        resultList.append(row[0] + ': ' + row[2] + ', ' + row[1])

    # print("The Number of Full Time Faculty Using Virtual Classroom: " + str(len(fullTimeFacultyUsingVC)))
    resultList.append("The Number of Full Time Faculty Using Virtual Classroom: " + str(len(fullTimeFacultyUsingVC)))
    # print("Part Time Faculty Using Virtual Classroom:")
    resultList.append("Part Time Faculty Using Virtual Classroom:")
    for row in partTimeFacultyUsingVC:
        # print(row[0] + ': ' + row[2] + ', ' + row[1])
        resultList.append(row[0] + ': ' + row[2] + ', ' + row[1])
    # print("The Number of Part Time Faculty Using Virtual Classroom: " + str(len(partTimeFacultyUsingVC)))
    resultList.append("The Number of Part Time Faculty Using Virtual Classroom: " + str(len(partTimeFacultyUsingVC)))
    # print("Staff Teaching Part Time Using Virtual Classroom:")
    resultList.append("Staff Teaching Part Time Using Virtual Classroom:")
    for row in staffUsingVC:
        # print(row[0] + ': ' + row[2] + ', ' + row[1])
        resultList.append(row[0] + ': ' + row[2] + ', ' + row[1])
    print("The Number of Staff Teaching Part Time Using Virtual Classroom: " + str(len(staffUsingVC)))
    resultList.append("The Number of Staff Teaching Part Time Using Virtual Classroom: " + str(len(staffUsingVC)))
    return resultList

def facultyNotUsingD2LCalculation(usage, fullTime, partTime, semester):
    resultList = []
    usageFile = open(usage, 'rU')
    fullTimeFile = open(fullTime, 'rU')
    partTimeFile = open(partTime, 'rU')
    usageFileReader = csv.reader(usageFile)
    fullTimeFileReader = csv.reader(fullTimeFile)
    partTimeFileReader = csv.reader(partTimeFile)
    usageDataRaw = []
    usageData = []
    fullTimeDataRaw = []
    partTimeDataRaw = []
    fullTimeNotUsing = []
    partTimeNotUsing = []
    staffTeachingPartTimeNotUsing = []
    fullTimeRIds = []
    partTimeRIds = []
    ridsOfInstructorsUsing = []
    seenRidsOfInstructorsUsing = []
    ridsOfInstructorsUsingDuplicatesRemoved = []
    # read in the usage data
    for row in usageFileReader:
        usageDataRaw.append(row)
    # Get the rids of the instructors using D2L
    for row in usageDataRaw:
        # print(row[13] + ',' +row[15] + ',' +row[16] + ',' +row[18])
        try:
            if((int(row[13])>0 or int(row[15])>2 or int(row[16])>0 or int(row[18])>0)):
                if(semester in row[9]):
                    ridsOfInstructorsUsing.append(row[3])
        except Exception:
            print(Exception)
    seenRIds = []
    # Filter the rids of instuctors using for duplicates
    for row in ridsOfInstructorsUsing:
        if(row not in seenRidsOfInstructorsUsing):
            ridsOfInstructorsUsingDuplicatesRemoved.append(row)
            seenRidsOfInstructorsUsing.append(row)
    #get the Rids of instructors not using
    for row in usageDataRaw:
        if(row[3] not in ridsOfInstructorsUsingDuplicatesRemoved and semester in row[9]):
            usageData.append(row)
    for row in fullTimeFileReader:
        fullTimeDataRaw.append(row)
    for row in partTimeFileReader:
        partTimeDataRaw.append(row)
    for row in fullTimeDataRaw:
        fullTimeRIds.append(row[0])
    for row in partTimeDataRaw:
        partTimeRIds.append(row[0])
    for row in usageData:
        if(row[3] in fullTimeRIds and row[3] not in seenRIds):
            fullTimeDepartment = ''
            for fullTimeRow in fullTimeDataRaw:
                if(row[3]==fullTimeRow[0]):
                    fullTimeDepartment = fullTimeRow[5]
            fullTimeNotUsing.append([row[3], row[1], row[2], fullTimeDepartment])
            seenRIds.append(row[3])
        elif(row[3] in partTimeRIds and row[3] not in seenRIds):
            if(row[3] in partTimeRIds):
                partTimeDepartment = ''
                for partTimeRow in partTimeDataRaw:
                    if(row[3]==partTimeRow[0]):
                        partTimeDepartment = partTimeRow[5]
                partTimeNotUsing.append([row[3], row[1], row[2], partTimeDepartment])
                seenRIds.append(row[3])
        else:
            if(row[3] not in seenRIds):
                staffTeachingPartTimeNotUsing.append([row[3], row[1], row[2]])
                seenRIds.append(row[3])
    resultList.append('The Total Number of Faculty Not Using Desire 2 Learn: ' + str(len(fullTimeNotUsing)+len(partTimeNotUsing) + len(staffTeachingPartTimeNotUsing)))
    resultList.append('The Number of Full Time Faculty Not Using D2L: ' + str(len(fullTimeNotUsing)))
    resultList.append('Full Time Faculty Not Using Desire 2 Learn:')
    for row in fullTimeNotUsing:
        resultList.append(row[0] + ', ' + row[1] + ', ' + row[2] + ', ' + row[3])
    resultList.append('')
    resultList.append('')
    resultList.append('The Number of Part Time Faculty Not Using D2L: ' + str(len(partTimeNotUsing)))
    resultList.append('Part Time Faculty Not Using Desire 2 Learn:')
    for row in partTimeNotUsing:
        resultList.append(row[0] + ', ' + row[1] + ', ' + row[2] + ', ' + row[3])
    resultList.append('')
    resultList.append('')
    resultList.append(
        'The Number of Staff Teaching Part Time Faculty Not Using D2L: ' + str(len(staffTeachingPartTimeNotUsing)))
    resultList.append('Staff Teaching Part Time Not Using Desire 2 Learn:')
    for row in staffTeachingPartTimeNotUsing:
        resultList.append(row[0] + ', ' + row[1] + ', ' + row[2])
    return resultList


import json
import datetime
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from pygtail import Pygtail

tp_executor = ThreadPoolExecutor(3)

class EventTimedStorage:
    def __init__(self, duration):
          self.duration = datetime.timedelta(minutes=duration)
          self.events = []
          self.shops = {'shops.computeruniverse' : True}
          self.not_shops = ['worker', 'discord_webhook.webhook']

    def add_event(self, timestamp, type, event, shop=None):
        self.events.append((timestamp, type, shop, event))
        self.events = list(filter(lambda ts_ev: ts_ev[0] + self.duration >= timestamp, self.events))
        if shop != None and shop not in self.shops and shop not in self.not_shops:
            self.shops[shop] = True

    def _get_events(self, timestamp):
        self.events = list(filter(lambda ts_ev: ts_ev[0] + self.duration >= timestamp, self.events))
        return self.events

    def _get_last_shops(self, timestamp):
        self.events = list(filter(lambda ts_ev: ts_ev[0] + self.duration >= timestamp, self.events))
        return [e[2] for e in self.events]

class NotifierEventStorage(EventTimedStorage):
    def __init__(self, duration):
        EventTimedStorage.__init__(self, duration)
        self.is_broke_param = {
            False : 'Notifier is work',
            True : 'Notifier is broken',
        }

    def is_notifier_broken(self, timestmp):
        return sum(e[1] == 'ERROR' for e in self._get_events(timestmp)) > 1

class MonitorEventStorage(EventTimedStorage):
    def __init__(self, duration):
        EventTimedStorage.__init__(self, duration)
        self.is_broke_param = {
            True : 'Monitor is work',
            False : 'Monitor is broken',
        }
        self.message_param = {
            True : 'Monitor restarted',
            False : 'Cannot give items last 15 minute',
        }

    # def is_monitor_broken(self, timestmp):
    #     return sum(e[1] == 'ERROR' for e in self._get_events(timestmp)) > 1

    def is_monitor_broken(self, timestmp):
        result = {}
        shop = None
        last_shops = self._get_last_shops(timestmp)
        if last_shops == []:
            return {}
        for s in self.shops:
            if s not in last_shops:
                result[s] = False
                shop = s
            else:
                result[s] = True
        return result

class LogCheck():
    def __init__(self):
        self.path = None
        self.webhook = None
        self.timestamp_last_restart = None
        self.sleep_time = datetime.timedelta(minutes=15).total_seconds()

    def notify(self, log):
        log['webhooks'] = self.webhook
        self.send_notification(**log)
        print(log)

    def restart_docker(self, shop):
        try:
            if (self.timestamp_last_restart == None
                or (datetime.datetime.now() - self.timestamp_last_restart).total_seconds() > self.sleep_time
                or shop == 'shops.computeruniverse'):
                # open("/to_check/logs/restart_state.txt", "w")
                open("Production/MessiahNotify/logs/restart_state.txt", "w")
                self.timestamp_last_restart = datetime.datetime.now()
        except Exception as e:
            raise e

    def send_notification_impl(self, info, asctime, name, webhooks, levelname,
                                message, override=None, **kwargs):
        if not override:
            override = {}

        webhook = DiscordWebhook(url=webhooks)
        embed = DiscordEmbed(title='event: ' + info + ', time: ' + asctime + ' ' + ', file name: ' + name,
                            description=message,
                            color=override.get('color', 0),
                            )
        webhook.add_embed(embed)
        webhook.execute()

    def send_notification(self, **kwargs):
        self.send_notification_impl(**kwargs)

    def return_log(self):
        tail = Pygtail(self.path, paranoid=True, offset_file='log.offset')
        try:
            line = tail.next()
            log = json.loads(line)
            return log
        except:
            tail._update_offset_file()

    def check_on_error(self):
        pass

class LogCheckNotify(LogCheck):
    def __init__(self):
        LogCheck.__init__(self)
        self.path = 'daemon.log'
        self.webhook = 'https://discord.com/api/webhooks/871656292568162334/iOwyRZMwH-rDE_dkVPpsmaUXeHPxmmGT9RaxnqiSMVrzN2tM1vDDdT2WCBe7ZdrY5Bdt'
        self.is_broke = 0

    def check_on_error(self):
        notifier_checker = NotifierEventStorage(15)
        while not is_stopped():
            log = self.return_log()
            if log != None:
                try:
                    timestamp = datetime.datetime.strptime(log['asctime'], '%Y-%m-%d %H:%M:%S,%f')
                    notifier_checker.add_event(timestamp, log['levelname'], log['message'])
                    current_time = datetime.datetime.now()
                    is_broke = notifier_checker.is_notifier_broken(current_time)
                    if is_broke != self.is_broke:
                        self.is_broke = is_broke
                        log['info'] = notifier_checker.is_broke_param[self.is_broke]
                        self.notify(log)
                except:
                    raise

class LogCheckMonitors(LogCheck):
    def __init__(self):
        LogCheck.__init__(self)
        self.path = 'daemon.log'
        self.webhook = 'https://discord.com/api/webhooks/871656292568162334/iOwyRZMwH-rDE_dkVPpsmaUXeHPxmmGT9RaxnqiSMVrzN2tM1vDDdT2WCBe7ZdrY5Bdt'
        self.shops_state = {}

    def check_on_error(self):
        checker = MonitorEventStorage(15)
        while not is_stopped():
            log = self.return_log()
            if log != None:
                try:
                    timestamp = datetime.datetime.strptime(log['asctime'], '%Y-%m-%d %H:%M:%S,%f')
                    checker.add_event(timestamp, log['levelname'], log['message'], log['name'])
                    self.shops_state = checker.shops
                    current_time = datetime.datetime.now()
                    is_state_change= checker.is_monitor_broken(current_time)
                    if is_state_change != {} and is_state_change != self.shops_state:
                        broken_shop = [s for s in is_state_change if is_state_change[s] != self.shops_state[s]]
                        checker.shops = is_state_change
                        for shop in broken_shop:
                            log['name'] = shop
                            log['levelname'] = 'INFO'
                            log['info'] = checker.is_broke_param[is_state_change[shop]]
                            log['message'] = checker.message_param[is_state_change[shop]]
                            # self.notify(log)
                            if is_state_change[shop] == False:
                                self.restart_docker(shop)
                except:
                    raise

checker_stopped = [False]

checkers = [
    # LogCheckNotify,
    LogCheckMonitors,
]

pp_executor = ProcessPoolExecutor(3)

def is_stopped():
    return checker_stopped[0]

def start_checker():
    # apis = [api_cls() for api_cls in checkers]

    # futures = [(pp_executor.submit(api.check_on_error))
    #            for api in apis]
    # return futures
    LogCheckMonitors().check_on_error()

def stop_checker():
    print('Stopping checker')
    global checker_stopped
    checker_stopped[0] = True


# def solution(A, B):
#     lad_seq = []
#     for i in range(0, max(A)+1):
#         if i in (0,1):
#             lad_seq.append(1)
#         else:
#             lad_seq.append(lad_seq[-1] + lad_seq[-2])

#     res = []
#     for i in range(len(A)):
#         res.append(lad_seq[A[i]] % 2**B[i])

#     return res

def solution(A, B):
    # https://stackoverflow.com/questions/6670715/mod-of-power-2-on-bitwise-operators/6670766#6670766
    lad_seq = [1,2]
    for i in range(2, max(A)+1):
        lad_seq.append(lad_seq[-1] + lad_seq[-2])

    res = []
    for i in range(len(A)):
        res.append(lad_seq[A[i]-1] & pow(2, B[i])-1 )

    return res

print(solution([4,4,5,5,1], [3,2,4,3,1]))


import torch
from scipy.signal import butter, lfilter, filtfilt

from scipy import signal
import numpy as np
import pywt

from .models import *

CUTOFF_FREQ = 100
SAMPLING_RATE = 5500
INPUT_SIZE = 27500

def STFT(x, f_s=SAMPLING_RATE, nperseg=4000):
    f, t, Zxx_2_0 = signal.stft(np.abs(x), f_s, nperseg=nperseg)
    return np.abs(Zxx_2_0)

def downsample(x, interval=250):
    num_iter = int(x.shape[0]/interval)
    downsampled = np.zeros(num_iter)
    for i in range(num_iter):
        downsampled[i] = x[i*interval]
    return downsampled

def DWT(signal, f_s=SAMPLING_RATE, interval_ms=200):
    """
      https://sigmobile.org/mobicom/2015/papers/p65-wangA.pdf,
      https://pywavelets.readthedocs.io/en/latest/ref/dwt-discrete-wavelet-transform.html
    """
    samples_200_ms = int(f_s*interval_ms/1000)
    num_iter = int(signal.shape[0]/samples_200_ms)
    energies_over_time = np.zeros((12, num_iter))
    for i in range(num_iter):
        signal_snippet = signal[i*samples_200_ms:i *
                                samples_200_ms + samples_200_ms]
        detail_components = pywt.wavedec(signal_snippet, 'haar', level=12)[1:]
        energies = np.array([np.sum(np.power(components, 2))
                            for components in detail_components])
        energies_over_time[:, i] = energies
    return energies_over_time

def butterworth(signal, cutoff_freq=CUTOFF_FREQ, sampling_rate=SAMPLING_RATE, order=4):
    """Butterworth low-pass filter"""
    nyquist = 1/2 * sampling_rate
    normal_cutoff_freq = cutoff_freq/nyquist
    b, a = butter(order, normal_cutoff_freq, btype="low", analog=False)
    signal_filtered = lfilter(b, a, signal)
    return signal_filtered

def normalize_min_max(signal: np.ndarray) -> np.ndarray:
    xmin, xmax = np.amin(signal), np.amax(signal)
    return (signal - xmin ) / (xmax-xmin)

class ModelHandler:
    def __init__(self, model_path):
        # Load model dict
        self.model_dict = torch.load(model_path)

        # Initialize the model
        self.base_model = self.model_dict['base_model']
        self.model = model_types[self.base_model]()

        # Loads in the weights of the model
        self.model.load_state_dict(self.model_dict['model_state_dict'])

        # Mean and standardeviation.
        #self.mean = self.model_dict['data_mean']
        #self.std = self.model_dict['data_std']
        if self.base_model == "2DCNN":
            self.dwt_mean = self.model_dict['dwt_mean']
            self.dwt_std = self.model_dict['dwt_std']

        # Datasets and training parameters
        self.datasets = self.model_dict['datasets']
        self.epochs = self.model_dict['epoch']

        # Metrics
        self.train_loss = self.model_dict['train_loss']
        self.train_accuracy = self.model_dict['train_accuracy']
        self.test_accuracy = self.model_dict['test_accuracy']
        self.f1_score = self.model_dict['f1_score']
        self.num_train_samples = self.model_dict['num_train_samples']
        self.num_test_samples = self.model_dict['num_test_samples']
        self.batch_size = self.model_dict['batch_size']

    def get_relevant_metrics(self):
        irrelevant_metrics = {"optimizer_state_dict", "model_state_dict"}
        return {key:val for key, val in self.model_dict.items() if key not in irrelevant_metrics}

    def predict(self, data):
        '''
        Predicts the outcome of the input data. It will give the predicted class and also the certainty.
        '''
        # Absolute value of data
        data = np.abs(data)

        # Normalize and pre-process
        data = normalize_min_max(data)
        data = butterworth(data)
        if self.base_model == "1DCNN":
            data = downsample(data)
        elif self.base_model == "2DCNN":
            data = DWT(data)
            data = (data - self.dwt_mean)/self.dwt_std

        sample = torch.tensor(data, dtype=torch.float)
        # second unsqueeze to add batch dimension (needed for model).
        sample = sample.unsqueeze(dim=0).unsqueeze(dim=0)
        # Assertion to check if dimension is supported.
        assert sample.dim() <= 4, \
            'The input data has too many dimensions, max is 2 but the given input has {}.'.format(
                sample.dim() - 2)
        sample = sample.permute((1, 0, 2)) if sample.dim(
        ) == 3 else sample.permute((1, 0, 2, 3))

        self.model.eval()
        prob = self.model(sample)

        return torch.argmax(prob).detach().numpy(), np.ravel(prob.detach().numpy())


from typing import Dict, Iterable, List, Union
import torch
from pathlib import Path
from torch.utils.data import DataLoader

def create_dataloader(dataset, batch_size, shuffle=True):
    """
    Create a DataLoader instance from a dataset.

    Parameters:
    dataset: the dataset to load
    batch_size (int): the number of samples per batch
    shuffle (bool, optional): whether to shuffle the data. Default is True.

    Returns:
    DataLoader: a DataLoader instance
    """
    dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=shuffle)
    return dataloader

class StepLR:
    """
    Step Learning Rate scheduler.
    Adjusts the learning rate according to the number of epochs passed.
    """

    def __init__(self, optimizer, learning_rate: float, total_epochs: int):
        # Initializes the StepLR class.

        self.optimizer = optimizer
        self.total_epochs = total_epochs
        self.base = learning_rate

    def __call__(self, epoch):
        # Adjusts the learning rate according to the current epoch.

        if epoch < self.total_epochs * 3/10:
            lr = self.base
        elif epoch < self.total_epochs * 6/10:
            lr = self.base * 0.2
        elif epoch < self.total_epochs * 8/10:
            lr = self.base * 0.2 ** 2
        else:
            lr = self.base * 0.2 ** 3
        # Apply the learning rate to all parameter groups in the optimizer
        for param_group in self.optimizer.param_groups:
            param_group["lr"] = lr

    def lr(self) -> float:
        # Returns the current learning rate.

        return self.optimizer.param_groups[0]["lr"]

def parse_output(input_string: str) -> List[str]:
    """
    Parses a string into a list of strings.

    Parameters:
    input_string (str): The input string to parse.

    Returns:
    List[str]: The parsed output list.
    """

    input_list = input_string.split("\n")  # split the input by newline characters
    output_list = []
    for i, item in enumerate(input_list):
        item = item.lstrip(
            "0123456789. "
        )  # remove numbers and any leading whitespace
        if item:  # skip empty items
            output_list.append(item)
    return output_list

def get_device() -> torch.device:
    """
    Get the current device.

    If a CUDA GPU is available, it is used. Else, it checks for MPS compatibility.
    If not, it defaults to the CPU.

    Returns:
    torch.device: The device to use for computations.
    """

    if torch.cuda.is_available():
        device = torch.device("cuda:0")
    elif torch.has_mps:
        device = torch.device("mps")
    else:
        device = torch.device("cpu")

    print(f"Using device: {device}")

    return device

def read_json(path: Path) -> List[Dict[str, Union[str, int]]]:
    """
    Reads a JSON file and returns it as a Python list of dictionaries.

    Parameters:
    path (Path): The path to the JSON file to read.

    Returns:
    List[Dict[str, Union[str, int]]]: The content of the JSON file as a Python list of dictionaries.
    """
    with open(path, "r") as f:
        data: List[Dict[str, Union[str, int]]] = json.load(f)
    return data

def save_json(path: Path, container: Iterable) -> None:
    """
    Saves a Python iterable (such as a list or a dictionary) into a JSON file.

    Parameters:
    path (Path): The path to the JSON file to write into.
    container (Iterable): The Python iterable to write into the JSON file.
    """
    print(f"Saving json to {path}")
    with open(path, "w") as outfile:
        json.dump(container, outfile, ensure_ascii=False, indent=4)


"""
2325. Decode the Message
https://leetcode.com/problems/decode-the-message/
"""

class Solution:
    def decodeMessage(self, key: str, message: str) -> str:
        key_list = list(dict.fromkeys([i for i in key.replace(' ', '')]))
        output = ''
        for i in message:
            if i == ' ':
                output += ' '
            else:
                output += chr(key_list.index(i) + 97)
        return output


#from Room import Room

def search_available(vector,first,search_input,search_type):

  """
  vector: lista compuesta de otras listas, estas a su vez compuestas de objetos
  r_type_answer: tipo de habitacion o lista de clientes
  first: indice donde se desea comenzar la busqueda
  search_input: elemento que se desea buscar
  search_type: tipo de busqueda, denota lo que se desea buscar

  """

  for i in range(first,len(vector)):

    if search_type == 1:
      if vector[i].status == search_input:
        found = i
        break
      else:
        found = "No encontrado"
    if search_type == 2:
      if vector[i].dni == search_input:
        found = i
        break
      else:
        found = "No encontrado"
    if search_type == 3:
      if vector[i].capacity == search_input:
        found = i
        break
      else:
        found = "No encontrado"
    if search_type == 4:
      if vector[i].room_id == search_input:
        found = i
        break
      else:
        found = "No encontrado"
    if search_type == 5:
      if vector[i].tour_id == search_input:
        found = i
        break
      else:
        found = "No encontrado"
    if search_type == 6:
      if vector[i].name == search_input:
        found = i
        break
      else:
        found = "No encontrado"

  return found

def sort_vector(vector):

  n = len(vector)

  for i in range(n):

    min_idx = i

    for j in range(i+1,n):

      if vector[min_idx][1] > vector[j][1]:

        min_idx = j

    vector[i],vector[min_idx] = vector[min_idx],vector[i]

  return vector


def intro():
    print("Larry: I'm Larry. I'll be your hunting instructor.")

def travel_to_camp():
    print("Larry: Let's go to the Meadow to begin your training!")
    user_travel = input("Press Enter to travel to the Meadow...")
    if user_travel == "":
        print("Travelling to the Meadow...")
        print("Larry: This is your camp. Here you'll set up your mouse trap.")

def setup_trap():
    """Function that allows the player to select a trap from Larry.

    Returns
    -------
    tuple
        player's trap and cheese quantity by the end of the function
    """

    trap = " "
    cheese = 0

    print("Larry: Let's get your first trap...")
    user_enter = input("Press Enter to view traps that Larry is holding...")

    if user_enter == "":
        print("Larry is holding...\nLeft: High Strain Steel Trap\nRight: Hot Tub Trap")
        user_trap_select = input('Select a trap by typing "left" or "right": ')

        if user_trap_select.lower().strip() == 'left':
            trap = "High Strain Steel Trap"
            cheese = 1
            print(f'Larry: Excellent choice.\nYour "{trap}" is now set!\n'
            'Larry: You need cheese to attract a mouse.\n'
            'Larry places one cheddar on the trap!')
            return (trap, cheese)

        elif user_trap_select.lower().strip() == 'right':
            trap = "Hot Tub Trap"
            cheese = 1
            print(f'Larry: Excellent choice.\nYour "{trap}" is now set!\n'
            'Larry: You need cheese to attract a mouse.\n'
            'Larry places one cheddar on the trap!')
            return (trap, cheese)

        else:
            trap = "Cardboard and Hook Trap"
            cheese = 0
            print("Invalid command! No trap selected.\nLarry: Odds are slim with no trap!")
            return (trap, cheese)

def sound_horn(trap, cheese):
    """Function that starts the hunt depending on the user's input.

    Parameters
    ----------
    trap : str
        stores "High Strain Steel Trap", "Hot Tub Trap" or in case
        of the user having no trap, stores "Cardboard and Hook Trap"

    cheese : int
        stores the number of cheddar the player has

    Returns
    -------
    tuple
        user's trap and cheese quantity by the end of the function

    """
    print('Sound the horn to call for the mouse...')
    user_horn = input('Sound the horn by typing "yes": ')

    if user_horn.lower().strip() == 'yes':
        if trap == "High Strain Steel Trap" or "Hot Tub Trap" and cheese == 1:
            cheese -= 1
            print('Caught a Brown mouse!')
            print('Congratulations. Ye have completed the training.')
            print('Good luck~')
            return (trap, cheese)

        elif trap == "Cardboard and Hook Trap" and cheese == 0:
            print('Nothing happens.\nTo catch a mouse, you need both trap and cheese!')
            return (trap, cheese)

    elif user_horn.lower().strip() != 'yes':
        if trap == "High Strain Steel Trap" or "Hot Tub Trap" and cheese == 1:
            cheese -= 1
            print('Nothing happens.\nTo catch a mouse, you need both trap and cheese!')
            return (trap, cheese)

        elif trap == "Cardboard and Hook Trap":
            print('Nothing happens.')
            return (trap, cheese)

    # else statement in case of any other input
    else:
        print('Nothing happens.')
        return (trap, cheese)

def main():
    intro()
    travel_to_camp()
    # store trap and cheese to both setup_trap() and sound_horn() to update
    trap, cheese = setup_trap()
    trap, cheese = sound_horn(trap, cheese)

if __name__ == "__main__":
     main()


std = ["NA", "MMYY", "YYMM", "AMBIGUOUS"]
for t in range(1, int(input())+1):
    N = list(input())
    a, b = int("".join(N[:2])), int("".join(N[2:]))

    res = 0
    #MMYY
    if 1 < a < 13:
        res += 1

    #YYMM
    if 1 < b < 13:
        res += 2

    print("#{} {}".format(t, std[res]))


import asyncio
import json
from pathlib import Path
from types import SimpleNamespace
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Union, cast

from playwright._impl._api_structures import (
    Cookie,
    Geolocation,
    SetCookieParam,
    StorageState,
)
from playwright._impl._api_types import Error
from playwright._impl._artifact import Artifact
from playwright._impl._cdp_session import CDPSession
from playwright._impl._connection import (
    ChannelOwner,
    from_channel,
    from_nullable_channel,
)
from playwright._impl._event_context_manager import EventContextManagerImpl
from playwright._impl._fetch import APIRequestContext
from playwright._impl._frame import Frame
from playwright._impl._helper import (
    RouteHandler,
    RouteHandlerCallback,
    TimeoutSettings,
    URLMatch,
    URLMatcher,
    async_readfile,
    async_writefile,
    is_safe_close_error,
    locals_to_params,
    to_impl,
)
from playwright._impl._network import Request, Response, Route, serialize_headers
from playwright._impl._page import BindingCall, Page, Worker
from playwright._impl._tracing import Tracing
from playwright._impl._wait_helper import WaitHelper

if TYPE_CHECKING:  # pragma: no cover
    from playwright._impl._browser import Browser

class BrowserContext(ChannelOwner):

    Events = SimpleNamespace(
        BackgroundPage="backgroundpage",
        Close="close",
        Page="page",
        ServiceWorker="serviceworker",
        Request="request",
        Response="response",
        RequestFailed="requestfailed",
        RequestFinished="requestfinished",
    )

    def __init__(
        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
    ) -> None:
        super().__init__(parent, type, guid, initializer)
        self._pages: List[Page] = []
        self._routes: List[RouteHandler] = []
        self._bindings: Dict[str, Any] = {}
        self._timeout_settings = TimeoutSettings(None)
        self._browser: Optional["Browser"] = None
        self._owner_page: Optional[Page] = None
        self._options: Dict[str, Any] = {}
        self._background_pages: Set[Page] = set()
        self._service_workers: Set[Worker] = set()
        self._tracing = cast(Tracing, from_channel(initializer["tracing"]))
        self._request: APIRequestContext = from_channel(
            initializer["APIRequestContext"]
        )
        self._channel.on(
            "bindingCall",
            lambda params: self._on_binding(from_channel(params["binding"])),
        )
        self._channel.on("close", lambda _: self._on_close())
        self._channel.on(
            "page", lambda params: self._on_page(from_channel(params["page"]))
        )
        self._channel.on(
            "route",
            lambda params: self._on_route(
                from_channel(params.get("route")), from_channel(params.get("request"))
            ),
        )

        self._channel.on(
            "backgroundPage",
            lambda params: self._on_background_page(from_channel(params["page"])),
        )

        self._channel.on(
            "serviceWorker",
            lambda params: self._on_service_worker(from_channel(params["worker"])),
        )
        self._channel.on(
            "request",
            lambda params: self._on_request(
                from_channel(params["request"]),
                from_nullable_channel(params.get("page")),
            ),
        )
        self._channel.on(
            "response",
            lambda params: self._on_response(
                from_channel(params["response"]),
                from_nullable_channel(params.get("page")),
            ),
        )
        self._channel.on(
            "requestFailed",
            lambda params: self._on_request_failed(
                from_channel(params["request"]),
                params["responseEndTiming"],
                params.get("failureText"),
                from_nullable_channel(params.get("page")),
            ),
        )
        self._channel.on(
            "requestFinished",
            lambda params: self._on_request_finished(
                from_channel(params["request"]),
                from_nullable_channel(params.get("response")),
                params["responseEndTiming"],
                from_nullable_channel(params.get("page")),
            ),
        )
        self._closed_future: asyncio.Future = asyncio.Future()
        self.once(
            self.Events.Close, lambda context: self._closed_future.set_result(True)
        )

    def __repr__(self) -> str:
        return f"<BrowserContext browser={self.browser}>"

    def _on_page(self, page: Page) -> None:
        self._pages.append(page)
        self.emit(BrowserContext.Events.Page, page)
        if page._opener and not page._opener.is_closed():
            page._opener.emit(Page.Events.Popup, page)

    def _on_route(self, route: Route, request: Request) -> None:
        for handler_entry in self._routes:
            if handler_entry.matches(request.url):
                try:
                    handler_entry.handle(route, request)
                finally:
                    if not handler_entry.is_active:
                        self._routes.remove(handler_entry)
                        if not len(self._routes) == 0:
                            asyncio.create_task(self._disable_interception())
                break
        route._internal_continue()

    def _on_binding(self, binding_call: BindingCall) -> None:
        func = self._bindings.get(binding_call._initializer["name"])
        if func is None:
            return
        asyncio.create_task(binding_call.call(func))

    def set_default_navigation_timeout(self, timeout: float) -> None:
        self._timeout_settings.set_navigation_timeout(timeout)
        self._channel.send_no_reply(
            "setDefaultNavigationTimeoutNoReply", dict(timeout=timeout)
        )

    def set_default_timeout(self, timeout: float) -> None:
        self._timeout_settings.set_timeout(timeout)
        self._channel.send_no_reply("setDefaultTimeoutNoReply", dict(timeout=timeout))

    @property
    def pages(self) -> List[Page]:
        return self._pages.copy()

    @property
    def browser(self) -> Optional["Browser"]:
        return self._browser

    async def new_page(self) -> Page:
        if self._owner_page:
            raise Error("Please use browser.new_context()")
        return from_channel(await self._channel.send("newPage"))

    async def cookies(self, urls: Union[str, List[str]] = None) -> List[Cookie]:
        if urls is None:
            urls = []
        if not isinstance(urls, list):
            urls = [urls]
        return await self._channel.send("cookies", dict(urls=urls))

    async def add_cookies(self, cookies: List[SetCookieParam]) -> None:
        await self._channel.send("addCookies", dict(cookies=cookies))

    async def clear_cookies(self) -> None:
        await self._channel.send("clearCookies")

    async def grant_permissions(
        self, permissions: List[str], origin: str = None
    ) -> None:
        await self._channel.send("grantPermissions", locals_to_params(locals()))

    async def clear_permissions(self) -> None:
        await self._channel.send("clearPermissions")

    async def set_geolocation(self, geolocation: Geolocation = None) -> None:
        await self._channel.send("setGeolocation", locals_to_params(locals()))

    async def set_extra_http_headers(self, headers: Dict[str, str]) -> None:
        await self._channel.send(
            "setExtraHTTPHeaders", dict(headers=serialize_headers(headers))
        )

    async def set_offline(self, offline: bool) -> None:
        await self._channel.send("setOffline", dict(offline=offline))

    async def add_init_script(
        self, script: str = None, path: Union[str, Path] = None
    ) -> None:
        if path:
            script = (await async_readfile(path)).decode()
        if not isinstance(script, str):
            raise Error("Either path or script parameter must be specified")
        await self._channel.send("addInitScript", dict(source=script))

    async def expose_binding(
        self, name: str, callback: Callable, handle: bool = None
    ) -> None:
        for page in self._pages:
            if name in page._bindings:
                raise Error(
                    f'Function "{name}" has been already registered in one of the pages'
                )
        if name in self._bindings:
            raise Error(f'Function "{name}" has been already registered')
        self._bindings[name] = callback
        await self._channel.send(
            "exposeBinding", dict(name=name, needsHandle=handle or False)
        )

    async def expose_function(self, name: str, callback: Callable) -> None:
        await self.expose_binding(name, lambda source, *args: callback(*args))

    async def route(
        self, url: URLMatch, handler: RouteHandlerCallback, times: int = None
    ) -> None:
        self._routes.insert(
            0,
            RouteHandler(URLMatcher(self._options.get("baseURL"), url), handler, times),
        )
        if len(self._routes) == 1:
            await self._channel.send(
                "setNetworkInterceptionEnabled", dict(enabled=True)
            )

    async def unroute(
        self, url: URLMatch, handler: Optional[RouteHandlerCallback] = None
    ) -> None:
        self._routes = list(
            filter(
                lambda r: r.matcher.match != url or (handler and r.handler != handler),
                self._routes,
            )
        )
        if len(self._routes) == 0:
            await self._disable_interception()

    async def _disable_interception(self) -> None:
        await self._channel.send("setNetworkInterceptionEnabled", dict(enabled=False))

    def expect_event(
        self,
        event: str,
        predicate: Callable = None,
        timeout: float = None,
    ) -> EventContextManagerImpl:
        if timeout is None:
            timeout = self._timeout_settings.timeout()
        wait_helper = WaitHelper(self, f"browser_context.expect_event({event})")
        wait_helper.reject_on_timeout(
            timeout, f'Timeout {timeout}ms exceeded while waiting for event "{event}"'
        )
        if event != BrowserContext.Events.Close:
            wait_helper.reject_on_event(
                self, BrowserContext.Events.Close, Error("Context closed")
            )
        wait_helper.wait_for_event(self, event, predicate)
        return EventContextManagerImpl(wait_helper.result())

    def _on_close(self) -> None:
        if self._browser:
            self._browser._contexts.remove(self)

        self.emit(BrowserContext.Events.Close, self)

    async def close(self) -> None:
        try:
            if self._options.get("recordHar"):
                har = cast(
                    Artifact, from_channel(await self._channel.send("harExport"))
                )
                await har.save_as(
                    cast(Dict[str, str], self._options["recordHar"])["path"]
                )
                await har.delete()
            await self._channel.send("close")
            await self._closed_future
        except Exception as e:
            if not is_safe_close_error(e):
                raise e

    async def _pause(self) -> None:
        await self._channel.send("pause")

    async def storage_state(self, path: Union[str, Path] = None) -> StorageState:
        result = await self._channel.send_return_as_dict("storageState")
        if path:
            await async_writefile(path, json.dumps(result))
        return result

    async def wait_for_event(
        self, event: str, predicate: Callable = None, timeout: float = None
    ) -> Any:
        async with self.expect_event(event, predicate, timeout) as event_info:
            pass
        return await event_info

    def expect_page(
        self,
        predicate: Callable[[Page], bool] = None,
        timeout: float = None,
    ) -> EventContextManagerImpl[Page]:
        return self.expect_event(BrowserContext.Events.Page, predicate, timeout)

    def _on_background_page(self, page: Page) -> None:
        self._background_pages.add(page)
        self.emit(BrowserContext.Events.BackgroundPage, page)

    def _on_service_worker(self, worker: Worker) -> None:
        worker._context = self
        self._service_workers.add(worker)
        self.emit(BrowserContext.Events.ServiceWorker, worker)

    def _on_request_failed(
        self,
        request: Request,
        response_end_timing: float,
        failure_text: Optional[str],
        page: Optional[Page],
    ) -> None:
        request._failure_text = failure_text
        if request._timing:
            request._timing["responseEnd"] = response_end_timing
        self.emit(BrowserContext.Events.RequestFailed, request)
        if page:
            page.emit(Page.Events.RequestFailed, request)

    def _on_request_finished(
        self,
        request: Request,
        response: Optional[Response],
        response_end_timing: float,
        page: Optional[Page],
    ) -> None:
        if request._timing:
            request._timing["responseEnd"] = response_end_timing
        self.emit(BrowserContext.Events.RequestFinished, request)
        if page:
            page.emit(Page.Events.RequestFinished, request)
        if response:
            response._finished_future.set_result(True)

    def _on_request(self, request: Request, page: Optional[Page]) -> None:
        self.emit(BrowserContext.Events.Request, request)
        if page:
            page.emit(Page.Events.Request, request)

    def _on_response(self, response: Response, page: Optional[Page]) -> None:
        self.emit(BrowserContext.Events.Response, response)
        if page:
            page.emit(Page.Events.Response, response)

    @property
    def background_pages(self) -> List[Page]:
        return list(self._background_pages)

    @property
    def service_workers(self) -> List[Worker]:
        return list(self._service_workers)

    async def new_cdp_session(self, page: Union[Page, Frame]) -> CDPSession:
        page = to_impl(page)
        params = {}
        if isinstance(page, Page):
            params["page"] = page._channel
        elif isinstance(page, Frame):
            params["frame"] = page._channel
        else:
            raise Error("page: expected Page or Frame")
        return from_channel(await self._channel.send("newCDPSession", params))

    @property
    def tracing(self) -> Tracing:
        return self._tracing

    @property
    def request(self) -> "APIRequestContext":
        return self._request


from django.test import TestCase
from django.test import Client
from django.urls import reverse
from inventory.tests.factories import (
    StyleVersionFactory,
    UserFactory
)
from inventory.tests.functions import login_as

class TestActivateTheme(TestCase):
    view_name = "activate_theme"

    def setUp(self):
        self.client = Client()
        self.user = UserFactory()
        self.version = StyleVersionFactory()
        self.active_version = StyleVersionFactory(currently_live=True,
                                                  currently_test=True)
        self.url = reverse(
            self.view_name,
            urlconf="inventory.urls",
            args=[self.version.pk, "live"])
        self.return_url = reverse("themes_list", urlconf="inventory.urls")
        self.title = "List of Themes and Versions"

    def test_no_login(self):
        response = self.client.get(self.url)
        self.assertRedirects(response,
                             "/login/?next=%s" % self.url,
                             fetch_redirect_response=False)

    def test_activate_live(self):
        login_as(self.user, self)
        response = self.client.get(self.url, follow=True)
        self.assertRedirects(response, "%s?changed_id=%d" % (
            self.return_url,
            self.version.pk))
        self.assertContains(response, self.title)
        self.assertContains(response, "Activated %s on %s" % (
            self.version,
            "live"))
        self.assertContains(
            response,
            '' % reverse(
                self.view_name,
                urlconf="inventory.urls",
                args=[self.active_version.pk, "live"]))
        self.assertNotContains(
            response,
            '' % reverse(
                self.view_name,
                urlconf="inventory.urls",
                args=[self.active_version.pk, "test"]))

    def test_activate_test(self):
        login_as(self.user, self)
        self.url = reverse(
            self.view_name,
            urlconf="inventory.urls",
            args=[self.version.pk, "test"])
        response = self.client.get(self.url, follow=True)
        self.assertRedirects(response, "%s?changed_id=%d" % (
            self.return_url,
            self.version.pk))
        self.assertContains(response, self.title)
        self.assertContains(response, "Activated %s on %s" % (
            self.version,
            "test"))
        self.assertContains(
            response,
            '' % reverse(
                self.view_name,
                urlconf="inventory.urls",
                args=[self.active_version.pk, "test"]))
        self.assertNotContains(
            response,
            '' % reverse(
                self.view_name,
                urlconf="inventory.urls",
                args=[self.active_version.pk, "live"]))

    def test_activate_bad_id(self):
        login_as(self.user, self)
        response = self.client.get(reverse(
            self.view_name,
            urlconf="inventory.urls",
            args=[self.active_version.pk+1, "live"]))
        self.assertEqual(404, response.status_code)

    def test_activate_bad_system(self):
        login_as(self.user, self)
        response = self.client.get(reverse(
            self.view_name,
            urlconf="inventory.urls",
            args=[self.version.pk, "bad"]))
        self.assertEqual(404, response.status_code)