# -*- coding: utf-8 -*-

import math
import sys
from typing import Dict

import maths.lib as mlib
from maths.parser import *
from util.log import Logger
from util.math import *

DEBUG = False

class Evaluator:
    def __init__(self, strict=False):
        self.frames = [{}]
        self.arguments = None
        self.beautified = None
        self.node_tree = None

        for name, item in mlib.__dict__.items():
            if isinstance(item, types.ModuleType):
                for member_name, member in item.__dict__.items():
                    if callable(member):  # if function
                        doc_func = mlib.find_function(member_name)

                        if doc_func:
                            member.doc_spec = doc_func

                        self.frames[0][member_name] = member
                    elif member_name.startswith("c_"):  # if constant
                        self.frames[0][member_name[2:]] = member

        for alias, func in mlib.docs.ext_aliases:
            self.frames[0][alias] = self.frames[0][func]

        self.log = Logger("Eval")
        self.strict_typing = strict

    def enter_frame(self, value: Dict[str, Any] = None):
        """Pushes the a new frame / the specified frame to the stack.

        value -- Frame (optional)"""
        self.frames.append(value or {})

    def exit_frame(self):
        """Pops the last frame from the stack."""
        return self.frames.pop()

    def set_variable(self, variable: str, value: Any, local=False):
        """Sets the value of the specified variable.

        variable -- Variable name
        value -- Variable value
        local -- If true, forces the creation of a new variable in the current frame. Otherwise, sets the value in the nearest frame containing the variable."""
        if not local:
            for frame in reversed(self.frames):
                if variable in frame:
                    frame[variable] = value
                    return

        self.frames[-1][variable] = value

    def get_variable(self, variable: str) -> Optional[Any]:
        """Returns the value of the specified variable.

        variable -- Variable name"""
        for frame in reversed(self.frames):
            if variable in frame:
                return frame[variable]

        self.log.error(translate("Evaluator", "Cannot find variable or function {name}").format(name=variable))
        return None

    def evaluate(self, expr: str) -> Optional[Any]:
        """Evaluates the specified expression.
        If an error occurs during the parsing, the result will be None.
        If an error occurs during the evaluation, the result will be undefined."""
        parser = Parser(expr)
        parser.log = self.log
        node = None

        try:
            node = parser.parse()
        except:
            if DEBUG:
                raise
            self.log.error(translate("Evaluator", "Parser: ") + str(sys.exc_info()[1]))

        self.beautified = parser.beautify()

        if not node:
            return None

        return self.evaluate_parsed(node)

    def evaluate_parsed(self, node: nodes.AstNode):
        """Evaluates the specified root node.

        node -- Node to be evaluated"""
        self.node_tree = node
        self.beautified = self.node_tree.code()

        result = None

        try:
            result = self.eval_node(self.node_tree)
        except:
            if DEBUG:
                raise
            self.log.error(str(sys.exc_info()[1]))

        return result

    def eval_node(self, node: nodes.AstNode):
        """Evaluates the specified node.
        Basically just a wrapper for eval_node_real that handles all the IEEE754 floating point rounding fuckery."""
        value = self.eval_node_real(node)

        if value is not None and is_num(value) and not isinstance(value, bool):
            if isinstance(value, complex):
                # if real, convert to float directly
                if is_real(value):
                    value = value.real

            # only convert to int if it fits well
            if is_int(value) and 1 <= abs(value) <= 1e15:
                value = int(round(value))

            # if zero, use zero directly
            if is_zero(value):
                value = 0
            else:
                if not (type(value) == int and value != int(float(value))):
                    value = close_round(value, 12)

        return value

    def call_lambda(self, node: nodes.LambdaNode, *args):
        """Calls the specified lambda function.

        node -- Function node
        args -- Argument list"""
        args = list(args)

        if len(args) != len(node.args):
            self.log.error(
                translate("Evaluator", "Argument count mismatch (expected {exp}, got {act})").format(exp=len(node.args),
                                                                                act=len(args)))
            return None

        frame = {node.args[idx]: arg for idx, arg in enumerate(args)}

        self.enter_frame(frame)

        result = self.eval_node(node.expr)

        if callable(result):
            if not hasattr(result, "frames"):
                result.frames = []

            result.frames.append(frame.copy())

        # pop arguments after use
        self.exit_frame()

        return result

    def eval_node_real(self, node: nodes.AstNode):
        """Evaluates the specified node.

        node -- Node to be evaluated"""
        if isinstance(node, nodes.ListNode):
            return [self.eval_node(x) for x in node.value]

        if isinstance(node, (nodes.NumberNode, nodes.StringNode)):
            return node.value

        if isinstance(node, nodes.IdentifierNode):
            return self.get_variable(node.value)

        if isinstance(node, nodes.UnaryOpNode):
            return self.eval_unary(node)

        if isinstance(node, nodes.BinOpNode):
            return self.eval_binary(node)

        if isinstance(node, nodes.CallNode):
            return self.eval_call(node)

        if isinstance(node, nodes.ArrayAccessNode):
            return self.eval_array_access(node)

        if isinstance(node, nodes.LambdaNode):
            return self.eval_lambda(node)

        # if the object is not a node, it must be a remnant of an already-parsed value
        # return it directly
        if not isinstance(node, nodes.AstNode):
            return node

        # if it's an unknown descendant of AstNode
        # this should never happen, but we put a message just in case
        self.log.error(translate("Evaluator", "Unknown node type: {type}").format(type=type(node)))
        return None

    def eval_lambda(self, node: nodes.LambdaNode):
        """Evaluates the specified lambda function node."""
        return lambda *args: self.call_lambda(node, *list(args))

    def eval_array_access(self, node: nodes.ArrayAccessNode):
        """Evaluates the specified array access node."""
        array = self.eval_node(node.array)
        index = int(self.eval_node(node.index))

        if not isinstance(array, (list, str)):
            self.log.error(translate("Evaluator", "Array access target must be of array type"))
            return None

        if index < len(array):
            return array[index]
        else:
            self.log.error(translate("Evaluator", "Index '{idx}' too big for array").format(idx=index))
            return None

    def eval_call(self, node: nodes.CallNode):
        """Evaluates the specified function call node."""
        function = self.eval_node(node.func)

        if function is None:
            self.log.error(translate("Evaluator", "Callee is None"))
            return None

        if (len(node.args) == 1
                and isinstance(node.args[0], nodes.UnaryOpNode)
                and node.args[0].operator == "*"):
            # expand list of arguments
            arg_list = self.eval_node(node.args[0].value)

            if type(arg_list) != list:
                self.log.error(translate("Evaluator", "Only lists can be expanded"))
                return None

            args = arg_list
        else:
            args = [self.eval_node(x) for x in node.args]

        if hasattr(function, "doc_spec") and not hasattr(function, "listfunc"):
            arg_spec = function.doc_spec[1][1]
            num_opt = sum(1 for arg in arg_spec if len(arg) >= 4)

            if not (len(arg_spec) - num_opt <= len(args) <= len(arg_spec)):
                self.log.error(translate("Evaluator", "Argument count mismatch (expected {exp}, got {act})").format(
                    exp=len(arg_spec) - num_opt, act=len(args)))
                return None

            for idx, (arg, spec) in enumerate(zip(args, arg_spec)):
                if not check_type(arg, spec[1]):
                    self.log.error(
                        translate("Evaluator", "Type mismatch for argument #{idx} '{arg}' (expected {exp})").format(
                            idx=idx + 1, arg=spec[0], exp=spec[1]))
                    return None

        if hasattr(function, "frames"):
            for f in function.frames:
                self.enter_frame(f)

        result = function.__call__(*args)

        if hasattr(function, "frames"):
            for _ in function.frames:
                self.exit_frame()

        return result

    def eval_unary(self, node: nodes.UnaryOpNode):
        """Evaluates the specified unary operation node."""
        value = self.eval_node(node.value)
        value_type = ValueType.get_type(value)

        if node.operator == "+":
            return value

        if node.operator == "-" and (is_num(value) and (not self.strict_typing or not is_bool(value))):
            return -value

        if node.operator == "-" and value_type == ValueType.LIST:
            return value[::-1]

        if node.operator == "NOT" and (is_bool(value) or (not self.strict_typing and is_num(value))):
            return not value

        self.log.error(translate("Evaluator", "Invalid unary operator '{op}'").format(op=node.operator))
        return None

    def eval_binary(self, node: nodes.BinOpNode):
        """Evaluates the specified binary operation node.
        Wrapper for binary_operation."""
        return self.binary_operation(self.eval_node(node.left), self.eval_node(node.right), node.operator)

    def binary_operation(self, left, right, operator):
        """Evaluates the specified binary operation."""
        left_type = ValueType.get_type(left)
        right_type = ValueType.get_type(right)

        if left is None or right is None:
            self.log.error(translate("Evaluator", "Trying to use None"))
            return None

        if operator == "+" and ValueType.STRING in [left_type, right_type]:
            # if one of the operands is a string
            return str(left) + str(right)

        if operator in ["*"] and right_type == ValueType.LIST and left_type != ValueType.LIST:
            # if one operand is list and not the other, then put the list at left
            # so we don't have to handle both cases afterwards
            (left, left_type, right, right_type) = (right, right_type, left, left_type)

        result = None

        if self.strict_typing:
            if left_type != right_type:
                self.log.error(
                    translate("Evaluator", "Type mismatch: operands have different types ({left} and {right})").format(
                        left=ValueType.get_name(left_type), right=ValueType.get_name(right_type)))
                return None

            if left_type == ValueType.BOOLEAN:
                allowed = Operators.boolean
            elif left_type == ValueType.NUMBER:
                allowed = Operators.math + Operators.comp
            elif left_type == ValueType.STRING:
                allowed = Operators.eq + ["+"]
            elif left_type == ValueType.LIST:
                allowed = Operators.eq + ["+", "-", "&", "|"]
            else:
                error_pos = []

                if left_type is None:
                    error_pos.append(translate("Evaluator", "left"))

                if right_type is None:
                    error_pos.append(translate("Evaluator", "right"))

                self.log.error(translate("Evaluator", "Invalid value type for {val} and operator '{op}'").format(
                    val=translate("Evaluator", " and ").join(error_pos), op=operator))
                return None

            if operator not in allowed:
                self.log.error(
                    translate("Evaluator", "Operator '{op}' not allowed for value type {type}").format(
                        op=operator, type=ValueType.get_name(left_type)))
                return None

        # arithmetic
        if operator == "+":
            result = left + right
        elif operator == "-":
            if left_type == right_type == ValueType.LIST:
                result = [x for x in left if x not in right]
            else:
                result = left - right
        elif operator == "*":
            if left_type == ValueType.LIST:
                if not is_int(right):
                    self.log.error(
                        translate("Evaluator", "Trying to multiply List by non-integer ({val})").format(val=right))
                    return None
                else:
                    result = left * int(right)
            else:
                result = left * right
        elif operator == "/":
            if right == 0:
                self.log.error(translate("Evaluator", "Trying to divide by zero"))
                return None
            result = left / right
        elif operator == "%":
            result = math.fmod(left, right)
        elif operator in ["^", "**"]:
            result = left ** right

        # comparison
        elif operator == "<=":
            result = left <= right or is_close(left, right)
        elif operator == "<":
            result = left < right
        elif operator == ">":
            result = left > right
        elif operator == ">=":
            result = left >= right or is_close(left, right)

        # equality
        elif operator == "==":
            result = is_close(left, right)
        elif operator == "!=":
            result = not is_close(left, right)

        # logic / bitwise
        elif operator == "&":
            if left_type == right_type == ValueType.LIST:
                result = [x for x in left if x in right]
            else:
                result = int(left) & int(right)
        elif operator == "|":
            if left_type == right_type == ValueType.LIST:
                result = list(set(left + right))
            else:
                result = int(left) | int(right)
        elif operator == "XOR":
            if left_type == right_type == ValueType.LIST:
                result = list(set(x for x in left + right if x not in left or x not in right))
            else:
                result = int(left) ^ int(right)

        if result is None:
            self.log.error(
                translate("Evaluator", "Invalid binary operator '{op}' for '{left}' and '{right}'").format(op=operator,
                                                                                left=left,
                                                                                right=right))
        else:
            if is_bool(left) and is_bool(right):
                # if both operands are bool, then cast the whole thing to bool so it looks like we're professional
                result = bool(result)

        return result


#!/usr/bin/python
"""
Bar chart demo with pairs of bars grouped for easy comparison.
"""
import numpy as np
import matplotlib.pyplot as plt

n_groups = 7

means_men = (0.0, 16.0, 27.4, 60.8, 81.6, 87.3, 81.0)
#std_men = (2, 3, 4, 1, 2)

means_women = (0.0,3.3, 08.8, 6.3, 11.2, 5.4,4.5)

#women = (3, 5, 2, 3, 3)

fig, ax = plt.subplots()

index = np.arange(n_groups)
bar_width = 0.35

opacity = 0.4
#error_config = {'ecolor': '0.3'}

rects1 = plt.bar(index, means_men, bar_width,
                 alpha=opacity,
                 color='b',
                 #yerr=std_men,
                 #error_kw=error_config,
                 label='Pull')

rects2 = plt.bar(index + bar_width, means_women, bar_width,
                 alpha=opacity,
		 color='r',
		 #yerr=std_women,
		 #error_kw=error_config,
		 label='Push')

plt.xlabel('Number of Concurrent Downloaders')
plt.ylabel('Misses / Hits')
plt.title('Ratio of Misses to Hits')
plt.xticks(index + bar_width / 2, ('1', '2', '3', '4', '5', '6','7'))
plt.legend()

plt.tight_layout()
plt.show()


"""
httpserver部分主程序
获取http请求
解析http请求
将请求发送给WebFrame
从WebFrame接收反馈数据
将数据组织为Response格式发送给客户端
"""
from socket import *
from threading import Thread
from config import *
import re
import json

# 与后端应用交互
def connect_frame(env):
    """
    将请求发送给WebFrame
    从WebFrame接收反馈数据
    """
    s = socket()
    try:
        s.connect((frame_ip, frame_port))
    except Exception as e:
        print(e)
        return
    # 发送json请求
    data = json.dumps(env)
    s.send(data.encode())
    # 获取返回的数据 (json格式)
    data = s.recv(1024 * 1024 * 10).decode()
    if data:
        return json.loads(data)  # 返回字典

# 主体功能写入类
class HTTPServer:
    def __init__(self):
        self.address = (HOST, PORT)
        self.create_socket()
        self.bind()

    # 创建套接字
    def create_socket(self):
        self.sock = socket()
        self.sock.setsockopt(SOL_SOCKET,
                             SO_REUSEADDR,
                             DEBUG)

    # 绑定地址
    def bind(self):
        # 有些属性可以在调用函数时再生产
        self.host = HOST
        self.port = PORT
        self.sock.bind(self.address)

    # 服务启动函数 (多线程并发)
    def serve_forever(self):
        self.sock.listen(3)
        print("Listen the port %d" % self.port)
        while True:
            connfd, addr = self.sock.accept()
            print("Connect from", addr)
            t = Thread(target=self.handle,
                       args=(connfd,))
            t.setDaemon(True)
            t.start()

    # 处理具体的浏览器请求
    def handle(self, connfd):
        # http请求
        request = connfd.recv(4096).decode()
        pattern = r"(?P<method>[A-Z]+)\s+(?P<info>/\S*)"
        try:
            env = re.match(pattern, request).groupdict()
        except:
            connfd.close()
            return
        else:
            # 与webframe交互 (数据字典/None)
            data = connect_frame(env)
            if data:
                self.response(connfd, data)

    # 组织响应格式
    def response(self, connfd, data):
        # data->{'status':'200','data':'zzzz'}
        if data['status'] == '200':
            res="HTTP/1.1 200 OK\r\n"
            res+="Content-Type:text/html\r\n"
            res+="\r\n"
            res+=data['data']
        elif data['status'] == '404':
            res="HTTP/1.1 404 Not Found\r\n"
            res+="Content-Type:text/html\r\n"
            res+="\r\n"
            res+=data['data']
        connfd.send(res.encode()) # 响应给浏览器

if __name__ == '__main__':
    httpd = HTTPServer()
    httpd.serve_forever()  # 启动服务


import gym
from gym import wrappers
import numpy as np

import matplotlib.pyplot as plt

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.distributions import Categorical

from collections import deque
from actor_critic import PolicyModel as Policy

def reinforce(n_episodes=1000, max_time=1000, gamma=1.0, print_every=1000):
    env = gym.make("CartPole-v0")
    # env = wrappers.Monitor(env, "reinforce", force=True)
    env.seed(0)
    policy = Policy()
    optimizer = optim.Adam(policy.parameters(), lr=1e-2)
    scores_deque = deque(maxlen=100)
    scores = []
    for episode in range(1, n_episodes + 1):

        log_probs_tensor = []
        rewards = []

        state = env.reset()
        for t in range(max_time):
            action, log_prob = policy.choose_action(state)

            log_probs_tensor.append(log_prob)
            state, reward, done, _ = env.step(action)
            rewards.append(reward)
            if done:
                break

        scores_deque.append(sum(rewards))
        scores.append(sum(rewards))

        discounts = [gamma ** i for i in range(len(rewards) + 1)]
        R = sum([d * r for d, r in zip(discounts, rewards)])

        policy_loss = []
        for log_prob in log_probs_tensor:
            policy_loss.append(-log_prob * R)

        policy_loss = torch.cat(policy_loss).sum()

        optimizer.zero_grad()
        policy_loss.backward()
        optimizer.step()

        if episode % print_every == 0:
            print(
                "Episode {}\tAverage Score: {:.2f}".format(
                    episode, np.mean(scores_deque)
                )
            )
        # if np.mean(scores_deque) >= 195.0:
        #     print(
        #         "Environment solved in {:d} episodes!\tAverage Score: {:.2f}".format(
        #             episode - 100, np.mean(scores_deque)
        #         )
        #     )
            # break
    return scores


import os
from sqlalchemy import Column, String, Integer, create_engine
from flask_sqlalchemy import SQLAlchemy
import json

database_name = "postgres"
database_path = "postgres://{}@{}/{}".format('postgres:Liszt762!','localhost:5432', database_name)

db = SQLAlchemy()

def setup_db(app, database_path=database_path):
    app.config["SQLALCHEMY_DATABASE_URI"] = database_path
    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
    db.app = app
    db.init_app(app)
    db.create_all()
    return db

def migrations(app):
    migrate = Migrate(app, db)

class Actor(db.Model):
    __tablename__ = 'actors'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    age = Column(String, nullable=False)
    gender = Column(String, nullable=False)

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def insert(self):
        db.session.add(self)
        db.session.commit()

    def update(self):
        db.session.commit()

    def delete(self):
        db.session.delete(self)
        db.session.commit()

    def format(self):
        return ({
            'id': self.id,
            'name': self.name,
            'age': self.age,
            'gender': self.gender,

        })

    def __repr__(self):
        return f'Actor: {self.id}, {self.name}'

class Movie(db.Model):
    __tablename__ = 'movies'
    id = Column(db.Integer(), primary_key=True)
    title = Column(db.String(), nullable=False)
    release_date = Column(db.Date(), nullable=False)
    genre = Column(db.String(), nullable=False)

    def __init__(self, title, release_date):
        self.title = title
        self.release_date = release_date

    def insert(self):
        db.session.add(self)
        db.session.commit()

    def update(self):
        db.session.commit()

    def delete(self):
        db.session.delete(self)
        db.session.commit()

    def format(self):
        return ({
            'id': self.id,
            'title': self.title,
            'release_date': self.release_date.strftime("%m/%d/%y"),
            'genre': self.genre,
        })


# -*- coding: utf-8 -*-
"""
Created on Sat Apr 21 11:13:44 2018

@author: walke
"""

from selenium import webdriver
import requests
import pandas as pd
import time
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import re

def scroll_until_element_clicked_on(names1, REIT):
    for i in [10, 25, 50, 75, 100]:
        try:
            print(i)
            print(names1[REIT].text)
            names1[REIT].click()
            time.sleep(1)
            print('Breaking out of loop')
            break
        except:
            script = "window.scrollTo(0, " + str(i) + ")"
            driver.execute_script(script)
            names1 = driver.find_elements_by_class_name('name')
            time.sleep(1)

driver = webdriver.Firefox(executable_path=r'C:/geckodriver.exe')
driver.get('https://www.reit.com/investing/reit-directory?field_rtc_listing_status_tid_selective[]=524&field_address_country_selective[]=US&sort_by=field_stock_return_30_value')

skip_page_button = driver.find_element_by_xpath('/html/body/div[7]/div/div[5]/a[1]')
skip_page_button.click()
time.sleep(1)

df = pd.DataFrame({"Company_Name" : [],
                   "Ticker" : [],
                   "Address" : [],
                   "Sectors" : [],
                   "Description" : []
                   })

for page_num in range(0, 26):
    html = driver.page_source
    soup = BeautifulSoup(html, 'html.parser')

    names = driver.find_elements_by_class_name('name')
    comp_names = [e.text for e in names]
    tickers = driver.find_elements_by_class_name('ticker')
    tick_names = [e.text for e in tickers]
    addresses = driver.find_elements_by_class_name('address')
    address_name = [e.text for e in addresses]
    sectors = soup.find_all('div', attrs={'class':"right"})
    sect_name = [e.text.split('   ')[2] for e in sectors]

    print(len(df))
    descriptions = []
    for REIT in range(0, len(names)):
        try:
            names1 = driver.find_elements_by_class_name('name')
            scroll_until_element_clicked_on(names1, REIT)
            time.sleep(5)
            text = driver.find_element_by_class_name('investor__body').text
            descriptions.append(text)
            driver.back()
            time.sleep(4)
        except:
            driver.get('https://www.reit.com/investing/reit-directory?field_rtc_listing_status_tid_selective[]=524&field_address_country_selective[]=US&sort_by=field_stock_return_30_value')
            try:
                skip_page_button = driver.find_element_by_xpath('/html/body/div[7]/div/div[5]/a[1]')
                skip_page_button.click()
                time.sleep(1)
            except:
                print('')
            print(str(REIT) + "  Failed")
            descriptions.append("")
            for i in range(0, page_num):
                next_page_button = driver.find_element_by_xpath('//*[@title="Go to next page"]')
                next_page_button.click()
                time.sleep(3)

    df = df.append(pd.DataFrame({"Company_Name" : comp_names,
               "Ticker" : tick_names,
               "Address" : address_name,
               "Sector" : sect_name,
               "Description" : descriptions
               }))

    next_page_button = driver.find_element_by_xpath('//*[@title="Go to next page"]')
    next_page_button.click()
    time.sleep(5)

df.to_csv('REIT_data_all2.csv')


import urwid

class SplashText(urwid.Frame):
    def __init__(self, text, parent, new_on_return=True):
        self.new_on_return = new_on_return
        self.parent = parent
        bt = urwid.BigText(text, font=urwid.HalfBlock7x7Font())
        bt = urwid.Padding(bt, align='center', width='clip')
        bt = urwid.Filler(bt)
        bt = urwid.AttrMap(bt, 'bluescreen')
        return super(SplashText, self).__init__(body=bt)

    def selectable(self):
        return True

    def keypress(self, size, key):
        if key == 'esc':
            raise urwid.ExitMainLoop()
        # any keypress returns to main screen
        if self.new_on_return:
            self.parent.new_sbemail()
        self.parent.reassert()


import logging
from abc import (
    ABC,
    abstractmethod,
)
from collections import defaultdict
from typing import Optional

from reconcile.gql_definitions.common.ocm_environments import (
    query as ocm_environment_query,
)
from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
from reconcile.oum.metrics import (
    OCMUserManagementOrganizationActionCounter as ReconcileActionCounter,
)
from reconcile.oum.metrics import (
    OCMUserManagementOrganizationReconcileCounter as ReconcileCounter,
)
from reconcile.oum.metrics import (
    OCMUserManagementOrganizationReconcileErrorCounter as ReconcileErrorCounter,
)
from reconcile.oum.metrics import (
    OCMUserManagementOrganizationValidationErrorsGauge as ValidationErrorsGauge,
)
from reconcile.oum.models import (
    ClusterError,
    ClusterRoleReconcileResult,
    ClusterUserManagementSpec,
    OrganizationUserManagementConfiguration,
)
from reconcile.oum.providers import (
    GroupMemberProvider,
    init_ldap_group_member_provider,
)
from reconcile.utils import (
    gql,
    metrics,
)
from reconcile.utils.ocm.base import (
    CAPABILITY_MANAGE_CLUSTER_ADMIN,
    OCMClusterGroupId,
)
from reconcile.utils.ocm.cluster_groups import (
    add_user_to_cluster_group,
    delete_user_from_cluster_group,
    get_cluster_groups,
)
from reconcile.utils.ocm_base_client import (
    OCMBaseClient,
    init_ocm_base_client,
)
from reconcile.utils.runtime.integration import (
    PydanticRunParams,
    QontractReconcileIntegration,
)

class OCMUserManagementIntegrationParams(PydanticRunParams):
    ocm_environment: Optional[str] = None
    ocm_organization_ids: Optional[set[str]] = None
    group_provider_specs: list[str]

class OCMUserManagementIntegration(
    QontractReconcileIntegration[OCMUserManagementIntegrationParams], ABC
):
    def run(self, dry_run: bool) -> None:
        with metrics.transactional_metrics(self.name):
            # init group providers
            self.group_member_providers = get_group_providers(
                self.params.group_provider_specs
            )

            for ocm_env in self.get_ocm_environments():
                self.reconcile_ocm_environment(dry_run, ocm_env)

    @property
    def group_member_provider_ids(self) -> set[str]:
        return set(self.group_member_providers.keys())

    def get_ocm_environments(self) -> list[OCMEnvironment]:
        return ocm_environment_query(
            gql.get_api().query,
            variables={"name": self.params.ocm_environment}
            if self.params.ocm_environment
            else None,
        ).environments

    @abstractmethod
    def get_user_mgmt_config_for_ocm_env(
        self, ocm_env: OCMEnvironment, org_ids: Optional[set[str]]
    ) -> dict[str, OrganizationUserManagementConfiguration]:
        """
        Discover cluster user mgmt configurations in the given OCM environment.
        If org_ids are provided, only return configurations for the given organizations.

        To be implemented by subclasses.
        """

    def reconcile_ocm_environment(self, dry_run: bool, ocm_env: OCMEnvironment) -> None:
        """
        Processes user management configuration for all OCM organizations
        within the given OCM environment.
        """
        org_configs = self.get_user_mgmt_config_for_ocm_env(
            ocm_env, self.params.ocm_organization_ids
        )

        ocm_api = init_ocm_base_client(ocm_env, self.secret_reader)
        for org_id, org_config in org_configs.items():
            specs = build_specs_from_config(org_config, self.group_member_providers)
            self.reconcile_ocm_organization(
                dry_run, ocm_api, org_id, ocm_env.name, specs
            )

    def reconcile_ocm_organization(
        self,
        dry_run: bool,
        ocm_api: OCMBaseClient,
        org_id: str,
        ocm_env: str,
        cluster_specs: list[ClusterUserManagementSpec],
    ) -> None:
        """
        Process user management configuration for all clusters in the
        given OCM organization.
        """
        metrics.inc_counter(
            ReconcileCounter(
                integration=self.name,
                ocm_env=ocm_env,
                org_id=org_id,
            )
        )

        validation_errors = 0
        errors: list[Exception] = []
        for spec in cluster_specs:
            if spec.errors:
                validation_errors += 1
                validation_msg = "\n".join([m.message for m in spec.errors])
                self.signal_cluster_validation_error(
                    dry_run, ocm_api, spec, Exception(validation_msg)
                )
                continue
            reconcile_result = reconcile_cluster_roles(
                dry_run=dry_run,
                ocm_api=ocm_api,
                org_id=org_id,
                spec=spec,
            )

            # expose action metrics
            # we do this also in error situations to signal partial work
            # that has been done on a cluster
            metrics.inc_counter(
                ReconcileActionCounter(
                    integration=self.name,
                    ocm_env=ocm_env,
                    org_id=org_id,
                    action=ReconcileActionCounter.Action.AddUser,
                ),
                by=reconcile_result.users_added,
            )
            metrics.inc_counter(
                ReconcileActionCounter(
                    integration=self.name,
                    ocm_env=ocm_env,
                    org_id=org_id,
                    action=ReconcileActionCounter.Action.RemoveUser,
                ),
                by=reconcile_result.users_removed,
            )

            # signal errors
            if reconcile_result.error:
                self.signal_cluster_reconcile_error(
                    dry_run, ocm_api, spec, reconcile_result.error
                )
                errors.append(reconcile_result.error)

        # expose organization level metrics
        metrics.inc_counter(
            ReconcileErrorCounter(
                integration=self.name,
                ocm_env=ocm_env,
                org_id=org_id,
            ),
            by=1 if len(errors) > 0 else 0,
        )
        metrics.set_gauge(
            ValidationErrorsGauge(
                integration=self.name,
                ocm_env=ocm_env,
                org_id=org_id,
            ),
            validation_errors,
        )

    @abstractmethod
    def signal_cluster_reconcile_success(
        self,
        dry_run: bool,
        ocm_api: OCMBaseClient,
        spec: ClusterUserManagementSpec,
        message: str,
    ) -> None:
        """
        This method is called when the cluster reconcile operation was successful.
        """

    @abstractmethod
    def signal_cluster_validation_error(
        self,
        dry_run: bool,
        ocm_api: OCMBaseClient,
        spec: ClusterUserManagementSpec,
        error: Exception,
    ) -> None:
        """
        This method is called when the configuration for a cluster is invalid.

        If this method throws an exception, reconciliation on the rest of the clusters
        of an organization will stop.
        """

    @abstractmethod
    def signal_cluster_reconcile_error(
        self,
        dry_run: bool,
        ocm_api: OCMBaseClient,
        spec: ClusterUserManagementSpec,
        error: Exception,
    ) -> None:
        """
        This method is called when the cluster reconcile operation failed.

        If this method throws an exception, reconciliation on the rest of the clusters
        of an organization will stop.
        """

def reconcile_cluster_roles(
    dry_run: bool,
    ocm_api: OCMBaseClient,
    org_id: str,
    spec: ClusterUserManagementSpec,
) -> ClusterRoleReconcileResult:
    """
    Make sure that the cluster roles and given users are synced onto the cluster.
    This implies adding missing users and removing stale ones.
    Cluster roles not mentioned in the spec are not touched.
    """
    result = ClusterRoleReconcileResult()

    try:
        org_id = spec.cluster.organization_id
        cluster = spec.cluster.ocm_cluster
        desired_groups: dict[OCMClusterGroupId, set[str]] = spec.roles
        current_groups: dict[OCMClusterGroupId, set[str]] = {
            role_id: {u.id for u in group.users.items}
            for role_id, group in get_cluster_groups(
                ocm_api=ocm_api, cluster_id=cluster.id
            ).items()
            if group.users
        }
        # only process roles present in the desired state and ignore the ones that are not
        # this way a role can still be managed manually while another one is managed by this integration
        for group in desired_groups.keys():
            desired_members = desired_groups.get(group, set())
            current_members = current_groups.get(group, set())

            # add missing users
            for missing_user in desired_members - current_members:
                logging.info(
                    f"add user {missing_user} to {group.value} - cluster id={cluster.id}, name={cluster.name}, org={org_id}"
                )
                if not dry_run:
                    add_user_to_cluster_group(
                        ocm_api=ocm_api,
                        cluster_id=cluster.id,
                        user_name=missing_user,
                        group=group,
                    )
                result.users_added += 1
            # remove obsolete users
            for obsolete_user in current_members - desired_members:
                logging.info(
                    f"remove user {obsolete_user} from {group.value} -  cluster id={cluster.id}, name={cluster.name}, org={org_id}"
                )
                if not dry_run:
                    delete_user_from_cluster_group(
                        ocm_api=ocm_api,
                        cluster_id=cluster.id,
                        user_name=obsolete_user,
                        group=group,
                    )
                result.users_removed += 1
    except Exception as e:
        result.error = e

    return result

def build_specs_from_config(
    org_config: OrganizationUserManagementConfiguration,
    group_member_providers: dict[str, GroupMemberProvider],
) -> list[ClusterUserManagementSpec]:
    """
    Transforms the external provider/group references into actual user names.
    """
    # collect external group refs by provider so we can do a bulk resolve
    external_group_refs_by_provider: dict[str, set[str]] = defaultdict(set)
    for cluster_config in org_config.cluster_configs:
        for role_external_group_ref in cluster_config.roles.values():
            for external_group_ref in role_external_group_ref:
                external_group_refs_by_provider[external_group_ref.provider].add(
                    external_group_ref.group_id
                )

    # resolve all groups using the provider implementations
    external_group_members_by_provider: dict[str, dict[str, set[str]]] = {}
    for provider, group_ids in external_group_refs_by_provider.items():
        external_group_members_by_provider[provider] = group_member_providers[
            provider
        ].resolve_groups(group_ids)

    # build specs
    cluster_specs: list[ClusterUserManagementSpec] = []
    for cluster_config in org_config.cluster_configs:
        spec = ClusterUserManagementSpec(
            cluster=cluster_config.cluster,
            roles={},
            errors=cluster_config.errors,
        )
        cluster_specs.append(spec)

        # fill roles
        for (
            role_id,
            external_group_refs,
        ) in cluster_config.roles.items():
            spec.roles[role_id] = set()
            if (
                cluster_config.cluster.ocm_cluster.is_osd()
                and role_id == OCMClusterGroupId.CLUSTER_ADMINS
                and not cluster_config.cluster.is_capability_set(
                    CAPABILITY_MANAGE_CLUSTER_ADMIN, "true"
                )
            ):
                spec.errors.append(
                    ClusterError(
                        message="This cluster does not have the capability to manage the cluster-admins role. Go to https://red.ht/ohss-incident and request cluster-admin access."
                    )
                )
            for group_ref in external_group_refs:
                members = external_group_members_by_provider[group_ref.provider].get(
                    group_ref.group_id
                )
                if members is None:
                    spec.errors.append(
                        ClusterError(
                            message=f"{group_ref.provider} group {group_ref.group_id} for {role_id.value} not found"
                        )
                    )
                    continue
                spec.roles[role_id].update(members)

            if spec.errors:
                spec.roles = {}

    return cluster_specs

def get_group_providers(
    group_provider_specs: list[str],
) -> dict[str, GroupMemberProvider]:
    """
    Initialize the group member providers.
    """
    providers: dict[str, GroupMemberProvider] = {}
    for provider_spec in group_provider_specs:
        provider_name, provider_type, provider_args = provider_spec.split(":")
        if provider_type == "ldap":
            providers[provider_name] = init_ldap_group_member_provider(provider_args)
        else:
            raise ValueError(f"unknown group member provider type {provider_type}")
    return providers


from Tree import *
#from gp import MUTATIONPERCENT, CROSSOVERPERCENT
import random
from GeneticOperators import mutation, crossover
import copy
import multiprocessing as mp
import itertools

MUTATIONPERCENT = 0.05      # probabilidade de mutacao
CROSSOVERPERCENT = 0.95     # probabilidade de crossover

def Generate_Offsprings(parents, popsize):
    #start_time = time.time()
    pool = mp.Pool(processes=5)
    pop_per_process = int(popsize/5)
    offsprings =  pool.starmap(apply_operators, ((parents, pop_per_process, []), (parents, pop_per_process, []),
                                                 (parents,pop_per_process, []), (parents,pop_per_process, []), (parents,pop_per_process, [])))

    pool.close ()
    pool.join()
    offsprings = list(itertools.chain.from_iterable(offsprings))
    #print("--- %s seconds1 ---" % (time.time() - start_time))
    offsprings = sorted(offsprings, key=lambda x: x[1])

    return offsprings

def apply_operators(parents, popsize, offspring):
    appendof = offspring.append
    while len(offspring) < popsize:
        if len(parents) >= 2:
            if random.random() <= MUTATIONPERCENT:
                p = parents[random.randint(0, len(parents) - 1)]
                parent = copy.deepcopy(p)
                mutation(parent)
                appendof(p)
            else:
                p1 = parents[random.randint(0, len(parents) - 1)]
                parent1 = copy.deepcopy(p1)
                p2 = parents[random.randint(0, len(parents) - 1)]
                parent2 = copy.deepcopy(p2)
                crossover(parent1, parent2)
                appendof(parent1)
                appendof(parent2)
        else:
            p = parents[random.randint(0, len(parents) - 1)]
            parent = copy.deepcopy(p)
            mutation(parent)
            appendof(p)
    offspring = sorted(offspring, key=lambda x: x.fit)
    return offspring


'''
SpaceShip Logo Image :- Icons made by Skyclick from  www.flaticon.com
Spaceship in game :- Icons made by Freepik from  www.flaticon.com
EnemyShip in game :- Icons made by Smashicons from  www.flaticon.com
Background Image :- Background vector created by pikisuperstar - www.freepik.com
Bullet Image :- Icons made by Those Icons from  www.flaticon.com

'''

import pygame
from pygame import mixer

import random
import math

# Initialize
pygame.init()

# Create Screen
# (Width, Height)
# (0, 0) => top-left of the Window || (800, 600) => bottom-right
screen = pygame.display.set_mode((800, 600))

# Title and Window
pygame.display.set_caption("Space Invader")
icon = pygame.image.load("spaceship.png")
pygame.display.set_icon(icon)

# background
backgroundImg = pygame.image.load("background.png")

# music
mixer.music.load("music/background.wav")
mixer.music.play(-1)

# Player
playerImg = pygame.image.load("space-invaders.png")
playerX = 370
playerY = 480
playerX_change = 0
playerY_change = 0

# Enemy
enemyImg = []
enemyX, enemyY = [], []
enemyX_change, enemyY_change = [], []
num_of_enemies = 4

for i in range(num_of_enemies):
    enemyImg.append(pygame.image.load("invader2.png"))
    enemyX.append(random.randint(0, 800))
    enemyY.append(random.randint(50, 150))
    enemyX_change.append(4)
    enemyY_change.append(20)

# bullet
bulletImg = pygame.image.load("bullet.png")
bulletX, bulletY = 0, 480
bulletX_change, bulletY_change = 3, 10
# READY state means is not visible
# FIRE state means Bullet is moving
bullet_state = 'ready'

# score
score_value = 0
font = pygame.font.Font('freesansbold.ttf', 16)

textX = 10
textY = 10

#  Game Over
gameOver = pygame.font.Font("freesansbold.ttf", 64)

def game_over_text():
    over = gameOver.render("Game Over", True, (255, 255, 255))
    screen.blit(over, (225, 200))

def show_score(textX, textY):
    score = font.render("Score :" + str(score_value), True, (255, 255, 255))
    screen.blit(score, (textX, textY))

# functions
def player(X, Y):
    screen.blit(playerImg, (X, Y))

def enemy(X_e, Y_e, i):
    screen.blit(enemyImg[i], (X_e, Y_e))

def fire_bullet(X, Y):
    global bullet_state
    bullet_state = "fire"
    screen.blit(bulletImg, (X + 16, Y + 10))

def isCollision(bulletX, bulletY, enemyX, enemyY):
    distance = math.sqrt((bulletX - enemyX)**2 + (bulletY - enemyY)**2)
    if distance <= 27:
        return True
    return False

# Game Loop
running = True

score = 0
while running:
    # screen color will update
    screen.fill((255, 255, 255))
    # background image
    screen.blit(backgroundImg, (0, 0))

    for event in pygame.event.get():

        if event.type == pygame.QUIT:  # or event.type == (pygame.K_AT + pygame.K_F4):
            running = False
        # main character movement
        if event.type == pygame.KEYDOWN:
            print("Key Stroke is PRESSED")
            if event.key == pygame.K_LEFT:
                playerX_change = -5

            if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                playerX_change = 5
                print("RIGHT is pressed")

            if event.key == pygame.K_SPACE:
                if bullet_state is 'ready':
                    bullet_sound = mixer.Sound("music/laser.wav")
                    bullet_sound.play()
                    bulletX = playerX
                    fire_bullet(bulletX, bulletY)

        if event.type == pygame.KEYUP:
            playerY_change = 0
            playerX_change = 0
            print("KEY Stroke is RELEASED")

    # player() function will bw called to draw PlayerImg on Canvas
    playerX += playerX_change
    playerY += playerY_change

    # setting boundries
    # images are considered from (0, 0), not from centre.
    if playerX <= 0:
        playerX = 0
    elif playerX >= 736:
        playerX = 736

    if playerY <= 0:
        playerY = 0
    elif playerY >= 536:
        playerY = 536

    for i in range(num_of_enemies):
        # Game Over
        if enemyY[i] > 440:
            for j in range(num_of_enemies):
                enemyY[i] = 1000
            game_over_text()
            break

        enemyX[i] += enemyX_change[i]
        if enemyX[i] <= 0:
            enemyX[i] = 0
            enemyX_change[i] = 3
            enemyY[i] += enemyY_change[i]
        elif enemyX[i] >= 736:
            enemyX[i] = 736
            enemyX_change[i] = -3
            enemyY[i] += enemyY_change[i]

        # collision
        collision = isCollision(enemyX[i], enemyY[i], bulletX, bulletY)
        if collision:
            explosion_sound = mixer.Sound("music/explosion.wav")
            explosion_sound.play()
            bulletY = 480
            bullet_state = 'ready'
            score_value += 1
            print(score)
            enemyX[i] = random.randint(0, 800)
            enemyY[i] = random.randint(50, 150)

        enemy(enemyX[i], enemyY[i], i)

    #bullet Movement

    # reset bullet
    if bulletY <= 0:
        bulletY = 480
        bullet_state = 'ready'

    # bullet movement
    if bullet_state is 'fire':
        fire_bullet(bulletX, bulletY)
        bulletY -= bulletY_change

    # player will be drawn
    player(playerX, playerY)
    show_score(textX, textY)
    pygame.display.update()

'''
    After loading the background image, movements of the game character might get slow
    due to the Background image as it takes tim time to load image and iteration slows down.
'''


from selenium import webdriver
import time
import random
from PIL import Image
from chapter2.ShowapiRequest import ShowapiRequest
from base.find_element import FindElement

class RegisterFunction(object):

    def __init__(self,url):
        self.driver = self.get_driver(url)
    # 获取并打开url
    def get_driver(self,url):
        driver = webdriver.Chrome()
        driver.get(url)
        driver.maximize_window()
        return driver

    # 输入用户信息
    def send_user_info(self,key,data):
        self.get_user_element(key).send_keys(data)

    # 获取用户信息，获取element

    def get_user_element(self,key):
        find_element = FindElement(self.driver)
        user_element = find_element.get_element(key)
        return user_element

    # 获取随机数
    def get_range_user(self):
        user_info = ''.join(random.sample('1234567890abcdefgh', 5))
        return user_info

    # 获取图片

    def get_code_image(self,file_name):
        self.driver.save_screenshot(file_name)
        code_element = self.get_user_element("code_image")
        # 打印出验证码地址（元素坐标）
        # print(code_element.location)
        left = code_element.location['x']
        top = code_element.location['y']
        right = code_element.size['width'] + left
        height = code_element.size['height'] + top
        # 调用保存图片
        im = Image.open(file_name)
        # 裁剪图片
        img = im.crop((left, top, right, height))
        img.save(file_name)

        # 解析图片获取验证码

    def code_online(self,file_name):
        self.get_code_image(file_name)
        r = ShowapiRequest("http://route.showapi.com/184-5", "100157", "c58c05e4469d48788594d9ae7063da63")
        r.addBodyPara("img_base64", "file_name")
        r.addBodyPara("typeId", "35")
        r.addBodyPara("convert_to_jpg", "0")
        r.addBodyPara("needMorePrecise", "0")
        res = r.post()
        text = res.json()['showapi_res_body']['Result']
        return text  # 返回信息

    def main(self):
        user_name_info = self.get_range_user()
        user_email = user_name_info + "@qq.com"
        file_name = "D:/python tupian/imooc.png"
        code_text = self.code_online(file_name)
        self.send_user_info('user_email',user_email)
        self.send_user_info('user_name',user_name_info)
        self.send_user_info('password', "1111123")
        self.send_user_info('code_text',code_text)
        self.get_user_element('register_button').click()
        time.sleep(5)
        self.driver.close()

if __name__ == '__main__':
    register_function = RegisterFunction('http://www.5itest.cn/register')
    register_function.main()


from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'data/$', views.data, name='data'),
    url(r'model/$', views.model, name='model'),
    url(r'topic/$', views.topic, name='topic'),
    url(r'g_all/$', views.g_all, name='g_all'),
    url(r'g_bias/$', views.g_bias, name='g_bias'),
    url(r'g_confirmed/$', views.g_confirmed, name='g_confirmed'),
    url(r'g_false/$', views.g_false, name='g_false'),
    url(r'g_mostlyFalse/$', views.g_mostlyFalse, name='g_mostlyFalse'),
    url(r'g_mostlyTrue/$', views.g_mostlyTrue, name='g_mostlyTrue'),
    url(r'g_none/$', views.g_none, name='g_none'),
    url(r'g_verify/$', views.g_verify, name='g_verify'),
]


import typing
import settings
import discord
import enum
from discord.ext import commands
from discord import app_commands

logger = settings.logging.getLogger("bot")

def is_owner():
    def predicate(interaction: discord.Interaction):
        if interaction.user.id == interaction.guild.owner_id:
            return True
    return app_commands.check(predicate)

def run():
    intents = discord.Intents.all()

    bot = commands.Bot(command_prefix="!", intents=intents)

    @bot.event
    async def on_ready():
        logger.info(f"User: {bot.user} (ID: {bot.user.id})")

        bot.tree.copy_global_to(guild=settings.GUILD_ID)
        await bot.tree.sync(guild=settings.GUILD_ID)

    @bot.tree.command()
    @is_owner()
    async def say(interaction: discord.Interaction, text_to_send: str):
        await interaction.response.send_message(
            f"{text_to_send}", ephemeral=False
        )

    @say.error
    async def say_error(interaction: discord.Interaction, error):
        await interaction.response.send_message("Not allowed!", ephemeral=False)

    bot.run(settings.TOKEN, root_logger=True)

if __name__ == "__main__":
    run()


"""Abstractions for setting up a Galaxy instance."""
from __future__ import absolute_import
from __future__ import print_function

import abc
import contextlib
import os
import random
import shutil
from string import Template
from tempfile import mkdtemp

from galaxy.containers.docker_model import DockerVolume
from galaxy.tools.deps import docker_util
from galaxy.tools.deps.commands import argv_to_str
from pkg_resources import parse_version
from six import (
    add_metaclass,
    iteritems
)
from six.moves import shlex_quote

from planemo import git
from planemo.config import OptionSource
from planemo.deps import ensure_dependency_resolvers_conf_configured
from planemo.docker import docker_host_args
from planemo.io import (
    communicate,
    kill_pid_file,
    shell,
    shell_join,
    wait_on,
    warn,
    write_file,
)
from planemo.mulled import build_involucro_context
from planemo.shed import tool_shed_url
from planemo.virtualenv import DEFAULT_PYTHON_VERSION
from .api import (
    DEFAULT_MASTER_API_KEY,
    gi,
    user_api_key,
)
from .distro_tools import (
    DISTRO_TOOLS_ID_TO_PATH
)
from .run import (
    setup_common_startup_args,
    setup_venv,
)
from .workflows import (
    find_tool_ids,
    import_workflow,
    install_shed_repos,
)

NO_TEST_DATA_MESSAGE = (
    "planemo couldn't find a target test-data directory, you should likely "
    "create a test-data directory or pass an explicit path using --test_data."
)

WEB_SERVER_CONFIG_TEMPLATE = """
[server:${server_name}]
use = egg:Paste#http
port = ${port}
host = ${host}
use_threadpool = True
threadpool_kill_thread_limit = 10800
[app:main]
paste.app_factory = galaxy.web.buildapp:app_factory
"""

TOOL_CONF_TEMPLATE = """<toolbox>
  <tool file="data_source/upload.xml" />
  ${tool_definition}
</toolbox>
"""

SHED_TOOL_CONF_TEMPLATE = """<?xml version="1.0"?>
<toolbox tool_path="${shed_tool_path}">
</toolbox>
"""

SHED_DATA_MANAGER_CONF_TEMPLATE = """<?xml version="1.0"?>
<data_managers>
</data_managers>
"""

EMPTY_JOB_METRICS_TEMPLATE = """<?xml version="1.0"?>
<job_metrics>
</job_metrics>
"""

TOOL_SHEDS_CONF = """<tool_sheds>
  <tool_shed name="Target Shed" url="${shed_target_url}" />
</tool_sheds>
"""

JOB_CONFIG_LOCAL = """<job_conf>
    <plugins>
        <plugin id="planemo_runner" type="runner" load="galaxy.jobs.runners.local:LocalJobRunner" workers="4"/>
    </plugins>
    <handlers>
        <handler id="main"/>
    </handlers>
    <destinations default="planemo_dest">
        <destination id="planemo_dest" runner="planemo_runner">
            <param id="require_container">${require_container}</param>
            <param id="docker_enabled">${docker_enable}</param>
            <param id="docker_sudo">${docker_sudo}</param>
            <param id="docker_sudo_cmd">${docker_sudo_cmd}</param>
            <param id="docker_cmd">${docker_cmd}</param>
            ${docker_host_param}
        </destination>
        <destination id="upload_dest" runner="planemo_runner">
            <param id="docker_enable">false</param>
        </destination>
    </destinations>
    <tools>
        <tool id="upload1" destination="upload_dest" />
    </tools>
</job_conf>
"""

LOGGING_TEMPLATE = """
## Configure Python loggers.
[loggers]
keys = root,paste,displayapperrors,galaxydeps,galaxymasterapikey,galaxy

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console

[logger_paste]
level = WARN
handlers = console
qualname = paste
propagate = 0

[logger_galaxydeps]
level = DEBUG
handlers = console
qualname = galaxy.tools.deps
propagate = 0

[logger_galaxymasterapikey]
level = WARN
handlers = console
qualname = galaxy.web.framework.webapp
propagate = 0

[logger_displayapperrors]
level = ERROR
handlers =
qualname = galaxy.datatypes.display_applications.application
propagate = 0

[logger_galaxy]
level = ${log_level}
handlers = console
qualname = galaxy
propagate = 0

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = DEBUG
formatter = generic

[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s
"""

EMPTY_TOOL_CONF_TEMPLATE = """<toolbox></toolbox>"""

DEFAULT_GALAXY_BRANCH = "master"
DEFAULT_GALAXY_SOURCE = "https://github.com/galaxyproject/galaxy"
CWL_GALAXY_SOURCE = "https://github.com/common-workflow-language/galaxy"

# TODO: Mac-y curl variant of this.
DOWNLOAD_GALAXY = (
    "wget -q https://codeload.github.com/galaxyproject/galaxy/tar.gz/"
)

DOWNLOADS_URL = ("https://raw.githubusercontent.com/"
                 "jmchilton/galaxy-downloads/master/")
DOWNLOADABLE_MIGRATION_VERSIONS = [141, 127, 120, 117]
MIGRATION_PER_VERSION = {
    "dev": 145,
    "master": 142,
    "18.09": 142,
    "18.05": 141,
    "18.01": 140,
    "17.09": 135,
    "17.05": 134,
    "17.01": 133,
}
OLDEST_SUPPORTED_VERSION = 127

DATABASE_LOCATION_TEMPLATE = "sqlite:///%s?isolation_level=IMMEDIATE"

COMMAND_STARTUP_COMMAND = "./scripts/common_startup.sh ${COMMON_STARTUP_ARGS}"

CLEANUP_IGNORE_ERRORS = True
DEFAULT_GALAXY_BRAND = 'Configured by Planemo'

@contextlib.contextmanager
def galaxy_config(ctx, runnables, **kwds):
    """Set up a ``GalaxyConfig`` in an auto-cleaned context."""
    c = local_galaxy_config
    if kwds.get("dockerize", False):
        c = docker_galaxy_config
    elif kwds.get("external", False):
        c = external_galaxy_config

    with c(ctx, runnables, **kwds) as config:
        yield config

def simple_docker_volume(path):
    path = os.path.abspath(path)
    return DockerVolume("%s:%s:rw" % (path, path))

@contextlib.contextmanager
def docker_galaxy_config(ctx, runnables, for_tests=False, **kwds):
    """Set up a ``GalaxyConfig`` for Docker container."""
    test_data_dir = _find_test_data(runnables, **kwds)

    with _config_directory(ctx, **kwds) as config_directory:
        def config_join(*args):
            return os.path.join(config_directory, *args)

        ensure_dependency_resolvers_conf_configured(ctx, kwds, os.path.join(config_directory, "resolvers_conf.xml"))
        _handle_job_metrics(config_directory, kwds)

        shed_tool_conf = "config/shed_tool_conf.xml"
        all_tool_paths = _all_tool_paths(runnables, **kwds)

        tool_directories = set([])  # Things to mount...
        for tool_path in all_tool_paths:
            directory = os.path.dirname(os.path.normpath(tool_path))
            if os.path.exists(directory):
                tool_directories.add(directory)

        # TODO: remap these.
        tool_volumes = []
        for tool_directory in tool_directories:
            volume = simple_docker_volume(tool_directory)
            tool_volumes.append(volume)

        empty_tool_conf = config_join("empty_tool_conf.xml")

        tool_conf = config_join("tool_conf.xml")

        shed_tool_path = kwds.get("shed_tool_path") or config_join("shed_tools")
        _ensure_directory(shed_tool_path)

        sheds_config_path = _configure_sheds_config_file(
            ctx, config_directory, **kwds
        )
        port = _get_port(kwds)
        properties = _shared_galaxy_properties(config_directory, kwds, for_tests=for_tests)
        _handle_container_resolution(ctx, kwds, properties)
        master_api_key = _get_master_api_key(kwds)

        template_args = dict(
            shed_tool_path=shed_tool_path,
            tool_conf=tool_conf,
        )
        tool_config_file = "%s,%s" % (tool_conf, shed_tool_conf)

        _write_tool_conf(ctx, all_tool_paths, tool_conf)
        write_file(empty_tool_conf, EMPTY_TOOL_CONF_TEMPLATE)

        properties.update(dict(
            tool_config_file=tool_config_file,
            tool_sheds_config_file=sheds_config_path,
            migrated_tools_config=empty_tool_conf,
        ))

        server_name = "planemo%d" % random.randint(0, 100000)

        # Value substitutions in Galaxy properties - for consistency with
        # non-Dockerized version.
        template_args = dict(
        )
        env = _build_env_for_galaxy(properties, template_args)
        env["NONUSE"] = "nodejs,proftp,reports"
        if ctx.verbose:
            env["GALAXY_LOGGING"] = "full"

        # TODO: setup FTP upload dir and disable FTP server in container.
        _build_test_env(properties, env)

        docker_target_kwds = docker_host_args(**kwds)
        volumes = tool_volumes + [simple_docker_volume(config_directory)]
        export_directory = kwds.get("export_directory", None)
        if export_directory is not None:
            volumes.append(DockerVolume("%s:/export:rw" % export_directory))

        # TODO: Allow this to real Docker volumes and allow multiple.
        extra_volume = kwds.get("docker_extra_volume")
        if extra_volume:
            volumes.append(simple_docker_volume(extra_volume))
        yield DockerGalaxyConfig(
            ctx,
            config_directory,
            env,
            test_data_dir,
            port,
            server_name,
            master_api_key,
            runnables,
            docker_target_kwds=docker_target_kwds,
            volumes=volumes,
            export_directory=export_directory,
            kwds=kwds,
        )

@contextlib.contextmanager
def local_galaxy_config(ctx, runnables, for_tests=False, **kwds):
    """Set up a ``GalaxyConfig`` in an auto-cleaned context."""
    test_data_dir = _find_test_data(runnables, **kwds)
    tool_data_table = _find_tool_data_table(
        runnables,
        test_data_dir=test_data_dir,
        **kwds
    )
    data_manager_config_paths = [r.data_manager_conf_path for r in runnables if r.data_manager_conf_path]
    galaxy_root = _find_galaxy_root(ctx, **kwds)
    install_galaxy = kwds.get("install_galaxy", False)
    if galaxy_root is not None:
        if os.path.isdir(galaxy_root) and not os.listdir(galaxy_root):
            os.rmdir(galaxy_root)
        if os.path.isdir(galaxy_root) and install_galaxy:
            raise Exception("%s is an existing non-empty directory, cannot install Galaxy again" % galaxy_root)

    # Duplicate block in docker variant above.
    if kwds.get("mulled_containers", False) and not kwds.get("docker", False):
        if ctx.get_option_source("docker") != OptionSource.cli:
            kwds["docker"] = True
        else:
            raise Exception("Specified no docker and mulled containers together.")

    with _config_directory(ctx, **kwds) as config_directory:
        def config_join(*args):
            return os.path.join(config_directory, *args)

        install_env = {}
        if kwds.get('galaxy_skip_client_build', True):
            install_env['GALAXY_SKIP_CLIENT_BUILD'] = '1'
        if galaxy_root is None:
            galaxy_root = config_join("galaxy-dev")
        if not os.path.isdir(galaxy_root):
            _build_eggs_cache(ctx, install_env, kwds)
            _install_galaxy(ctx, galaxy_root, install_env, kwds)

        if parse_version(kwds.get('galaxy_python_version') or DEFAULT_PYTHON_VERSION) >= parse_version('3'):
            # on python 3 we use gunicorn,
            # which requires 'main' as server name
            server_name = 'main'
        else:
            server_name = "planemo%d" % random.randint(0, 100000)
        # Once we don't have to support earlier than 18.01 - try putting these files
        # somewhere better than with Galaxy.
        log_file = "%s.log" % server_name
        pid_file = "%s.pid" % server_name
        ensure_dependency_resolvers_conf_configured(ctx, kwds, os.path.join(config_directory, "resolvers_conf.xml"))
        _handle_job_config_file(config_directory, server_name, kwds)
        _handle_job_metrics(config_directory, kwds)
        file_path = kwds.get("file_path") or config_join("files")
        _ensure_directory(file_path)

        tool_dependency_dir = kwds.get("tool_dependency_dir") or config_join("deps")
        _ensure_directory(tool_dependency_dir)

        shed_tool_conf = kwds.get("shed_tool_conf") or config_join("shed_tools_conf.xml")
        all_tool_paths = _all_tool_paths(runnables, **kwds)
        empty_tool_conf = config_join("empty_tool_conf.xml")

        tool_conf = config_join("tool_conf.xml")

        shed_data_manager_config_file = config_join("shed_data_manager_conf.xml")

        shed_tool_path = kwds.get("shed_tool_path") or config_join("shed_tools")
        _ensure_directory(shed_tool_path)

        sheds_config_path = _configure_sheds_config_file(
            ctx, config_directory, **kwds
        )

        database_location = config_join("galaxy.sqlite")
        master_api_key = _get_master_api_key(kwds)
        dependency_dir = os.path.join(config_directory, "deps")
        preseeded_database = attempt_database_preseed(
            ctx,
            galaxy_root,
            database_location,
            **kwds
        )
        _ensure_directory(shed_tool_path)
        port = _get_port(kwds)
        template_args = dict(
            port=port,
            host=kwds.get("host", "127.0.0.1"),
            server_name=server_name,
            temp_directory=config_directory,
            shed_tool_path=shed_tool_path,
            database_location=database_location,
            tool_conf=tool_conf,
            debug=kwds.get("debug", "true"),
            id_secret=kwds.get("id_secret", "test_secret"),
            log_level="DEBUG" if ctx.verbose else "INFO",
        )
        tool_config_file = "%s,%s" % (tool_conf, shed_tool_conf)
        # Setup both galaxy_email and older test user test@bx.psu.edu
        # as admins for command_line, etc...
        properties = _shared_galaxy_properties(config_directory, kwds, for_tests=for_tests)
        properties.update(dict(
            server_name="main",
            ftp_upload_dir_template="${ftp_upload_dir}",
            ftp_upload_purge="False",
            ftp_upload_dir=test_data_dir or os.path.abspath('.'),
            ftp_upload_site="Test Data",
            check_upload_content="False",
            tool_dependency_dir=dependency_dir,
            file_path=file_path,
            new_file_path="${temp_directory}/tmp",
            tool_config_file=tool_config_file,
            tool_sheds_config_file=sheds_config_path,
            manage_dependency_relationships="False",
            job_working_directory="${temp_directory}/job_working_directory",
            template_cache_path="${temp_directory}/compiled_templates",
            citation_cache_type="file",
            citation_cache_data_dir="${temp_directory}/citations/data",
            citation_cache_lock_dir="${temp_directory}/citations/lock",
            database_auto_migrate="True",
            enable_beta_tool_formats="True",
            id_secret="${id_secret}",
            log_level="${log_level}",
            debug="${debug}",
            watch_tools="auto",
            default_job_shell="/bin/bash",  # For conda dependency resolution
            tool_data_table_config_path=tool_data_table,
            data_manager_config_file=",".join(data_manager_config_paths) or None,  # without 'or None' may raise IOError in galaxy (see #946)
            integrated_tool_panel_config=("${temp_directory}/"
                                          "integrated_tool_panel_conf.xml"),
            migrated_tools_config=empty_tool_conf,
            test_data_dir=test_data_dir,  # TODO: make gx respect this
            shed_data_manager_config_file=shed_data_manager_config_file,
        ))
        _handle_container_resolution(ctx, kwds, properties)
        write_file(config_join("logging.ini"), _sub(LOGGING_TEMPLATE, template_args))
        if not for_tests:
            properties["database_connection"] = _database_connection(database_location, **kwds)

        _handle_kwd_overrides(properties, kwds)

        # TODO: consider following property
        # watch_tool = False
        # datatypes_config_file = config/datatypes_conf.xml
        # welcome_url = /static/welcome.html
        # logo_url = /
        # sanitize_all_html = True
        # serve_xss_vulnerable_mimetypes = False
        # track_jobs_in_database = None
        # outputs_to_working_directory = False
        # retry_job_output_collection = 0

        env = _build_env_for_galaxy(properties, template_args)
        env.update(install_env)
        _build_test_env(properties, env)
        env['GALAXY_TEST_SHED_TOOL_CONF'] = shed_tool_conf

        # No need to download twice - would GALAXY_TEST_DATABASE_CONNECTION
        # work?
        if preseeded_database:
            env["GALAXY_TEST_DB_TEMPLATE"] = os.path.abspath(database_location)
        env["GALAXY_TEST_UPLOAD_ASYNC"] = "false"
        env["GALAXY_TEST_LOGGING_CONFIG"] = config_join("logging.ini")
        env["GALAXY_DEVELOPMENT_ENVIRONMENT"] = "1"
        # Following are needed in 18.01 to prevent Galaxy from changing log and pid.
        # https://github.com/galaxyproject/planemo/issues/788
        env["GALAXY_LOG"] = log_file
        env["GALAXY_PID"] = pid_file
        web_config = _sub(WEB_SERVER_CONFIG_TEMPLATE, template_args)
        write_file(config_join("galaxy.ini"), web_config)
        _write_tool_conf(ctx, all_tool_paths, tool_conf)
        write_file(empty_tool_conf, EMPTY_TOOL_CONF_TEMPLATE)

        shed_tool_conf_contents = _sub(SHED_TOOL_CONF_TEMPLATE, template_args)
        # Write a new shed_tool_conf.xml if needed.
        write_file(shed_tool_conf, shed_tool_conf_contents, force=False)

        write_file(shed_data_manager_config_file, SHED_DATA_MANAGER_CONF_TEMPLATE)

        yield LocalGalaxyConfig(
            ctx,
            config_directory,
            env,
            test_data_dir,
            port,
            server_name,
            master_api_key,
            runnables,
            galaxy_root,
            kwds,
        )

def _all_tool_paths(runnables, **kwds):
    tool_paths = [r.path for r in runnables if r.has_tools and not r.data_manager_conf_path]
    all_tool_paths = list(tool_paths) + list(kwds.get("extra_tools", []))
    for runnable in runnables:
        if runnable.type.name == "galaxy_workflow":
            tool_ids = find_tool_ids(runnable.path)
            for tool_id in tool_ids:
                if tool_id in DISTRO_TOOLS_ID_TO_PATH:
                    all_tool_paths.append(DISTRO_TOOLS_ID_TO_PATH[tool_id])

    return all_tool_paths

def _shared_galaxy_properties(config_directory, kwds, for_tests):
    """Setup properties useful for local and Docker Galaxy instances.

    Most things related to paths, etc... are very different between Galaxy
    modalities and many taken care of internally to the container in that mode.
    But this method sets up API stuff, tool, and job stuff that can be shared.
    """
    master_api_key = _get_master_api_key(kwds)
    user_email = _user_email(kwds)
    properties = {
        'master_api_key': master_api_key,
        'admin_users': "%s,test@bx.psu.edu" % user_email,
        'expose_dataset_path': "True",
        'cleanup_job': 'never',
        'collect_outputs_from': "job_working_directory",
        'allow_path_paste': "True",
        'check_migrate_tools': "False",
        'use_cached_dependency_manager': str(kwds.get("conda_auto_install", False)),
        'brand': kwds.get("galaxy_brand", DEFAULT_GALAXY_BRAND),
        'strict_cwl_validation': str(not kwds.get("non_strict_cwl", False)),
    }
    if kwds.get("galaxy_single_user", True):
        properties['single_user'] = user_email

    if for_tests:
        empty_dir = os.path.join(config_directory, "empty")
        _ensure_directory(empty_dir)
        properties["tour_config_dir"] = empty_dir
        properties["interactive_environment_plugins_directory"] = empty_dir
        properties["visualization_plugins_directory"] = empty_dir
    return properties

@contextlib.contextmanager
def external_galaxy_config(ctx, runnables, for_tests=False, **kwds):
    yield BaseGalaxyConfig(
        ctx=ctx,
        galaxy_url=kwds.get("galaxy_url", None),
        master_api_key=_get_master_api_key(kwds),
        user_api_key=kwds.get("galaxy_user_key", None),
        runnables=runnables,
        kwds=kwds
    )

def _get_master_api_key(kwds):
    master_api_key = kwds.get("galaxy_admin_key") or DEFAULT_MASTER_API_KEY
    return master_api_key

def _get_port(kwds):
    port = int(kwds.get("port", 9090))
    return port

def _user_email(kwds):
    user_email = kwds.get("galaxy_email")
    return user_email

@contextlib.contextmanager
def _config_directory(ctx, **kwds):
    config_directory = kwds.get("config_directory", None)
    created_config_directory = False
    if not config_directory:
        created_config_directory = True
        config_directory = os.path.realpath(mkdtemp())
        ctx.vlog("Created directory for Galaxy configuration [%s]" % config_directory)
    try:
        yield config_directory
    finally:
        cleanup = not kwds.get("no_cleanup", False)
        if created_config_directory and cleanup:
            shutil.rmtree(config_directory)

@add_metaclass(abc.ABCMeta)
class GalaxyInterface(object):
    """Abstraction around a Galaxy instance.

    Description of a Galaxy instance and how to interact with it - this could
    potentially be a remote, already running instance or an instance Planemo manages
    to execute some task(s).
    """

    @abc.abstractproperty
    def gi(self):
        """Return an admin bioblend Galaxy instance for API interactions."""

    @abc.abstractproperty
    def user_gi(self):
        """Return a user-backed bioblend Galaxy instance for API interactions."""

    @abc.abstractmethod
    def install_repo(self, *args, **kwds):
        """Install specified tool shed repository."""

    @abc.abstractproperty
    def tool_shed_client(self):
        """Return a admin bioblend tool shed client."""

    @abc.abstractmethod
    def wait_for_all_installed(self):
        """Wait for all queued up repositories installs to complete."""

    @abc.abstractmethod
    def install_workflows(self):
        """Install all workflows configured with these planemo arguments."""

    @abc.abstractmethod
    def workflow_id(self, path):
        """Get installed workflow API ID for input path."""

@add_metaclass(abc.ABCMeta)
class GalaxyConfig(GalaxyInterface):
    """Specialization of GalaxyInterface for Galaxy instances Planemo manages itself.

    This assumes more than an API connection is available - Planemo needs to be able to
    start and stop the Galaxy instance, recover logs, etc... There are currently two
    implementations - a locally executed Galaxy and one running inside a Docker containe
    """

    @abc.abstractproperty
    def kill(self):
        """Stop the running instance."""

    @abc.abstractmethod
    def startup_command(self, ctx, **kwds):
        """Return a shell command used to startup this instance.

        Among other common planmo kwds, this should respect the
        ``daemon`` keyword.
        """

    @abc.abstractproperty
    def log_contents(self):
        """Retrieve text of log for running Galaxy instance."""

    @abc.abstractmethod
    def cleanup(self):
        """Cleanup allocated resources to run this instance."""

    @abc.abstractproperty
    def use_path_paste(self):
        """Use path paste to upload data."""

class BaseGalaxyConfig(GalaxyInterface):

    def __init__(
        self,
        ctx,
        galaxy_url,
        master_api_key,
        user_api_key,
        runnables,
        kwds,
    ):
        self._ctx = ctx
        self.galaxy_url = galaxy_url
        self.master_api_key = master_api_key
        self._user_api_key = user_api_key
        self.runnables = runnables
        self._kwds = kwds
        self._workflow_ids = {}

    @property
    def gi(self):
        assert self.galaxy_url
        return gi(url=self.galaxy_url, key=self.master_api_key)

    @property
    def user_gi(self):
        user_api_key = self.user_api_key
        assert user_api_key
        return self._gi_for_key(user_api_key)

    @property
    def user_api_key(self):
        # TODO: thread-safe
        if self._user_api_key is None:
            # TODO: respect --galaxy_email - seems like a real bug
            self._user_api_key = user_api_key(self.gi)

        return self._user_api_key

    def _gi_for_key(self, key):
        assert self.galaxy_url
        return gi(url=self.galaxy_url, key=key)

    def install_repo(self, *args, **kwds):
        self.tool_shed_client.install_repository_revision(
            *args, **kwds
        )

    @property
    def tool_shed_client(self):
        return self.gi.toolShed

    def wait_for_all_installed(self):
        def status_ready(repo):
            status = repo["status"]
            if status in ["Installing", "New"]:
                return None
            if status == "Installed":
                return True
            raise Exception("Error installing repo status is %s" % status)

        def ready():
            repos = self.tool_shed_client.get_repositories()
            ready = all(map(status_ready, repos))
            return ready or None

        wait_on(ready, "galaxy tool installation", timeout=60 * 60 * 1)

    def install_workflows(self):
        for runnable in self.runnables:
            if runnable.type.name in ["galaxy_workflow", "cwl_workflow"]:
                self._install_workflow(runnable)

    def _install_workflow(self, runnable):
        if self._kwds["shed_install"]:
            install_shed_repos(runnable, self.gi, self._kwds.get("ignore_dependency_problems", False))

        default_from_path = self._kwds.get("workflows_from_path", False)
        # TODO: Allow serialization so this doesn't need to assume a
        # shared filesystem with Galaxy server.
        from_path = default_from_path or (runnable.type.name == "cwl_workflow")
        workflow = import_workflow(
            runnable.path, admin_gi=self.gi, user_gi=self.user_gi, from_path=from_path
        )
        self._workflow_ids[runnable.path] = workflow["id"]

    def workflow_id(self, path):
        return self._workflow_ids[path]

    @property
    def use_path_paste(self):
        option = self._kwds.get("paste_test_data_paths")
        if option is None:
            return self.default_use_path_paste
        else:
            return option

    @property
    def default_use_path_paste(self):
        return False

class BaseManagedGalaxyConfig(BaseGalaxyConfig):

    def __init__(
        self,
        ctx,
        config_directory,
        env,
        test_data_dir,
        port,
        server_name,
        master_api_key,
        runnables,
        kwds,
    ):
        galaxy_url = "http://localhost:%d" % port
        super(BaseManagedGalaxyConfig, self).__init__(
            ctx=ctx,
            galaxy_url=galaxy_url,
            master_api_key=master_api_key,
            user_api_key=None,
            runnables=runnables,
            kwds=kwds
        )
        self.config_directory = config_directory
        self.env = env
        self.test_data_dir = test_data_dir
        self.port = port
        self.server_name = server_name

class DockerGalaxyConfig(BaseManagedGalaxyConfig):
    """A :class:`GalaxyConfig` description of a Dockerized Galaxy instance."""

    def __init__(
        self,
        ctx,
        config_directory,
        env,
        test_data_dir,
        port,
        server_name,
        master_api_key,
        runnables,
        docker_target_kwds,
        volumes,
        export_directory,
        kwds,
    ):
        super(DockerGalaxyConfig, self).__init__(
            ctx,
            config_directory,
            env,
            test_data_dir,
            port,
            server_name,
            master_api_key,
            runnables,
            kwds,
        )
        self.docker_target_kwds = docker_target_kwds
        self.volumes = volumes
        self.export_directory = export_directory

    def kill(self):
        """Kill planemo container..."""
        kill_command = docker_util.kill_command(
            self.server_name,
            **self.docker_target_kwds
        )
        return shell(kill_command)

    def startup_command(self, ctx, **kwds):
        """Return a shell command used to startup this instance.

        Among other common planmo kwds, this should respect the
        ``daemon`` keyword.
        """
        daemon = kwds.get("daemon", False)
        daemon_str = "" if not daemon else " -d"
        docker_run_extras = "-p %s:80%s" % (self.port, daemon_str)
        env_directives = ["%s='%s'" % item for item in self.env.items()]
        image = kwds.get("docker_galaxy_image", "bgruening/galaxy-stable")
        run_command = docker_util.build_docker_run_command(
            "", image,
            interactive=False,
            env_directives=env_directives,
            working_directory=None,
            name=self.server_name,
            run_extra_arguments=docker_run_extras,
            set_user=False,
            volumes=self.volumes,
            **self.docker_target_kwds
        )
        chmod_command = [
            "chmod",
            "-R",
            "o+rwx",
            self.config_directory,
        ]
        if self.export_directory:
            chmod_command.append(self.export_directory)

        return shell_join(
            argv_to_str(chmod_command),
            run_command,
        )

    @property
    def log_contents(self):
        logs_command = docker_util.logs_command(
            self.server_name,
            **self.docker_target_kwds
        )
        output, _ = communicate(
            logs_command
        )
        return output

    def cleanup(self):
        shutil.rmtree(self.config_directory, CLEANUP_IGNORE_ERRORS)

class LocalGalaxyConfig(BaseManagedGalaxyConfig):
    """A local, non-containerized implementation of :class:`GalaxyConfig`."""

    def __init__(
        self,
        ctx,
        config_directory,
        env,
        test_data_dir,
        port,
        server_name,
        master_api_key,
        runnables,
        galaxy_root,
        kwds,
    ):
        super(LocalGalaxyConfig, self).__init__(
            ctx,
            config_directory,
            env,
            test_data_dir,
            port,
            server_name,
            master_api_key,
            runnables,
            kwds,
        )
        self.galaxy_root = galaxy_root

    def kill(self):
        kill_pid_file(self.pid_file)

    def startup_command(self, ctx, **kwds):
        """Return a shell command used to startup this instance.

        Among other common planemo kwds, this should respect the
        ``daemon`` keyword.
        """
        daemon = kwds.get("daemon", False)
        # TODO: Allow running dockerized Galaxy here instead.
        setup_venv_command = setup_venv(ctx, kwds)
        run_script = "%s $COMMON_STARTUP_ARGS" % shlex_quote(os.path.join(self.galaxy_root, "run.sh"))
        if daemon:
            run_script += " --daemon"
            self.env["GALAXY_RUN_ALL"] = "1"
        else:
            run_script += " --server-name %s" % shlex_quote(self.server_name)
        server_ini = os.path.join(self.config_directory, "galaxy.ini")
        self.env["GALAXY_CONFIG_FILE"] = server_ini
        if parse_version(kwds.get('galaxy_python_version') or DEFAULT_PYTHON_VERSION) >= parse_version('3'):
            # We need to start under gunicorn
            self.env['APP_WEBSERVER'] = 'gunicorn'
            self.env['GUNICORN_CMD_ARGS'] = "--bind={host}:{port} --name={server_name}".format(
                host=kwds.get('host', '127.0.0.1'),
                port=kwds['port'],
                server_name=self.server_name,
            )
        cd_to_galaxy_command = ['cd', self.galaxy_root]
        return shell_join(
            cd_to_galaxy_command,
            setup_venv_command,
            setup_common_startup_args(),
            run_script,
        )

    @property
    def log_file(self):
        """Log file used when planemo serves this Galaxy instance."""
        file_name = "%s.log" % self.server_name
        return os.path.join(self.galaxy_root, file_name)

    @property
    def pid_file(self):
        pid_file_name = "%s.pid" % self.server_name
        return os.path.join(self.galaxy_root, pid_file_name)

    @property
    def log_contents(self):
        if not os.path.exists(self.log_file):
            return ""
        with open(self.log_file, "r") as f:
            return f.read()

    def cleanup(self):
        shutil.rmtree(self.config_directory, CLEANUP_IGNORE_ERRORS)

    @property
    def default_use_path_paste(self):
        # If Planemo started a local, native Galaxy instance assume files URLs can be
        # pasted.
        return True

def _database_connection(database_location, **kwds):
    default_connection = DATABASE_LOCATION_TEMPLATE % database_location
    database_connection = kwds.get("database_connection") or default_connection
    return database_connection

def attempt_database_preseed(
    ctx, effective_galaxy_root, database_location, **kwds
):
    """If database location is unset, attempt to seed the database."""
    if os.path.exists(database_location):
        # Can't seed an existing database.
        return False

    if not _database_connection(database_location, **kwds).startswith("sqlite"):
        # Not going to use an sqlite database, don't preseed.
        return False

    preseeded_database = True
    galaxy_sqlite_database = kwds.get("galaxy_database_seed", None)
    galaxy_branch = kwds.get("galaxy_branch", None)
    try:
        _download_database_template(
            ctx,
            effective_galaxy_root,
            database_location,
            galaxy_sqlite_database=galaxy_sqlite_database,
            galaxy_branch=galaxy_branch
        )
    except Exception as e:
        print(e)
        # No network access - just roll forward from null.
        preseeded_database = False
    return preseeded_database

def _download_database_template(
    ctx,
    galaxy_root,
    database_location,
    galaxy_sqlite_database=None,
    galaxy_branch=None,
):

    if galaxy_sqlite_database is not None:
        shutil.copyfile(galaxy_sqlite_database, database_location)
        return True

    download_migration = None
    newest_migration = _newest_migration_version(galaxy_root, galaxy_branch)
    for migration in DOWNLOADABLE_MIGRATION_VERSIONS:
        if newest_migration >= migration:
            download_migration = migration
            break

    if download_migration:
        download_name = "db_gx_rev_0%d.sqlite" % download_migration
        download_url = DOWNLOADS_URL + download_name
        ctx.cache_download(download_url, database_location)
        return True
    else:
        return False

def _newest_migration_version(galaxy_root, galaxy_branch):
    versions_dir = galaxy_root and os.path.join(galaxy_root, "lib/galaxy/model/migrate/versions")
    if versions_dir and os.path.exists(versions_dir):
        version = max(map(_file_name_to_migration_version, os.listdir(versions_dir)))
    elif galaxy_branch:
        for branch in MIGRATION_PER_VERSION:
            if branch in galaxy_branch:
                version = MIGRATION_PER_VERSION[branch]
    else:
        version = OLDEST_SUPPORTED_VERSION
    return version

def _file_name_to_migration_version(name):
    try:
        return int(name[0:4])
    except ValueError:
        return -1

def _find_galaxy_root(ctx, **kwds):
    root_prop = "galaxy_root"
    cwl = kwds.get("cwl", False)
    if cwl:
        root_prop = "cwl_galaxy_root"
    galaxy_root = kwds.get(root_prop, None)
    if galaxy_root:
        return galaxy_root
    else:
        par_dir = os.getcwd()
        while True:
            run = os.path.join(par_dir, "run.sh")
            config = os.path.join(par_dir, "config")
            if os.path.isfile(run) and os.path.isdir(config):
                return par_dir
            new_par_dir = os.path.dirname(par_dir)
            if new_par_dir == par_dir:
                break
            par_dir = new_par_dir
    return None

def _find_test_data(runnables, **kwds):
    test_data_search_path = "."
    runnables = [r for r in runnables if r.has_tools]
    if len(runnables) > 0:
        test_data_search_path = runnables[0].test_data_search_path

    # Find test data directory associated with path.
    test_data = kwds.get("test_data", None)
    if test_data:
        return os.path.abspath(test_data)
    else:
        test_data = _search_tool_path_for(test_data_search_path, "test-data")
        if test_data:
            return test_data
    warn(NO_TEST_DATA_MESSAGE)
    return None

def _find_tool_data_table(runnables, test_data_dir, **kwds):
    tool_data_search_path = "."
    runnables = [r for r in runnables if r.has_tools]
    if len(runnables) > 0:
        tool_data_search_path = runnables[0].tool_data_search_path

    tool_data_table = kwds.get("tool_data_table", None)
    if tool_data_table:
        return os.path.abspath(tool_data_table)
    else:
        extra_paths = [test_data_dir] if test_data_dir else []
        return _search_tool_path_for(
            tool_data_search_path,
            "tool_data_table_conf.xml.test",
            extra_paths,
        ) or _search_tool_path_for(  # if all else fails just use sample
            tool_data_search_path,
            "tool_data_table_conf.xml.sample"
        )

def _search_tool_path_for(path, target, extra_paths=[]):
    """Check for presence of a target in different artifact directories."""
    if not os.path.isdir(path):
        tool_dir = os.path.dirname(path)
    else:
        tool_dir = path
    possible_dirs = [tool_dir, "."] + extra_paths
    for possible_dir in possible_dirs:
        possible_path = os.path.join(possible_dir, target)
        if os.path.exists(possible_path):
            return os.path.abspath(possible_path)
    return None

def _configure_sheds_config_file(ctx, config_directory, **kwds):
    if "shed_target" not in kwds:
        kwds = kwds.copy()
        kwds["shed_target"] = "toolshed"
    shed_target_url = tool_shed_url(ctx, **kwds)
    contents = _sub(TOOL_SHEDS_CONF, {"shed_target_url": shed_target_url})
    tool_sheds_conf = os.path.join(config_directory, "tool_sheds_conf.xml")
    write_file(tool_sheds_conf, contents)
    return tool_sheds_conf

def _tool_conf_entry_for(tool_paths):
    tool_definitions = ""
    for tool_path in tool_paths:
        if os.path.isdir(tool_path):
            tool_definitions += '''<tool_dir dir="%s" />''' % tool_path
        else:
            tool_definitions += '''<tool file="%s" />''' % tool_path
    return tool_definitions

def _install_galaxy(ctx, galaxy_root, env, kwds):
    if not kwds.get("no_cache_galaxy", False):
        _install_galaxy_via_git(ctx, galaxy_root, env, kwds)
    else:
        _install_galaxy_via_download(ctx, galaxy_root, env, kwds)

def _install_galaxy_via_download(ctx, galaxy_root, env, kwds):
    tmpdir = mkdtemp()
    branch = _galaxy_branch(kwds)
    command = DOWNLOAD_GALAXY + "%s -O - | tar -C '%s' -xvz | tail && mv '%s' '%s'" % \
        (branch, tmpdir, os.path.join(tmpdir, 'galaxy-' + branch), galaxy_root)
    _install_with_command(ctx, command, galaxy_root, env, kwds)

def _install_galaxy_via_git(ctx, galaxy_root, env, kwds):
    gx_repo = _ensure_galaxy_repository_available(ctx, kwds)
    branch = _galaxy_branch(kwds)
    command = git.command_clone(ctx, gx_repo, galaxy_root, branch=branch)
    _install_with_command(ctx, command, galaxy_root, env, kwds)

def _build_eggs_cache(ctx, env, kwds):
    if kwds.get("no_cache_galaxy", False):
        return None
    workspace = ctx.workspace
    eggs_path = os.path.join(workspace, "gx_eggs")
    if not os.path.exists(eggs_path):
        os.makedirs(eggs_path)
    env["GALAXY_EGGS_PATH"] = eggs_path

def _galaxy_branch(kwds):
    branch = kwds.get("galaxy_branch", None)
    if branch is None:
        cwl = kwds.get("cwl", False)
        branch = "cwl-1.0" if cwl else None
    if branch is None:
        branch = DEFAULT_GALAXY_BRANCH

    return branch

def _galaxy_source(kwds):
    source = kwds.get("galaxy_source", None)
    if source is None:
        cwl = kwds.get("cwl", False)
        source = CWL_GALAXY_SOURCE if cwl else None
    if source is None:
        source = DEFAULT_GALAXY_SOURCE

    return source

def _install_with_command(ctx, command, galaxy_root, env, kwds):
    setup_venv_command = setup_venv(ctx, kwds)
    env['__PYVENV_LAUNCHER__'] = ''
    install_cmd = shell_join(
        command,
        ['cd', galaxy_root],
        setup_venv_command,
        setup_common_startup_args(),
        COMMAND_STARTUP_COMMAND,
    )
    shell(install_cmd, env=env)

def _ensure_galaxy_repository_available(ctx, kwds):
    workspace = ctx.workspace
    cwl = kwds.get("cwl", False)
    galaxy_source = kwds.get('galaxy_source')
    if galaxy_source and galaxy_source != DEFAULT_GALAXY_SOURCE:
        sanitized_repo_name = "".join(c if c.isalnum() else '_' for c in kwds['galaxy_source']).rstrip()[:255]
        gx_repo = os.path.join(workspace, "gx_repo_%s" % sanitized_repo_name)
    else:
        gx_repo = os.path.join(workspace, "gx_repo")
    if cwl:
        gx_repo += "_cwl"
    if os.path.exists(gx_repo):
        # Convert the git repository from bare to mirror, if needed
        shell(['git', '--git-dir', gx_repo, 'config', 'remote.origin.fetch', '+refs/*:refs/*'])
        shell(['git', '--git-dir', gx_repo, 'config', 'remote.origin.mirror', 'true'])
        # Attempt remote update - but don't fail if not interweb, etc...
        shell("git --git-dir %s remote update >/dev/null 2>&1" % gx_repo)
    else:
        remote_repo = _galaxy_source(kwds)
        command = git.command_clone(ctx, remote_repo, gx_repo, mirror=True)
        shell(command)
    return gx_repo

def _build_env_for_galaxy(properties, template_args):
    env = {}
    for key, value in iteritems(properties):
        if value is not None:  # Do not override None with empty string
            var = "GALAXY_CONFIG_OVERRIDE_%s" % key.upper()
            value = _sub(value, template_args)
            env[var] = value
    return env

def _build_test_env(properties, env):
    # Keeping these environment variables around for a little while but
    # many are probably not needed as of the following commit.
    # https://bitbucket.org/galaxy/galaxy-central/commits/d7dd1f9
    test_property_variants = {
        'GALAXY_TEST_JOB_CONFIG_FILE': 'job_config_file',
        'GALAXY_TEST_MIGRATED_TOOL_CONF': 'migrated_tools_config',
        'GALAXY_TEST_TOOL_CONF': 'tool_config_file',
        'GALAXY_TEST_FILE_DIR': 'test_data_dir',
        'GALAXY_TOOL_DEPENDENCY_DIR': 'tool_dependency_dir',
        # Next line would be required for tool shed tests.
        # 'GALAXY_TEST_TOOL_DEPENDENCY_DIR': 'tool_dependency_dir',
    }
    for test_key, gx_key in test_property_variants.items():
        value = properties.get(gx_key, None)
        if value is not None:
            env[test_key] = value

def _handle_job_config_file(config_directory, server_name, kwds):
    job_config_file = kwds.get("job_config_file", None)
    if not job_config_file:
        template_str = JOB_CONFIG_LOCAL
        job_config_file = os.path.join(
            config_directory,
            "job_conf.xml",
        )
        docker_enable = str(kwds.get("docker", False))
        docker_host = str(kwds.get("docker_host", docker_util.DEFAULT_HOST))
        docker_host_param = ""
        if docker_host:
            docker_host_param = """<param id="docker_host">%s</param>""" % docker_host

        conf_contents = Template(template_str).safe_substitute({
            "server_name": server_name,
            "docker_enable": docker_enable,
            "require_container": "false",
            "docker_sudo": str(kwds.get("docker_sudo", False)),
            "docker_sudo_cmd": str(kwds.get("docker_sudo_cmd", docker_util.DEFAULT_SUDO_COMMAND)),
            "docker_cmd": str(kwds.get("docker_cmd", docker_util.DEFAULT_DOCKER_COMMAND)),
            "docker_host": docker_host_param,
        })
        write_file(job_config_file, conf_contents)
    kwds["job_config_file"] = job_config_file

def _write_tool_conf(ctx, tool_paths, tool_conf_path):
    tool_definition = _tool_conf_entry_for(tool_paths)
    tool_conf_template_kwds = dict(tool_definition=tool_definition)
    tool_conf_contents = _sub(TOOL_CONF_TEMPLATE, tool_conf_template_kwds)
    write_file(tool_conf_path, tool_conf_contents)
    ctx.vlog(
        "Writing tool_conf to path %s with contents [%s]",
        tool_conf_path,
        tool_conf_contents,
    )

def _handle_container_resolution(ctx, kwds, galaxy_properties):
    if kwds.get("mulled_containers", False):
        galaxy_properties["enable_beta_mulled_containers"] = "True"
        involucro_context = build_involucro_context(ctx, **kwds)
        galaxy_properties["involucro_auto_init"] = "False"  # Use planemo's
        galaxy_properties["involucro_path"] = involucro_context.involucro_bin

def _handle_job_metrics(config_directory, kwds):
    metrics_conf = os.path.join(config_directory, "job_metrics_conf.xml")
    open(metrics_conf, "w").write(EMPTY_JOB_METRICS_TEMPLATE)
    kwds["job_metrics_config_file"] = metrics_conf

def _handle_kwd_overrides(properties, kwds):
    kwds_gx_properties = [
        'job_config_file',
        'job_metrics_config_file',
        'dependency_resolvers_config_file',
    ]
    for prop in kwds_gx_properties:
        val = kwds.get(prop, None)
        if val:
            properties[prop] = val

def _sub(template, args):
    if template is None:
        return ''
    return Template(template).safe_substitute(args)

def _ensure_directory(path):
    if path is not None and not os.path.exists(path):
        os.makedirs(path)

__all__ = (
    "attempt_database_preseed",
    "DATABASE_LOCATION_TEMPLATE",
    "galaxy_config",
)


from django.contrib import admin
from VRMApp.models import *

class PhysicalInvD_Inline(admin.TabularInline):model = PhysicalInvD

@admin.register(PhysicalInvM)
class PhysicalInvMAdmin(admin.ModelAdmin):
    save_on_top = True
    list_display = ['PInvNo','PInvDate','PInvSeq','RegDT','UptDT']
    list_display_links = list_display
    search_fields = ['PInvNo','PInvDate']
    inlines = [PhysicalInvD_Inline]

class InOutD_Inline(admin.TabularInline):model = InOutD

@admin.register(InOutM)
class InOutMAdmin(admin.ModelAdmin):
    save_on_top = True
    list_display = ['InOutNo','InOutDate','InWHSeq','OutWHSeq','RegDT','UptDT']
    list_display_links = list_display
    search_fields = ['InOutNo','InOutDate']
    inlines = [InOutD_Inline]

# class PhysicalInvDAdmin(admin.ModelAdmin):
#     list_display = ['PInvSeq','PInvSerl','SerialNo','RegDT','UptDT']
#     list_display_links = list_display
#     search_fields = ['PInvSeq','SerialNo']

#admin.site.register(PhysicalInvM, PhysicalInvMAdmin)
#admin.site.register(PhysicalInvD, PhysicalInvDAdmin)