from flask import Flask, make_response, request, current_app
from flask_cors import CORS
from flask import json, jsonify
from base import conn, engine, session
from datetime import date, datetime
from sqlalchemy import Column, Integer, DateTime, Date
from scipy import stats
import pandas as pd

app = Flask(__name__)
CORS(app)

PathA = "/Users/rachelmei/Documents/WaterIslandCapital/src/data/FILES A/"
PathB = "/Users/rachelmei/Documents/WaterIslandCapital/src/data/FILES B/"
PathC = "/Users/rachelmei/Documents/WaterIslandCapital/src/data/FILES C/"

@app.route('/uploadCsv', methods = ['POST'])
def uploadCsv():
    res = []
    tick = request.json["firm"]
    for record in request.json["data"]:
        res.append((datetime.strptime(record["Date"], "%m/%d/%y").date(), tick, record["PX_LAST"], record["PX_VOLUME"]))

    try:
        conn.commit()
    except:
        conn.rollback()

    return "JSON Message: " + json.dumps(request.json)

@app.route('/resetAll', methods= ['PUT'])
def reset():
    print("reset")
    session.execute('''TRUNCATE TABLE MarketData''')
    session.execute('''TRUNCATE TABLE CompanyOverview''')
    session.execute('''TRUNCATE TABLE Trading''')
    session.commit()
    session.close()
    return "Success"

@app.route('/uploadAll', methods = ['PUT'])
def uploadAll():
    print("upload")
    uploadDescription()
    uploadEquity()
    uploadTrading()
    return "Success"

def uploadEquity():
    firmList = getFirmList()
    frames = []

    for firm in firmList:
        ticker = firm
        csv = PathA + ticker + ".csv"
        df = pd.read_csv(csv)
        df['Ticker'] = ticker
        frames.append(df)

    result = pd.concat(frames)
    result['Date'] = pd.to_datetime(result['Date'], format="%m/%d/%Y")
    print("done equity")
    with engine.connect() as conn, conn.begin():
        result.to_sql('MarketData', conn, if_exists='replace', dtype={"Date": DateTime})

def uploadDescription():
    result = pd.read_csv(PathB + "Description.csv")
    result['Ticker'] = result['Ticker'].apply(lambda x: x.split(" ")[0])
    print("done des")
    with engine.connect() as conn, conn.begin():
        result.to_sql('CompanyOverview', conn, if_exists='replace')

# def uploadTrading():
#     result = pd.read_csv(PathC + "Trading.csv")
#     result['Date'] = pd.to_datetime(result['Date'], format="%m/%d/%Y")
#     sLength = len(result['Date'])
#     result['PnL'] = pd.Series(0, index=result.index)
#     result['AccountBalance'] = pd.Series(0, index=result.index)
#
#     print("done trading")
#     with engine.connect() as conn, conn.begin():
#         result.to_sql('Trading', conn, if_exists='replace', dtype={"Date": DateTime})

def uploadTrading():
    result = pd.read_csv(PathC + "Trading.csv")
    result['Date'] = pd.to_datetime(result['Date'], format="%m/%d/%Y")

    print("done trading")
    with engine.connect() as conn, conn.begin():
        result.to_sql('Trading', conn, if_exists='replace', dtype={"Date": DateTime})

def getFirmList():
    data = pd.read_sql('SELECT Ticker FROM CompanyOverview', conn)
    return [x.split(" ")[0] for x in data['Ticker'].tolist()]

@app.route('/getFirms', methods = ['GET'])
def getFirms():
    firms = {
        'firms': getFirmList()
    }

    return jsonify(firms)

@app.route('/firm_overview/<firm>', methods = ['GET'])
def getOverView(firm):

    df = pd.read_sql("SELECT * FROM CompanyOverview where Ticker = \"%s\"" %firm, conn)
    data = {
        'overview': df.to_json(orient='records')

    }
    return jsonify(data)

@app.route('/stats', methods = ['POST'])
def pairStats():
    form = request.form.to_dict()
    tick1 = form['ticker1'].split(" ")[0]
    tick2 = form['ticker2'].split(" ")[0]

    df = pd.read_sql('SELECT * FROM MarketData', conn)

    a = df[df['Ticker'] == tick1]['PX_LAST'].tolist()
    b = df[df['Ticker'] == tick2]['PX_LAST'].tolist()

    slope, intercept, r_value, p_value, std_err = stats.linregress(a, b)
    corr = stats.pearsonr(a, b)[0]
    beta = slope

    data = {
        'corr' : corr,
        'beta' : beta
    }

    return jsonify(data)

@app.route('/timeseries/<firm>')
def getTS(firm):
    df = pd.read_sql("SELECT * FROM MarketData where Ticker= \"%s\"" %firm, conn)
    data = {
        'ts': df.to_json(orient='records')
    }
    return jsonify(data)

@app.route('/add_portfolio', methods = ['POST'])
def addPortfolio():
    form = request.form.to_dict()
    data = json.loads(form["jf"])
    weights = data["weights"]
    ids = data["ids"]
    d = {'Ticker': ids, 'Weight': weights}
    df = pd.DataFrame(data=d)
    df.to_sql('Portfolio', conn, if_exists='replace')
    return jsonify(data)

if __name__ == '__main__':

    # pairStats()
    # data = pd.read_sql('SELECT * FROM MarketData', conn)
    # df.to_sql(con=conn, name='CompanyOverview', if_exists='replace', flavor='mysql')
    #
    # with engine.connect() as conn, conn.begin():
    #     df.to_sql('CompanyOverview', conn, if_exists='replace')

    # print(list(df))

    app.run(host="localhost", port=5000,  threaded=True)

    # uploadAll()
    # reset()
    # uploadDescription()


import sys
import session

from phonetic import Phonetic

class Event(Phonetic):
    def __init__(self, desc = None, duration = None):
        if sys.version_info.major == 3:
            super().__init__()
        else:
            super(Event, self).__init__()

        self.desc = desc
        self.duration = duration
        self.midway = False
        self.countdown = 0

        session.tot_duration += 0 if self.duration == None else self.duration

    @property
    def duration(self):
        return self._dur

    @duration.setter
    def duration(self, value):
        self._dur = value

    @property
    def desc(self):
        return self._desc

    @desc.setter
    def desc(self, value):
        self._desc = value

    @property
    def midway(self):
        return self._midway

    @midway.setter
    def midway(self, value):
        self._midway = value

    @property
    def countdown(self):
        return self._countdown

    @countdown.setter
    def countdown(self, value):
        self._countdown = value

    def do(self):
        self.speak()
        session.sleep(self.duration)

class Exercise(Event):
    def __init__(self, desc, duration):
        if sys.version_info.major == 3:
            super().__init__(desc, duration)
        else:
            super(Exercise, self).__init__(desc, duration)

        self.speech = desc

    def do(self):
        self.speak()
        Rest(10)
        if session.midway and self.duration / 2 > session.countdown:
            self.speak("Go!")
            session.sleep(self.duration / 2.)
            self.speak("Halfway through. Keep pushing! Change side if you need to.")
            session.sleep(self.duration / 2. - self.countdown)
        else:
            session.sleep(self.duration - self.countdown)

        if session.countdown > 0:
            for i in range(session.countdown):
                self.speak(str(session.countdown - i))
                session.sleep(1)

class Rest(Event):
    def __init__(self, duration):
        if sys.version_info.major == 3:
            super().__init__("rest", duration)
        else:
            super(Rest, self).__init__("rest", duration)

class Announce(Event):
    def __init__(self, desc, duration):
        if sys.version_info.major == 3:
            super().__init__(desc, duration)
        else:
            super(Announce, self).__init__(desc, duration)

        self.speech = desc


# Import required libraries
import speech_recognition as sr
import pyttsx3
import os

class TriggerAssistant:
    def __init__(self):
        # Initialize the speech recognizer and text-to-speech engine
        self.recognizer = sr.Recognizer()
        self.engine = pyttsx3.init()

        # Set the voice rate and volume
        self.engine.setProperty('rate', 150)
        self.engine.setProperty('volume', 1)

        # Start listening
        self.listen()

    def listen(self):
        with sr.Microphone() as source:
            print("Listening...")
            self.recognizer.pause_threshold = 1
            audio = self.recognizer.listen(source)

            try:
                # Use the speech recognizer to recognize speech from the audio
                text = self.recognizer.recognize_google(audio)
                print(f"You said: {text}")

                # write speech to an environment variable so it can be used by the GPT module
                os.environ['query'] = text

                # Continue listening even if some other word is spoken
                self.listen()

            except sr.UnknownValueError:
                print("Audio could not be recognized")
                self.listen()

    def say(self, text):
        print(f"Chat Assistant: {text}")

        # Use the text-to-speech engine to speak the response
        self.engine.say(text)
        self.engine.runAndWait()

# # Create a ChatAssistant object
# assistant = TriggerAssistant()


from setup import mysql, session
from classes.personal import Personal

# class employee inherits class Personal
class Employee(Personal):
    def __init__(self,first_name,middle_name,last_name,dob,gender,email,password,contact_no,emp_id,dept_id,designation_id,salary,doj,orientation,training,credentials,equipment):
        super().__init__(emp_id,first_name,middle_name,last_name,dob,gender,email,password,contact_no)
        self.dept_id = dept_id
        self.designation_id = designation_id
        self.salary = salary
        self.doj = doj
        self.orientation = orientation
        self.training = training
        self.credentials = credentials
        self.equipment = equipment

    # To add new employee details into the database using method overriding
    def addEmployee(self):
        mycursor = mysql.connection.cursor()

        try:
            query = 'INSERT INTO EMPLOYEE_DETAILS(EMP_ID,DEPT_ID,DESIGNATION_ID,SALARY,DOJ) VALUES (%s,%s,%s,%s,%s)'
            values = (self.emp_id,self.dept_id,self.designation_id,self.salary,self.doj)
            mycursor.execute(query,values)
            mysql.connection.commit()
            if mycursor.rowcount > 0:
                return True
            else:
                return False
        except Exception as e:
            mysql.connection.rollback()
            print(f"employeeExistence | {e}")
            return False
        finally:
            mycursor.close()

    # To view exisiting employees in the database
    def viewEmployees(self):
        mycursor = mysql.connection.cursor()
        try:
            query = 'SELECT PERSONAL_DETAILS.*, EMPLOYEE_DETAILS.SALARY, EMPLOYEE_DETAILS.DOJ, DEPARTMENT_DETAILS.DEPT_NAME, DESIGNATION_DETAILS.DESIGNATION_NAME, EMPLOYEE_DETAILS.ORIENTATION, EMPLOYEE_DETAILS.TRAINING, EMPLOYEE_DETAILS.CREDENTIALS, EMPLOYEE_DETAILS.EQUIPMENT FROM PERSONAL_DETAILS INNER JOIN EMPLOYEE_DETAILS ON EMPLOYEE_DETAILS.EMP_ID = PERSONAL_DETAILS.EMP_ID INNER JOIN DEPARTMENT_DETAILS ON DEPARTMENT_DETAILS.DEPT_ID = EMPLOYEE_DETAILS.DEPT_ID INNER JOIN DESIGNATION_DETAILS ON DESIGNATION_DETAILS.DESIGNATION_ID = EMPLOYEE_DETAILS.DESIGNATION_ID'
            mycursor.execute(query)
            result = mycursor.fetchall()
            if result:
                return result
            else:
                return False
        except Exception as e:
            mysql.connection.rollback()
            print(f'viewEmployees | {e}')
            return False
        finally:
            mycursor.close()

    # To view specific employee details in the database
    def viewSpecificEmployee(self):
        mycursor = mysql.connection.cursor()
        try:
            query = 'SELECT PERSONAL_DETAILS.*, EMPLOYEE_DETAILS.SALARY, EMPLOYEE_DETAILS.DOJ, DEPARTMENT_DETAILS.DEPT_NAME, DESIGNATION_DETAILS.DESIGNATION_NAME,EMPLOYEE_DETAILS.ORIENTATION, EMPLOYEE_DETAILS.TRAINING, EMPLOYEE_DETAILS.CREDENTIALS, EMPLOYEE_DETAILS.EQUIPMENT FROM PERSONAL_DETAILS INNER JOIN EMPLOYEE_DETAILS ON EMPLOYEE_DETAILS.EMP_ID = PERSONAL_DETAILS.EMP_ID INNER JOIN DEPARTMENT_DETAILS ON DEPARTMENT_DETAILS.DEPT_ID = EMPLOYEE_DETAILS.DEPT_ID INNER JOIN DESIGNATION_DETAILS ON DESIGNATION_DETAILS.DESIGNATION_ID = EMPLOYEE_DETAILS.DESIGNATION_ID WHERE EMPLOYEE_DETAILS.EMP_ID = %s'
            values = (self.emp_id,)
            mycursor.execute(query,values)
            result = mycursor.fetchall()
            if result:
                print("id fetched")
                return result
            else:
                print("id not fetched")
                return False
        except Exception as e:
            mysql.connection.rollback()
            print(f'viewSpecificEmployee | {e}')
            return False
        finally:
            mycursor.close()

    # To modifiy employee details
    def editEmployee(self,orientation,training,credentials,equipment):
        mycursor = mysql.connection.cursor()
        try:
            query = 'UPDATE PERSONAL_DETAILS INNER JOIN EMPLOYEE_DETAILS ON EMPLOYEE_DETAILS.EMP_ID = PERSONAL_DETAILS.EMP_ID SET PERSONAL_DETAILS.FIRST_NAME = %s, PERSONAL_DETAILS.MIDDLE_NAME = %s,  PERSONAL_DETAILS.LAST_NAME = %s, PERSONAL_DETAILS.DOB = %s, PERSONAL_DETAILS.GENDER = %s, PERSONAL_DETAILS.EMAIL = %s, PERSONAL_DETAILS.CONTACT_NO = %s, EMPLOYEE_DETAILS.DEPT_ID = %s, EMPLOYEE_DETAILS.DESIGNATION_ID = %s, EMPLOYEE_DETAILS.SALARY = %s, EMPLOYEE_DETAILS.DOJ = %s, EMPLOYEE_DETAILS.ORIENTATION = %s, EMPLOYEE_DETAILS.TRAINING = %s, EMPLOYEE_DETAILS.CREDENTIALS = %s, EMPLOYEE_DETAILS.EQUIPMENT = %s WHERE PERSONAL_DETAILS.EMP_ID = %s'
            values = (self.first_name,self.middle_name,self.last_name,self.dob,self.gender,self.email,self.contact_no,self.dept_id,self.designation_id,self.salary,self.doj,orientation,training,credentials,equipment,self.emp_id)
            mycursor.execute(query,values)
            mysql.connection.commit()
            if mycursor.rowcount > 0:
                print("Edit emp successfull")
                return True
            else:
                print("Edit emp unsuccessfull")
                return False
        except Exception as e:
            mysql.connection.rollback()
            print(f"editEmployee | {e}")
            return False
        finally:
            mycursor.close()

    # To delete exisiting employees from the database
    def deleteEmployee(self):
        mycursor = mysql.connection.cursor()
        try:
            mycursor.callproc("delete_employee", [self.emp_id])
            mysql.connection.commit()
            if mycursor.rowcount > 0:
                return True
            else:
                return False
        except Exception as e:
            mysql.connection.rollback()
            print(f"deleteEmployee | {e} ")
            return False
        finally:
            mycursor.close()


from sys import stdin

n,m=map(int,stdin.readline().split())

answer = []
min=min(n,m) #최대 공약수 : 둘중 작은거 부터 1씩 줄어가며 두수 다 나머지 0으로 만드는 숫자찾기
for i in range(min,0,-1):
    if n%i==0 and m%i==0:
        answer.append(i)
        break

max=max(n,m) #최소 공배수 : 둘중 큰거부터 1씩 키워가며 두수로 나눴을때 나머지 0되는거
for i in range(max,n*m+1):
    if i%n==0 and i%m==0:
        answer.append(i)
        break

for i in answer:
    print(i)


from django.test import TestCase
from users.models import CustomUser
from reviews.models import Review, Feedback

class ReviewTest(TestCase):
    """
    Test module for Review model
    """

    def setUp(self):
        user = CustomUser.objects.create(
            username='testuser',
            is_admin=True,
            full_name='Test User'
        )
        Review.objects.create(
            employee=user,
        )

    def test_create_review(self):
        review = Review.objects.latest('created_at')
        self.assertEqual(review.employee.username, 'testuser')

class FeedbackTest(TestCase):
    """
    Test module for Feedback model
    """

    def setUp(self):
        user = CustomUser.objects.create(
            username='testuser',
            is_admin=True,
            full_name='Test User'
        )
        user_to_assign_1 = CustomUser.objects.create(
            username='testuser2',
            is_admin=False,
            full_name='Test User'
        )
        user_to_assign_2 = CustomUser.objects.create(
            username='testuser3',
            is_admin=False,
            full_name='Test User'
        )
        review = Review.objects.create(
            employee=user,
        )
        Feedback.objects.create(
            review=review,
            assignee=user_to_assign_1,
        )
        Feedback.objects.create(
            review=review,
            assignee=user_to_assign_2,
        )

    def test_create_review(self):
        review = Review.objects.latest('created_at')
        feedbacks = Feedback.objects.filter(
            review__id=review.id
        )
        self.assertEqual(feedbacks[0].review.employee.username, 'testuser')
        self.assertEqual(feedbacks[1].review.employee.username, 'testuser')


#221RDC035, Artemijs Zaharovs, 18.gr.
import os
def sif(data, i, swaps):
    n = len(data)
    x = i
    l = 2*i + 1
    if l < n and data[l] < data[x]:
        x = l
    r = 2*i + 2
    if r < n and data[r] < data[x]:
        x = r
    if i != x:
        data[i], data[x] = data[x], data[i]
        swaps.append((i, x))
        sif(data, x, swaps)

def build_heap(data):
    n = len(data)
    swaps = []
    for i in range(n//2, -1, -1):
        sif(data, i, swaps)
    return swaps

def main():
    input_method = input().strip()
    if input_method == 'I':
        n = int(input())
        data = list(map(int, input().split()))
    elif input_method == 'F':
        file_name = input().strip()
        file_path = os.path.join("tests", file_name)
        with open(file_path) as file:
            n = int(file.readline().strip())
            data = list(map(int, file.readline().strip().split()))
    else:
        print('Invalid input method')
        return

    swaps = build_heap(data)
    print(len(swaps))
    for i, j in swaps:
        print(i, j)

if __name__ == "__main__":
    main()


from configparser import ConfigParser
from multiprocessing import Process, Queue

import pygame

from core.data_retriever import DataRetriever
from core.data_presenter import DataPresenter
from core.logging_filters import create_logger
from scripts.create_db import create_db_if_not_present

CONFIG_FILE = 'config.ini'

pygame.init()

if __name__ == '__main__':
    config = ConfigParser()
    config.read(CONFIG_FILE)
    logger = create_logger(config['general']['logging_path'], __name__)

    logger.info("Starting main application")

    create_db_if_not_present(CONFIG_FILE)

    queue_retriever = Queue()
    queue_presenter = Queue()

    data_retriever = DataRetriever(CONFIG_FILE, queue_retriever)
    data_presenter = DataPresenter(CONFIG_FILE, queue_presenter)
    p_retrieve = Process(target=data_retriever.run)
    p_present = Process(target=data_presenter.run)
    p_retrieve.start()
    p_present.start()

    obj = queue_presenter.get()
    logger.info("Received halting signal from presenter")
    logger.info("halting retriever")
    queue_retriever.put("halt")

    logger.info("Closing queues")
    queue_retriever.close()
    queue_presenter.close()
    queue_retriever.join_thread()
    queue_presenter.join_thread()

    logger.info("Joining processes")
    p_present.join()
    p_retrieve.join()

    logger.info("Quitting pygame")
    pygame.quit()


import geopandas as gpd
import os

dir = os.path.dirname(os.path.abspath(__file__))
colors = [
    "#f77779",
    "#b6d86c",
    "#fccd7b",
    "#fb9677",
    "#fd453f",
    "#646e78",
    "#8d98a7",
    "#dcccbb",
    "#eab464",
    "#a7754d",
    "#fa8334",
    "#fffd77",
    "#ffe887",
    "#388697",
    "#776533",
    "#b47eb3",
    "#fdf5bf",
    "#ffd5ff",
    "#97d6c3",
    "#8bb8a8",
    "#4599f7",
    "#f4f4e7",
    "#eb9baf",
    "#f9e8ab",
    "#fbc5ae",
    "#668bc3",
    "#df5d68",
    "#f5cbcb",
    "#5fb6c4",
    "#c7ffe9",
    "#a54657",
    "#587635",
    "#f7ee7f",
    "#f6a66a",
    "#f76657",
    "#fbc7b5",
    "#ffa8a9",
    "#f786aa",
    "#a64a76",
    "#cdb7ab",
]
countries = gpd.read_file(os.path.join(dir, "datasets","ne_50m_admin_0_countries.shp"))
ocean = gpd.read_file(os.path.join(dir, "datasets","ne_50m_ocean.shp"))
gridlines = gpd.read_file(os.path.join(dir, "datasets","ne_50m_geographic_lines.shp"))
rivers = gpd.read_file(os.path.join(dir, "datasets","ne_50m_rivers_lake_centerlines.shp"))
lakes = gpd.read_file(os.path.join(dir, "datasets","ne_50m_lakes.shp"))
us_states = gpd.read_file(os.path.join(dir, "datasets","us_states.geojson"))


import numpy as np

def knn_search(Mu, att, size):
    distances = np.zeros(len(Mu))
    index = 0
    for mu in Mu:
        distances[index] = np.linalg.norm(mu - att)
        index = index + 1

    order = []
    for i in np.arange(0, len(Mu)):
        cur_value = distances[i]
        cur_index = 0
        for j in np.arange(0, len(Mu)):
            if distances[j] < cur_value:
                cur_index += 1
        order.append(cur_index)

    return order


file_in  ='fsklearn_training.py'
file_out = 'pyf.f90'
py_out_test = 'pyf.py'
file_num = 'file_num'
complete_f = True
line_begin = '    write('+file_num+',\'(A)\') "'
line_end = '"'

py_in = open(file_in)
f_out = open(file_out,'w')

if complete_f == True:
    f_out.write('Program pyf\n')
    f_out.write('    Implicit None\n')
    f_out.write('    Integer :: file_num = 7\n\n')
    f_out.write('    Open(7,file=\''+py_out_test+'\')\n')

for line in py_in:
    line2 = line_begin + line.replace('"','""').replace('\n','"\n')
    f_out.write(line2)

if complete_f == True:
    f_out.write('End Program pyf\n')


from collections import deque

direction = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)]
while True:
    w, h = map(int, input().split())
    if w == h == 0:
        break

    mymap = []
    for i in range(h):
        mymap.append(list(map(int, input().split())))

    cnt = 0
    for i in range(h):
        for j in range(w):
            if mymap[i][j] == 1:
                queue = deque()
                queue.append((i, j))
                mymap[i][j] = 0

                while len(queue) != 0:
                    q = queue.popleft()
                    for d in direction:
                        newpos = (q[0] + d[0], q[1] + d[1])
                        if not (0 <= newpos[0] < h) or not (0 <= newpos[1] < w):
                            continue
                        if mymap[newpos[0]][newpos[1]] == 1:
                            queue.append(newpos)
                            mymap[newpos[0]][newpos[1]] = 0
                cnt += 1
    print(cnt)


"""Core module.
"""
import copy
import hashlib
import os
import shutil

from astropy.table import Table

from astrobucket.config import Config
from astrobucket.xselect import XselectCommander

def open_fits_astable(fits_path: str):
    table = Table.read(fits_path, hdu=1)
    return table

class Client:
    """
    Raises
        TypeError: If `obsid` is not str.
    """
    def __init__(self, fits_path: str, object_name: str,
                 satellite: str, obsid: str, verbose: bool = True,
                 save_cache: bool = True):
        if not isinstance(obsid, str):
            raise TypeError(f"{type(obsid)} is not supported.")

        self.fits_path = fits_path
        self.object_name = object_name
        self.satellite = satellite
        self.obsid = obsid
        self.verbose = verbose

        # self.inputs[1] indicate creation mode (={"curve", "spectrum"}).
        self.inputs = [fits_path, "none", object_name, satellite, obsid]

        self.server = Server(Config.cache_path, verbose, save_cache)

    def clear_cache(self, mode: str, *args):
        inputs = copy.copy(self.inputs)
        inputs[1] = mode
        if mode == "curve":
            dt = args[0]
            energy_range_kev = args[1]
            inputs.extend([dt, energy_range_kev])
        elif mode == "spectrum":
            de = args[0]
            energy_range_kev = args[1]
            inputs.extend([de, energy_range_kev])
        else:
            raise NotImplementedError()
        self.server.clear_cache(*inputs)

    def request_curve(self, dt: float, energy_range_kev: tuple or list,
                      verbose: bool = True):
        inputs = copy.copy(self.inputs)
        inputs[1] = "curve"
        inputs.extend([dt, (energy_range_kev[0], energy_range_kev[1])])
        curve = self.server.request(*inputs)
        return curve

    def request_event(self):
        table = open_fits_astable(self.fits_path)
        return table

    def request_spectrum(self, de: float, energy_range_kev: tuple,
                         verbose: bool = True):
        inputs = copy.copy(self.inputs)
        inputs[1] = "spectum"
        inputs.extend([de, energy_range_kev])
        spectrum = self.server.request(*inputs)
        return spectrum

class Creator:
    """Create request `astropy.table`.
    """
    def _create_curve(self):
        xsel_commander = XselectCommander()
        xsel_commander.create_curve(*self.inputs)

    def _create_spectrum(self):
        raise NotImplementedError("Comming soon...")
        # TODO: create spectrum.
        # de = self.inputs[-2]
        # energy_range = self.inputs[-1]
        spectrum = None
        return spectrum

    def create(self, *inputs):
        self.inputs = inputs
        self.mode = inputs[1]  # mode = {"curve", "spectrum"}
        if self.mode == "curve":
            self._create_curve()
        elif self.mode == "spectrum":
            self._create_spectrum()
        fits_path = os.path.join(Config.tmp_path, "xselect_tmp.fits")
        return fits_path

class Namer:
    """Name class.

    Raises
        TypeError: if inputs have other types exept to `int`, `float`, `str`,
            `list`, and `tuple`.
    """
    def _convert_float(self, value: float):
        if int(value) == value:
            return self._convert_int(int(value))
        word = str(value).rjust(10, "0")
        word = word.replace(".", "p")
        return word

    def _convert_int(self, value: int):
        word = str(value).rjust(10, "0")
        return word

    def _convert_multiple(self, value: list or tuple):
        words = [self._convert_single(v) for v in value]
        words = "-".join(words)
        return words

    def _convert_single(self, value: float or int or str):
        if isinstance(value, float):
            word = self._convert_float(value)
        elif isinstance(value, int):
            word = self._convert_int(value)
        elif isinstance(value, str):
            word = self._convert_str(value)
        elif isinstance(value, (list, tuple)):
            word = self._convert_multiple(value)
        return word

    def _convert_str(self, value: str):
        word = str(value)  # Copy instance.
        word = value.lower()
        word = word.replace(" ", "")
        return word

    def get_name(self, *inputs):
        word_list = []
        for t in inputs:
            if isinstance(t, (float, int, str)):
                word = self._convert_single(t)
            elif isinstance(t, (list, tuple)):
                word = self._convert_multiple(t)
            else:
                raise TypeError(f"{type(t)} is not supported.")
            word_list.append(word)
        fullname = "_".join(word_list)
        return fullname

class Server:
    """Server.

    If cache exists, return that. If not, inputs are passed to
    Creator instance.
    """
    def __init__(self, cache_path: str, verbose: bool = True,
                 save_cache: bool = True):
        self.cache_path = cache_path
        self.verbose = verbose
        self.save_cache = save_cache

    def _check_cache(self, filename: str):
        search_path = os.path.join(self.cache_path, filename)
        return os.path.exists(search_path)

    def _convert2hash(self, target: object):
        target_hash = hashlib.md5(target.encode()).hexdigest()
        return target_hash

    def _get_eventhash(self, event_path: str):
        """Get event hash by reading event file.
        """
        hash_ = hashlib.md5()
        with open(event_path, 'rb') as f:
            while True:
                chunk = f.read(2048 * hash_.block_size)
                if len(chunk) == 0:
                    break
                hash_.update(chunk)
        event_hash = hash_.hexdigest()
        return event_hash

    def clear_cache(self, *inputs):
        basename = Namer().get_name(*inputs[1:])
        filename_hash = self._convert2hash(basename)
        fits_path = os.path.join(self.cache_path, filename_hash)
        if os.path.exists(fits_path):
            os.remove(fits_path)

    def request(self, *inputs):

        # Use hash from combined hash of filename and event_hash to
        # ensure that the event file is the same as previous one.
        basename = Namer().get_name(*inputs[1:])
        filename_hash = self._convert2hash(basename)
        event_hash = self._get_eventhash(inputs[0])
        reference_hash = self._convert2hash(filename_hash + event_hash)

        exist = self._check_cache(reference_hash)
        fits_path = os.path.join(self.cache_path, reference_hash)
        if not exist:
            tmp_fits_path = Creator().create(*inputs)
            if self.save_cache:
                shutil.copy(tmp_fits_path, fits_path)
                fits_path = tmp_fits_path
        else:
            if self.verbose:
                print(f"use cache for {basename}")
        table = open_fits_astable(fits_path)

        return table


import numpy as np
import cv2
from numpy.linalg import norm
import os
import matplotlib.pyplot as plt
SZ=20
def deskew(img):
    m = cv2.moments(img)
    if abs(m['mu02']) < 1e-2:
        return img.copy()
    skew = m['mu11']/m['mu02']
    M = np.float32([[1, skew, -0.5*SZ*skew], [0, 1, 0]])
    img = cv2.warpAffine(img, M, (SZ, SZ), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR)
    return img

def preprocess_hog(digits):
    samples = []
    for img in digits:
        gx = cv2.Sobel(img, cv2.CV_32F, 1, 0)
        gy = cv2.Sobel(img, cv2.CV_32F, 0, 1)
        mag, ang = cv2.cartToPolar(gx, gy)
        bin_n = 16
        bin = np.int32(bin_n * ang / (2 * np.pi))
        bin_cells = bin[:10, :10], bin[10:, :10], bin[:10, 10:], bin[10:, 10:]
        mag_cells = mag[:10, :10], mag[10:, :10], mag[:10, 10:], mag[10:, 10:]
        hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_cells)]
        hist = np.hstack(hists)

        eps = 1e-7
        hist /= hist.sum() + eps
        hist = np.sqrt(hist)
        hist /= norm(hist) + eps

        samples.append(hist)
    return np.float32(samples)

class StatModel(object):
    def load(self, fn):
        self.model = self.model.load(fn)
    def save(self, fn):
        self.model.save(fn)

class SVM(StatModel):
    def __init__(self, C = 100, gamma = 0.3):
        self.model = cv2.ml.SVM_create()
        self.model.setGamma(gamma)
        self.model.setC(C)
        self.model.setKernel(cv2.ml.SVM_RBF)
        self.model.setType(cv2.ml.SVM_C_SVC)
    #训练svm
    def train(self, samples, responses):
        self.model.train(samples, cv2.ml.ROW_SAMPLE, responses)
    #字符识别
    def predict(self, samples):
        r = self.model.predict(samples)
        return r[1].ravel()

model = SVM()
try:
    model.load("svm1.dat")
except:
    print("error :no trained file!!")
    exit()

chars_train = []
chars_label = []

path='image'
file=os.listdir(path)
kernal=cv2.getStructuringElement(cv2.MORPH_RECT,(2,2))
kernal2=cv2.getStructuringElement(cv2.MORPH_RECT,(4,4))
for cnt in range(len(file)):
    # if file[cnt][:-5]=='redline (10':
    #     pass
    # else :
    #     continue
    # print(file[cnt])
    img=cv2.imread(os.path.join(path,file[cnt]))
    img=cv2.resize(img,(100,100))

    imgCanny=cv2.Canny(img.copy(),250,400)
    imgCanny=cv2.morphologyEx(imgCanny,cv2.MORPH_CLOSE,kernal,iterations=2)

    cv2.imshow('imgCanny',imgCanny)

    #数字分割1
    y_his =np.sum(imgCanny,axis=0)/255
    y_his=np.concatenate([np.zeros(1),y_his,np.zeros(1)])
    # print(y_his)

    yloc = 0
    yloc_r = 0
    Weighty = 0
    for ycnt in range(len(y_his) - 1):
        if y_his[ycnt] == 0 and y_his[ycnt + 1] != 0:
            yloc_r = ycnt
        if y_his[ycnt] != 0 and y_his[ycnt + 1] == 0 and (ycnt - yloc_r) > Weighty:
            Weighty = ycnt - yloc_r
            yloc = yloc_r
        if Weighty > 10:
            break
    Weighty = Weighty + 1
    num1 = imgCanny[:, yloc:yloc + Weighty]

    x_his=np.sum(num1,axis=1)/255
    x_his = np.concatenate([np.zeros(1), x_his])
    xloc = 0
    xloc_r = 0
    Weightx = 0
    for xcnt in range(len(x_his) - 1):
        if (x_his[xcnt] == 0 or xcnt==0 )and x_his[xcnt + 1] != 0 :
            xloc_r = xcnt
        if x_his[xcnt] != 0 and x_his[xcnt + 1] == 0 and (xcnt - xloc_r) > Weightx:
            Weightx = xcnt - xloc_r
            xloc = xloc_r
        if Weightx > 10:
            break
    x0=xloc
    # print('x0:{0}'.format(x0))
    x1=len(x_his)-1
    Weightx = 0
    for xcnt1 in range(len(x_his)-1 ):
        xcnt=len(x_his)-xcnt1- 1
        if (x_his[xcnt] == 0 or xcnt==len(x_his)- 1)and x_his[xcnt - 1] != 0 :
            xloc_r = xcnt
        if x_his[xcnt] != 0 and x_his[xcnt - 1] == 0 and ( xloc_r-xcnt ) > Weightx:
            Weightx =  xloc_r-xcnt
            x1 = xloc_r
        if Weightx > 10:
            break
    cannynum1=imgCanny[x0:x1,yloc:yloc + Weighty].copy()
    num11=img[x0:x1,yloc:yloc + Weighty].copy()
    cv2.imshow('num11', num11)

    #######cv2.imwrite('compare\\'+file[cnt][:-4]+'_1.tif',num11)

    number_pix=num11.shape[0]*num11.shape[1]+1#修正除数为0的情况

    B=num11[:,:,0].copy()
    G=num11[:,:,1].copy()
    R=num11[:,:,2].copy()

    Bhist = cv2.calcHist([B],[0],None,[256],[0,256]).flatten()
    Ghist = cv2.calcHist([G],[0],None,[256],[0,256]).flatten()
    Rhist = cv2.calcHist([R],[0],None,[256],[0,256]).flatten()

    B_RATE=np.sum(Bhist[180:])   / (number_pix - np.sum(Bhist[:100]))
    G_RATE=np.sum(Ghist[180:] ) / (number_pix  - np.sum(Ghist[:100]))
    R_RATE=np.sum(Rhist[180:] ) / (number_pix  - np.sum(Rhist[:100]))
    # print('num1:')
    # print('B_RATE:{0}'.format(B_RATE))
    # print('G_RATE:{0}'.format(G_RATE))
    # print('R_RATE:{0}'.format(R_RATE))

    flag=False
    if B_RATE>0.6  :
        if G_RATE>0.6 or R_RATE> 0.6:
            flag =True
    elif G_RATE>0.6 and R_RATE> 0.6:
        flag = True

    if flag==True :
        num11=cv2.cvtColor(num11,cv2.COLOR_BGR2GRAY)
        _, num11 = cv2.threshold(num11, 127, 255, cv2.THRESH_BINARY)
    else :
        color_location = np.argmax([B_RATE, G_RATE, R_RATE])
        if color_location==0:
            _,num11 =cv2.threshold(B, 180, 255, cv2.THRESH_BINARY)
            num11=cv2.morphologyEx(num11,cv2.MORPH_OPEN,kernal,iterations=3)
            num11=cv2.dilate(num11,kernal2)
        elif color_location==1:
            _,num11 =cv2.threshold(G, 180, 255, cv2.THRESH_BINARY)
            num11=cv2.morphologyEx(num11,cv2.MORPH_OPEN,kernal,iterations=3)
            num11=cv2.dilate(num11,kernal2)
        else :
            _,num11 =cv2.threshold(R, 180, 255, cv2.THRESH_BINARY)
            num11=cv2.morphologyEx(num11,cv2.MORPH_OPEN,kernal,iterations=3)
            num11=cv2.dilate(num11,kernal2)

    num11 = cv2.erode(num11, kernal)
    num11=num11+cannynum1
    num11=cv2.morphologyEx(num11,cv2.MORPH_CLOSE,kernal2,iterations=1)
    if num11.shape[0]-num11.shape[1]>0:
        weight0=int((num11.shape[0]-num11.shape[1])/2)+2
        num11 = cv2.copyMakeBorder(num11, 1, 1, weight0, weight0, cv2.BORDER_CONSTANT, value=[0, 0, 0])
    num11=cv2.resize(num11,(18,18))
    num11 = cv2.copyMakeBorder(num11, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=[0, 0, 0])
    cv2.imshow('num ', num11)

    ###### cv2.imwrite('compare\\'+file[cnt][:-4]+'_11.tif',num11)
    # chars_train.append(num11)
    label = file[cnt][-6]
    if  file[cnt][-8] == '(':
        label = file[cnt][-7]
    # print('label1:{0}'.format(label))
    # chars_label.append(int(label))

    test = deskew(num11)
    pre1 = preprocess_hog([test])
    text1 = model.predict(pre1)
    # print('text1:{0}'.format(int(text1[0])))
    num_predict = text1[0].astype(int)
    if label!=str(int(text1[0])):
        print('not match num1:{0}'.format(int(text1[0])))
    # cv2.imwrite('after_img\\'+file[cnt][:-4]+'_1.tif', num11)

    #数字分割2
    num2 = np.zeros(1)
    y_his[yloc:yloc + Weighty] = np.zeros(Weighty)
    num = 1
    if np.sum(y_his) > 100:
        yloc1 = 0
        yloc_r = 0
        Weighty2 = 0
        for ycnt in range(len(y_his) - 1):
            if y_his[ycnt] == 0 and y_his[ycnt + 1] != 0:
                yloc_r = ycnt
            if y_his[ycnt] != 0 and y_his[ycnt + 1] == 0 and (ycnt - yloc_r) > Weighty2:
                Weighty2 = ycnt - yloc_r
                yloc1 = yloc_r
            if Weighty2 > 10:
                num = 2
        if num == 2 :
            num2 = imgCanny[:, yloc1:]

            x_his = np.sum(num2, axis=1)
            xloc = 0
            xloc_r = 0
            Weightx = 0
            for xcnt in range(len(x_his) - 1):
                if (x_his[xcnt] == 0 or xcnt == 0) and x_his[xcnt + 1] != 0:
                    xloc_r = xcnt
                if x_his[xcnt] != 0 and x_his[xcnt + 1] == 0 and (xcnt - xloc_r) > Weightx:
                    Weightx = xcnt - xloc_r
                    xloc = xloc_r
                if Weightx > 10:
                    break
            x3 = xloc
            # print('x0:{0}'.format(x0))
            x4 = len(x_his) - 1
            Weightx = 0
            for xcnt1 in range(len(x_his) - 1):
                xcnt = len(x_his) - xcnt1 - 1
                if (x_his[xcnt] == 0 or xcnt == len(x_his) - 1) and x_his[xcnt - 1] != 0:
                    xloc_r = xcnt
                if x_his[xcnt] != 0 and x_his[xcnt - 1] == 0 and (xloc_r - xcnt) > Weightx:
                    Weightx = xloc_r - xcnt
                    x4 = xloc_r
                if Weightx > 10:
                    break
            # print('xloc:{0}'.format(x1))
            # num2 = num2[x3:x4, :]
            cannynum2 = imgCanny[x3:x4, yloc1:yloc1+Weighty2].copy()
            num22 = img[x3:x4, yloc1:yloc1+Weighty2].copy()
            cv2.imshow('num2', num22)

            ##############cv2.imwrite('compare\\'+file[cnt][:-4]+'_2.tif',num22)

            number_pix = num22.shape[0] * num22.shape[1] + 1  # 修正除数为0的情况
            B = num22[:, :, 0].copy()
            G = num22[:, :, 1].copy()
            R = num22[:, :, 2].copy()

            Bhist = cv2.calcHist([B], [0], None, [256], [0, 256]).flatten()
            Ghist = cv2.calcHist([G], [0], None, [256], [0, 256]).flatten()
            Rhist = cv2.calcHist([R], [0], None, [256], [0, 256]).flatten()

            B_RATE = np.sum(Bhist[180:]) / (number_pix - np.sum(Bhist[:100]))
            G_RATE = np.sum(Ghist[180:]) / (number_pix - np.sum(Ghist[:100]))
            R_RATE = np.sum(Rhist[180:]) / (number_pix - np.sum(Rhist[:100]))
            # print('num2:')
            # print('B white rate:{0}'.format(B_RATE))
            # print('G white rate:{0}'.format(G_RATE))
            # print('R white rate:{0}'.format(R_RATE))
            # print(' ')
            flag = False
            if B_RATE > 0.6:
                if G_RATE > 0.6 or R_RATE > 0.6:
                    flag = True
            elif G_RATE > 0.6 and R_RATE > 0.6:
                flag = True

            if flag == True:
                # print('gray')
                num22 = cv2.cvtColor(num22, cv2.COLOR_BGR2GRAY)
                _, num22 = cv2.threshold(num22, 127, 255, cv2.THRESH_BINARY)
            else:
                color_location = np.argmax([B_RATE, G_RATE, R_RATE])
                if color_location == 0:
                    # print('B')
                    _, num22 = cv2.threshold(B, 180, 255, cv2.THRESH_BINARY)
                    num22=cv2.morphologyEx(num22,cv2.MORPH_OPEN,kernal,iterations=3)
                    num22=cv2.dilate(num22,kernal2)
                elif color_location == 1:
                    # print('G')
                    _, num22 = cv2.threshold(G, 180, 255, cv2.THRESH_BINARY)
                    num22=cv2.morphologyEx(num22,cv2.MORPH_OPEN,kernal,iterations=3)
                    num22=cv2.dilate(num22,kernal2)
                else:
                    # print('R')
                    _, num22 = cv2.threshold(R, 180, 255, cv2.THRESH_BINARY)
                    num22=cv2.morphologyEx(num22,cv2.MORPH_OPEN,kernal,iterations=3)
                    num22=cv2.dilate(num22,kernal2)

            num22 = cv2.erode(num22, kernal)
            num22=num22+cannynum2
            num22=cv2.morphologyEx(num22,cv2.MORPH_CLOSE,kernal2,iterations=1)
            if num22.shape[0] - num22.shape[1] > 0:
                weight0 = int((num22.shape[0] - num22.shape[1]) / 2 + 1)
                num22 = cv2.copyMakeBorder(num22, 0, 0, weight0, weight0, cv2.BORDER_CONSTANT, value=[0, 0, 0])
            num22 = cv2.resize(num22, (18, 18))
            num22 = cv2.copyMakeBorder(num22, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=[0, 0, 0])
            cv2.imshow('num22', num22)

            #############cv2.imwrite('compare\\'+file[cnt][:-4]+'_22.tif',num22)
            # chars_train.append(num22)
            label = file[cnt][-6]
            # print('label2:{0}'.format(label))
            # chars_label.append(int(label))

            test = deskew(num22)
            pre1 = preprocess_hog([test])
            text2 = model.predict(pre1)
            # print('text2:{0}'.format(int(text2[0])))
            if label != str(int(text2[0])):
                print('not match num2{0}'.format(int(text2[0])))
            num_predict = num_predict * 10 + text2[0].astype(int)
            # cv2.imwrite('after_img\\'+file[cnt][:-4]+'_2.tif', num22)

    # print('num_predict:{0}'.format(num_predict))
    if cv2.waitKey(1) == 27:
        cv2.destroyAllWindows()
        pass

    # B_num=np.arange(len(x_his))
    # G_num=np.arange(len(y_his))
    # FIG=plt.figure()
    # plt.subplot(121)
    # plt.bar(B_num,x_his,0.5,color='green')
    # plt.subplot(122)
    # plt.bar(G_num,y_his,0.5,color='green')
    # plt.savefig('canny\\hist'+file[cnt][:-4]+'.tif')
    # np.savetxt('tdf.txt',R,fmt='%d')

    #
    # cv2.imshow('B',B)
    # cv2.imshow('G',G)
    # cv2.imshow('R',R)
    #
    # if B_RATE>0.4 and G_RATE>0.4 and R_RATE>0.4 :
    #     print(file[cnt]+'has white region as main color')
    # #     pass
    # # else :
    # #     print('begin·')
    # #     for i in range(B.shape[0]):
    # #         for ii in range(B.shape[1]):
    # #             if B[i][ii]>200 and R[i][ii]>200 and R[i][ii]>200:
    # #                 B[i][ii] -=150
    # #                 G[i][ii] -=150
    # #                 R[i][ii] -=150
    # #             if B[i][ii]>100 and R[i][ii]>100 and R[i][ii]>100:
    # #                 B[i][ii] -=80
    # #                 G[i][ii] -=80
    # #                 R[i][ii] -=80
    #
    #
    # _, img1 = cv2.threshold(G, 180, 255, cv2.THRESH_BINARY)
    # _, img2 = cv2.threshold(R, 180, 255, cv2.THRESH_BINARY)
    # _, img3 = cv2.threshold(B, 180, 255, cv2.THRESH_BINARY)
    #
    # img1=cv2.morphologyEx(img1,cv2.MORPH_OPEN,kernal,iterations=2)
    # img2=cv2.morphologyEx(img2,cv2.MORPH_OPEN,kernal,iterations=2)
    # img3=cv2.morphologyEx(img3,cv2.MORPH_OPEN,kernal,iterations=2)
    #
    #
    # cv2.imshow('B1',img3)
    # cv2.imshow('G1',img1)
    # cv2.imshow('R2',img2)

# jh=os.listdir('after_img')
# for ij in range(len(jh)):
#     label=jh[ij][0]
#     img=cv2.imread('after_img\\'+jh[ij])
#     img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#     chars_train.append(img)
#     chars_label.append(int(label))
# chars_train = list(map(deskew, chars_train))
# chars_train = preprocess_hog(chars_train)
# chars_label = np.array(chars_label)
# model.train(chars_train , chars_label)
# model.save("svm2.dat")

# print(file[cnt])
# B_num=np.arange(len(Bhist))
# G_num=np.arange(len(Ghist))
# R_num=np.arange(len(Rhist))
# FIG=plt.figure()
# plt.subplot(131)
# plt.bar(B_num,Bhist,0.5,color='green')
# plt.subplot(132)
# plt.bar(G_num,Ghist,0.5,color='green')
# plt.subplot(133)
# plt.bar(R_num,Rhist,0.5,color='green')
# plt.savefig('hist\\'+file[cnt][:-4]+'.tif')
# print(B_num)


'''
Python Flow Application
'''

alist = [(3,4),(6,8),(10,1)]
result = 0
for x,y in alist:
    result = result + (x * y)

print(result)

'''
enumerate
'''

blist = [1,3,-7,4,9,-5,4]
result = enumerate(blist)

print(result)

clist = [2,4,6,8]
dlist = [1,3,5,7]

print("================================================================")
eresult = zip(clist,dlist)
new_collection = tuple(eresult)
print(new_collection)
cresult = 0
for a,b in new_collection:
    cresult = cresult + (a + b)

print(cresult)

print("================================================================")

value = [1,2,3,4]
value_squared = []
for item in value:
    value_squared.append(item * item)

print(value_squared)


"""
The distance between 2 binary strings is the sum of their lengths after removing the common prefix. For example: the common prefix of 1011000 and 1011110 is 1011 so the distance is len("000") + len("110") = 3 + 3 = 6.

Given a list of binary strings, pick a pair that gives you maximum distance among all possible pair and return that distance.
"""
def binary_distance(x: 'binary string', y: 'binary string') -> 'distance':
    def find_prefix(x, y, n):
        if not x or not y: return n
        # print(f'STATUS: x:{x}, y:{y}, n:{n}')
        if x[0] == y[0]:
            return find_prefix(x[1:], y[1:], n+1)
        else:
            return n
    prefix_len = find_prefix(x, y, 0)
    x, y = x[prefix_len:], y[prefix_len:]
    return len(x) + len(y)

# test
x = '1011000'
y = '1011110'
print(f'testing with: x:{x}, y:{y}')
print(f'ANS: {binary_distance(x, y)}')


'''
#此部分为利用注意力机制拟合训练的数据，权值是根据x值来确定的，测试数据和训练数据之间的相似度来确定权值，最后用该权值得到拟合的y值
import torch
from torch import nn
import matplotlib.pyplot as plt

n_train = 50  # 训练样本数
x_train, _ = torch.sort(torch.rand(n_train) * 5)   # 排序后的训练样本,返回顺序后的tensor以及对应的索引切片
#print(x_train,_)
#print(torch.normal(0.0, 0.5,(2,2)))
def f(x):
    return 2 * torch.sin(x) + x**0.8
y_train = f(x_train) + torch.normal(0.0, 0.5, (n_train,))  # 训练样本的输出
print(y_train.size())
x_test = torch.arange(0, 5, 0.1)  # 测试样本  生成数字0开始5结束步长0.1
y_truth = f(x_test)  # 测试样本的真实输出
n_test = len(x_test)  # 测试样本数
plt.plot(x_test,y_truth,'g')
plt.plot(x_train,y_train,'o')
X_repeat = x_test.repeat_interleave(n_train).reshape((-1, n_train))
#print(X_repeat.size())
attention_weights = nn.functional.softmax(-(X_repeat - x_train)**2 / 2, dim=1)
y_hat = torch.matmul(attention_weights, y_train)
plt.plot(x_test,y_hat,'y')
plt.show()
'''
import matplotlib.pyplot as plt

'''
#随机噪声拟合曲线（效果非常差）
import torch
from torch import nn
import matplotlib.pyplot as plt

x_test = torch.arange(0, 5, 0.1)
n_train = 50  # 训练样本数
def f(x):
    return 2 * torch.sin(x) + x**0.8
y_test = f(x_test)  # 训练样本的输出
n_test = len(x_test)  # 测试样本数
x_train, _ = torch.sort(torch.rand(n_train) * 5)
y_train=torch.normal(0.0, 1, (n_train,))
X_repeat = x_test.repeat_interleave(n_train).reshape((-1, n_train))
#print(X_repeat.size())
attention_weights = nn.functional.softmax(-(X_repeat - x_test)**2 / 2, dim=1)
y_hat = torch.matmul(attention_weights, y_train)
plt.plot(x_train,y_hat,'y')
plt.plot(x_train,y_train,'o')
plt.plot(x_test,y_test,'r')
#plt.show()
'''
'''
#矩阵相乘
import torch
weights = torch.ones((2, 10)) * 0.1
values = torch.arange(20.0).reshape((10,2))
q=torch.bmm(weights.unsqueeze(0), values.unsqueeze(0))
print(q.size())'''

import matplotlib.pyplot as plt
import torch
from torch import nn
class NWKernelRegression(nn.Module):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.w = nn.Parameter(torch.rand((1,), requires_grad=True)) #除了卷积全连接什么的有训练参数，要加入额外的参数
        #就用这个函数，同模型一起训练跟新

    def forward(self, queries, keys, values):
        # queries和attention_weights的形状为(查询个数，“键－值”对个数)
        queries = queries.repeat_interleave(keys.shape[1]).reshape((-1, keys.shape[1]))  #确保查询个数等于键值对数，列式个数和键值对
        self.attention_weights = nn.functional.softmax(
            -((queries - keys) * self.w)**2 / 2, dim=1)
        # values的形状为(查询个数，“键－值”对个数)
        return torch.bmm(self.attention_weights.unsqueeze(1),
                         values.unsqueeze(-1)).reshape(-1)

x_test = torch.arange(0, 5, 0.1)
n_test = len(x_test)
n_train=50
x_train, _ = torch.sort(torch.rand(n_train) * 5)
def f(x):
    return 2 * torch.sin(x) + x**0.8
y_test = f(x_test)
y_train = f(x_train) + torch.normal(0.0, 0.5, (n_train,))

#train
#这里是设置键值对,是用来训练网络的训练参数
# X_tile的形状:(n_train，n_train)，每一行都包含着相同的训练输入
X_tile = x_train.repeat((n_train, 1)) #维度不同第0维添加1维，将行复制n_train次列复制一次就是不变
# Y_tile的形状:(n_train，n_train)，每一行都包含着相同的训练输出
Y_tile = y_train.repeat((n_train, 1))
# keys的形状:('n_train'，'n_train'-1)
#keys = X_tile[(1 - torch.eye(n_train)).type(torch.bool)].reshape((n_train, -1))
keys = X_tile.reshape((n_train, -1))
print(x_train)
print(X_tile)
print(keys)
# values的形状:('n_train'，'n_train'-1)
#alues = Y_tile[(1 - torch.eye(n_train)).type(torch.bool)].reshape((n_train, -1))
values = Y_tile.reshape((n_train, -1))
#定义网络优化器损失函数
net = NWKernelRegression()
loss = nn.MSELoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=0.5)
#animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[1, 5])
#开始训练
for epoch in range(5):
    trainer.zero_grad()
    l = loss(net(x_train, keys, values), y_train)
    l.sum().backward()
    trainer.step()
    print(f'epoch {epoch + 1}, loss {float(l.sum()):.6f}')
    #animator.add(epoch + 1, float(l.sum()))

#test
#这里是测试参数并且查看测试结果
# keys的形状:(n_test，n_train)，每一行包含着相同的训练输入（例如，相同的键）
keys = x_train.repeat((n_test, 1))
# value的形状:(n_test，n_train)
values = y_train.repeat((n_test, 1))
y_hat = net(x_test, keys, values).unsqueeze(1).detach()
plt.plot(x_test,y_test,'r')
plt.plot(x_train,y_train,'o')
plt.plot(x_train,y_hat,'g')
plt.show()

#print(keys)
#print(keys.shape[1].reshape((-1, keys.shape[1])))
'''keys = X_tile[(1 - torch.eye(n_train)).type(torch.bool)]
print(X_tile)
print(keys)
a=torch.tensor([1,2,3])

c=a[True,True,True]
print("C",c)'''

#print((1 - torch.eye(n_train)).type(torch.bool).reshape((n_train, -1).shape()))
'''print(torch.rand((1,)))
queries=torch.randn(5)
keys=torch.tensor([[1,2]
                   [2,3]])
queries = queries.repeat_interleave(keys.shape[1]).reshape((-1, keys.shape[1]))
print(queries)'''
#看net就知道这里是要将输入的维度转变成和键值对一样的维数也就是说先必须和键一样，因为要拿查询q和所有的键来做相似度计算
#前面得到了每一个的权值然后要和v相乘计算所以维度要和v的维度能做矩阵相乘


from django.db import migrations, models
from ..helpers.date_time_without_tz_field import DateTimeWithoutTZField

class Migration(migrations.Migration):

    dependencies = [
        ('app', '0004_journeys'),
    ]

    operations = [
        migrations.CreateModel(
            name='JourneyDriver',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),

                ('datetime_start', DateTimeWithoutTZField()),
                ('states', models.PositiveSmallIntegerField()),

                (
                    'journey',
                    models.ForeignKey(
                        on_delete=models.deletion.DO_NOTHING,
                        to='app.Journey',
                    ),
                ),
                (
                    'driver',
                    models.ForeignKey(
                        on_delete=models.deletion.DO_NOTHING,
                        to='app.Driver',
                    ),
                ),

                ('created_at', DateTimeWithoutTZField(auto_now_add=True)),
                ('updated_at', DateTimeWithoutTZField(null=True)),
            ],
        ),
    ]


powerset = []
def powerSet(states_set, res ,n):
    global powerset
    if n == 0:
        cpyres = []
        cpyres.extend(res)
        powerset.append(cpyres)
        print(powerset)
    else:
        res.append(states_set[n-1])
        powerSet(states_set, res, n - 1)
        res.pop()
        powerSet(states_set, res, n - 1)

def convertNFAtoDFA(nfa):
    # preluam componentele nfa ului
    sigma_nfa = nfa[0]
    states_nfa = nfa[1]
    start_state_nfa = nfa[2]
    final_states_nfa = nfa[3]
    actions_nfa = nfa[4]

    # stabilim componentele dfa ului: dfa ul va fi o lista ce va contine [alfabetul, starile, starea de start, starile finale, tranzitiile]

    # alfabetul este acelasi
    sigma_dfa = sigma_nfa
    print(sigma_dfa)

    # starile dfa ului = power set (starile nfa ului)
    global powerset
    powerset = []
    res = []
    powerSet(states_nfa, res, len(states_nfa))
    states_dfa = powerset
    print(states_dfa)

    # stabilim starea de start
    start_state_dfa = [start_state_nfa]
    for state in start_state_dfa:
        for t in actions_nfa:
            if t[0] == state and t[1] == '*':
                print(t[2])
                start_state_dfa.append(t[2])
    print(start_state_dfa)

    # stabilim starile de final
    final_states_dfa = []
    for s in states_dfa:
        for f in final_states_nfa:
            if f in s:
                final_states_dfa.append(s)
    print(final_states_dfa)

    # stabilim tranzitiile
    actions_dfa = []
    for R in states_dfa:
        for symbol in sigma_dfa:
            lista = []
            # starile la care se ajunge prin simbol
            for r in R:
                for action in actions_nfa:
                    if action[0] == r and action[1] == symbol and action[2] not in lista:
                        lista.append(action[2])

            # starile la care se ajunge prin epsilon
            for stare in lista:
                for action in actions_nfa:
                    if action[0] == stare and action[1] == '*' and action[2] not in lista:
                        lista.append(action[2])

            # am gasit tranzitia, o adaugam la lista dfa ului
            if not lista:
                actions_dfa.append((R, symbol, []))
            else:
                actions_dfa.append((R, symbol, lista))

    return [sigma_dfa, states_dfa, start_state_dfa, final_states_dfa, actions_dfa]


import sys
sys.stdin = open('mag.txt')

T = 10
for test_case in range(1, T+1):
    N = int(input())
    mat = [list(map(int, input().split())) for i in range(N)]
    total = 0
    for x in range(N):
        NS = ''
        for y in range(N):
            if mat[y][x] != 0:
                NS += str(mat[y][x])
        for i in range(len(NS)-1):
            if NS[i]+NS[i+1]=='12':
                total += 1
    print('#{} {}'.format(test_case,total))


from typing import Any, Dict
from django.db import models
from django.shortcuts import render, get_object_or_404
from django.views import generic
from django.contrib.auth.models import User
from django.utils import timezone
from django.http import HttpResponse, HttpResponseRedirect
import datetime
from django.core.paginator import Paginator

from .models import Blog, Comment, Reply, BlogForm
from .forms import CommentForm, ReplyForm, SearchForm

# Create your views here.

class IndexView(generic.ListView):
    template_name = "blogs/index.html"
    context_object_name = "latest_blog_list"
    paginate_by = 3
    model = Blog

    def get_queryset(self):
        return Blog.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")

    def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
        context = super(IndexView, self).get_context_data(**kwargs)
        context['latest'] = Blog.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[:5]
        return context

class DetailView(generic.DetailView):
    model = Blog
    template_name = "blogs/details.html"

    def get_context_data(self, *args, **kwargs):
        context = super(DetailView, self).get_context_data(*args, **kwargs)
        context['blogs'] = Blog.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[:5]
        context['comments'] = Comment.objects.filter(blog_id=self.kwargs['pk'])#blog__pk=9)#context['blogs'].values('id'))
        context['replys'] = []

        all_replies = Reply.objects.all()
        for comment in context['comments']:
            for reply in all_replies:
                if reply.comment.comment_text == comment.comment_text:
                    context['replys'].append(reply)
        context['form_r'] = ReplyForm()
        context['form'] = CommentForm()
        context['search_form'] = SearchForm()
        return context

def createView(request):
    context = {}
    context['form'] = BlogForm()
    return render(request, "blogs/create.html", context)

def like(request, blog_id):
    blog = get_object_or_404(Blog, pk=blog_id)
    blog.likes += 1

def createBlog(request):
    if request.method == 'POST':
        #request.POST['back_img'] = 'images/597_1.jpg'
        form = BlogForm(request.POST, request.FILES)
        if form.is_valid():
            blog = form.save(commit=False)
            user = request.user
            blog.author = user
            blog.save()
        else:
            print(form.errors.as_data())
    return HttpResponseRedirect("/blogs/")

def post(request, blog_id):
    blog = get_object_or_404(Blog, pk=blog_id)
    new_comment = Comment()
    new_comment.comment_text = request.POST['comment_text']
    new_comment.pub_date = datetime.datetime.now()
    new_comment.author = request.user
    new_comment.blog_id = blog_id
    new_comment.save()
    blog.comment_set.add(new_comment)
    return HttpResponseRedirect(f"/blogs/{blog_id}/")

def reply(request, comment_id):
    comment = get_object_or_404(Comment, pk=comment_id)
    new_reply = Reply()
    new_reply.content = request.POST['content']
    new_reply.pub_date = datetime.datetime.now()
    new_reply.author = request.user
    comment.reply_set.add(new_reply)
    new_reply.save()
    comment.reply_set.add(new_reply)
    return HttpResponseRedirect(f"/blogs/")

def search(request):
    form = SearchForm(request.GET)
    results = []
    latest = Blog.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[:5]
    popular = Blog.objects.filter(pub_date__lte=timezone.now()).order_by("likes")[:5]
    trending = Blog.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[:5]

    if form.is_valid():
        query = form.cleaned_data['query']
        results = Blog.objects.filter(title__contains=query)

    return render(request, 'search/search.html', {'form':form, 'results': results, 'latest':latest, 'popular':popular, 'trending':trending})


#coding=utf-8
########################
# exportTQL_for_Remedy_to_UCMDB.py
# author: D. Orbach
# last modified: Vinay Seshadri - Aug 13, 2010
########################

# common framework imports
import sys
import string
import traceback
from java.lang import *
from java.util import *
from org.jdom import *
from org.jdom.input import *
from org.jdom.output import *
from java.io import *
from com.hp.ucmdb.discovery.probe.util import HostKeyUtil
from com.hp.ucmdb.discovery.probe.util import NetworkXmlUtil
from appilog.common.utils.parser import OperatorParser
from appilog.common.utils import Protocol
from appilog.common.system.defines import AppilogTypes
from com.hp.ucmdb.discovery.library.common import CollectorsParameters
# Integration imports from wrapper.jar
from com.hp.ucmdb.adapters.push9 import IntegrationAPI

# log4j stuff
from org.apache.log4j import Category

# get the logger instance for this script
logger = Category.getInstance('PATTERNS_DEBUG')

if logger.isInfoEnabled():
	logger.info('Start exportTQL_for_SIM_to_UCMDB.py')

########################
# a convenient print debug method
########################
def dbg(msg):
	if 1:
		info(msg)
	elif logger.isDebugEnabled():
		logger.debug(msg)

########################
# a convenient print info method
########################
def info(msg):
	if logger.isInfoEnabled():
		logger.info(msg)

########################
# a convenient strip method
########################
def strip(data):
	index = data.find ('({')
	if index != -1:
		data = data[0:index]
	return data

##############
## a convenient way to concatenate strings w/ any object type
##############
def concatenate(*args):
	return ''.join(map(str,args))

def exportTQL (ip, userExtDir):
	integrationAPI = IntegrationAPI (ip, "exportTQL_for_SIM_to_UCMDB.py")
	integrationAPI.processDir(userExtDir)

########################
#                      #
# MAIN ENTRY POINT     #
#                      #
########################

def DiscoveryMain(Framework):
	fileSeparator = File.separator
	# Destination Data
	userExtUcmdbDir = CollectorsParameters.BASE_PROBE_MGR_DIR + CollectorsParameters.getDiscoveryResourceFolder() + fileSeparator + 'TQLExport' + fileSeparator + 'hpsim' + fileSeparator

	inputFilesDirectory = File(userExtUcmdbDir + 'inter' + fileSeparator)
	inputFiles = inputFilesDirectory.listFiles()

	filePathDir = userExtUcmdbDir + 'results' + fileSeparator
	directory = File(filePathDir)
	files = directory.listFiles()

	## Clean up the existing result XML files
	if (files != None):
		for file in files:
			file.delete()

	## Make sure we have XML files in the intermediate directory
	xmlFileInIntermediatesDirectory = 0
	for inputFile in inputFiles:
		inputFileName = inputFile.getName()
		if inputFileName[len(inputFileName)-4:].lower() == '.xml' and inputFile.length() > 0:
			xmlFileInIntermediatesDirectory = 1
	if not xmlFileInIntermediatesDirectory:
		logger.warn('Intermediate XML not found or invalid. Perhaps no data was received from SIM or an error occurred in the SIM_Discovery script.')
		return

	## Connect to the UCMDB Server, retrieve the results of the TQL
	## and generate the output XML files in results directory
	ip = CollectorsParameters.getValue(CollectorsParameters.KEY_SERVER_NAME)
	exportTQL(ip, userExtUcmdbDir)
	info('End exportTQL_for_SIM_to_UCMDB.py')


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

from odoo import models, fields, api

class ir_attachment_ihr(models.Model):
    _inherit = 'ir.attachment'

    @api.model
    def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
        if self.env.user.has_group('prinizi_modifier_accessright.group_nv_kho') or \
            self.env.user.has_group('tts_modifier_access_right.group_ql_kho'):
            return super(ir_attachment_ihr, self.sudo())._search(args, offset=offset, limit=limit, order=order,
                                                    count=count, access_rights_uid=access_rights_uid)
        else:
            return super(ir_attachment_ihr, self)._search(args, offset=offset, limit=limit, order=order,
                                                                 count=count, access_rights_uid=access_rights_uid)


from PIL import Image
import os

def convert_to_jpeg(file_path):
    # check if the file exists
    if not os.path.isfile(file_path):
        raise ValueError(f"File {file_path} does not exist.")

    # get the file extension
    file_extension = os.path.splitext(file_path)[1].lower()

    # check if the file extension is supported
    if file_extension not in ['.png', '.jpg', '.jpeg', '.bmp', '.gif']:
        raise ValueError(f"File extension {file_extension} is not supported.")

    # open the image and convert it to JPEG
    with Image.open(file_path) as img:
        img = img.convert('RGB')
        new_file_path = os.path.splitext(file_path)[0] + '.jpeg'
        img.save(new_file_path, 'JPEG', quality=90)

    # return the path of the new JPEG file
    return new_file_path
#TO-pdf
import subprocess
import os

def convert_to_pdf(file_path):
    # check if the file exists
    if not os.path.isfile(file_path):
        raise ValueError(f"File {file_path} does not exist.")

    # get the file extension
    file_extension = os.path.splitext(file_path)[1].lower()

    # check if the file extension is supported
    if file_extension not in ['.doc', '.docx', '.ppt', '.pptx', '.odt', '.ods', '.odp', '.xls', '.xlsx']:
        raise ValueError(f"File extension {file_extension} is not supported.")

    # convert the file to PDF using unoconv
    subprocess.run(['unoconv', '-f', 'pdf', file_path])

    # get the path of the new PDF file
    pdf_file_path = os.path.splitext(file_path)[0] + '.pdf'

    # check if the PDF file was created
    if not os.path.isfile(pdf_file_path):
        raise ValueError("PDF conversion failed.")

    # return the path of the new PDF file
    return pdf_file_path

from fpdf import FPDF
import os

def convert_to_pdf(file_path):
    # Get the name and extension of the file
    file_name, file_extension = os.path.splitext(file_path)

    # Check if the file is already a PDF
    if file_extension.lower() == '.pdf':
        return file_path

    # Create a new PDF object
    pdf = FPDF()

    # Add a page to the PDF
    pdf.add_page()

    # Set the font and font size
    pdf.set_font('Arial', size=12)

    # Read the file contents
    with open(file_path, 'rb') as f:
        file_contents = f.read()

    # Add the file contents to the PDF
    pdf.cell(200, 10, txt=file_contents)


from sir_modelling.simulation import simulate_sir_epidemics

import numpy as np

def approximate_state(state, approximation_threshold = 0.01):
    St, It, Rt = state

    # use integers to avoid floating point problems
    approximated_St_percentile = int(np.trunc(St / approximation_threshold))
    approximated_It_percentile = int(np.trunc(It / approximation_threshold))
    approximated_Rt_percentile = int(np.trunc(Rt / approximation_threshold))

    if approximated_St_percentile < 0:
        # suscetible derivative can return a negative value sometimes
        # in these cases assumes 0 for St and add it on infected compartment
        approximated_It_percentile += (-1 * approximated_St_percentile)
        approximated_St_percentile = 0

    precision = int(1.0 / approximation_threshold)
    residual = precision - (approximated_St_percentile + approximated_It_percentile + approximated_Rt_percentile)

    if residual != 0:
        # sometimes we can have a residual due the truncation procedure
        # in that case assume the residual on chain start
        if approximated_St_percentile == 0:
            approximated_It_percentile += residual
        else:
            approximated_St_percentile += residual

    return (
        approximated_St_percentile * approximation_threshold,
        approximated_It_percentile * approximation_threshold,
        approximated_Rt_percentile * approximation_threshold
    )

def simulate_transition(state, beta, gamma, approximation_threshold, steps_per_transition = 1):
    t, S, I, R = simulate_sir_epidemics(
        infected_people_per_day = beta,
        infection_duration = 1.0 / gamma,
        days_of_simulation = (steps_per_transition + 1),
        initial_state = state
    )

    state = ( S[-1], I[-1], R[-1] )

    return approximate_state(state, approximation_threshold)

def enumerate_states(approximation_threshold):
    # should include 0 and 1 in values (this is why we have division + 1 values)
    precision = int(1.0 / approximation_threshold)
    divisions = precision + 1

    states = []

    for s_percentile in range(0, divisions):
        for i_percentile in range(0, divisions):
            r_percentile = precision - (s_percentile + i_percentile)
            if r_percentile < 0: continue

            susceptibles = s_percentile * approximation_threshold
            infective = i_percentile * approximation_threshold
            recovered = r_percentile * approximation_threshold

            states.append( (susceptibles, infective, recovered) )

    return states

def enumerate_transitions(approximation_threshold, beta, gamma, states, steps_per_transition):
    transitions_for_beta = []

    for state in states:
        next_state = simulate_transition(state, beta, gamma, approximation_threshold, steps_per_transition)
        transitions_for_beta.append( (state, next_state, 1.0) )

    return transitions_for_beta

def enumerate_reward(approximation_threshold, beta, states, reward_function):
    reward_per_state = []

    for state in states:
        susceptibles, infective, recovered = state

        reward = reward_function(susceptibles, infective, recovered, beta) / approximation_threshold
        reward_per_state.append( (state, reward) )

    return reward_per_state

def create_representation(approximation_threshold, gamma, betas, steps_per_transition = 1, reward_function = None):
    if reward_function is None:
        reward_function = lambda susceptibles, infective, recovered, beta: 10 * susceptibles + 5 * recovered - 15 * infective

    states = enumerate_states(approximation_threshold)

    transitions_per_beta = {}
    rewards_per_beta = {}

    # for each beta discover deterministic transitions
    for beta in betas:
        transitions_per_beta[beta] = enumerate_transitions(approximation_threshold, beta, gamma, states, steps_per_transition)
        rewards_per_beta[beta] = enumerate_reward(approximation_threshold, beta, states, reward_function)

    return states, transitions_per_beta, rewards_per_beta


# SECURITY WARNING: keep the secret key used in production secret!
import os
from yap_api.settings import BASE_DIR

SECRET_KEY = os.environ['DJANGO_SECRET_KEY']

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False

ALLOWED_HOSTS = []

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'public', 'static')

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'public', 'media')

CORS_ORIGIN_ALLOW_ALL = True


from setuptools import setup

params = {}
params['scripts'] = ['bin/logwork']

setup(name='logwork',
      version='0.05',
      description='simple work logging with active window monitoring',
      url='',
      author='Diogo Silva',
      author_email='',
      license='',
      packages=['logwork'],
      zip_safe=False,
      **params)


import enum

from ..ui.ui_base import *

from ..utill import image_manager

# origin To end
class ProgressbarDirection(enum.Enum):
    RightToLeft = 0
    DownToTop = 1
    LeftToRight = 2
    TopToDown = 3

class UIProgressBar(BaseUIObject):
    direction_dic = {
        ProgressbarDirection.RightToLeft: 1.0, ProgressbarDirection.DownToTop: 1.0,
        ProgressbarDirection.LeftToRight: -1.0, ProgressbarDirection.TopToDown: -1.0
    }

    def __init__(self, x, y, tag, w=None, h=None, direction: ProgressbarDirection = ProgressbarDirection.LeftToRight):
        super().__init__(x, y)
        self.ratio = 1.0
        self.image_tag = tag
        self.image_controller = None
        self.direction = direction

    def load(self):
        self.image_controller = image_manager.get_image_controller(self.image_tag)

    def update_value(self, value: float, value_max: float = 1.0):
        self.ratio = value / value_max

    def draw(self):
        if self.is_visible:
            src_w = self.image_controller.image.w
            src_h = self.image_controller.image.h
            center_calibration_direction = UIProgressBar.direction_dic[self.direction]
            if self.direction in (ProgressbarDirection.LeftToRight, ProgressbarDirection.RightToLeft):
                cal_w = src_w * self.ratio
                cal_h = src_h
                x = self.position[0] + center_calibration_direction * ((1 - self.ratio) * src_w / 2)
                y = self.position[1]
            else:
                cal_w = src_w
                cal_h = src_h * self.ratio
                x = self.position[0]
                y = self.position[1] + center_calibration_direction * ((1 - self.ratio) * src_h / 2)
            self.image_controller.clip_draw_no_animation(0, 0, int(cal_w), int(cal_h), int(x), int(y))


#!/usr/bin/env python3
"""Run a batch of simulations reproducibly.

Usage:
  run_batch.py <command>
  run_batch.py [options] [--] <command>
  run_batch.py [--git [--input_branch=<branch>]... [--results_branch=<branch>]]
               [--archive [--full]] [--class=<class>]
               [--sweep=<sweep>]...
               [--post=<command>]
               [--clean]
               [options] [--] <command>
  run_batch.py --help

Options:
  -h --help                 Show this screen.
  -g --git                  Use Git to checkout input files and record results.
  -a --archive              Save the results of each run in an archive folder.
  -f --full                 Save full data files to archive folder.
  -d --clean                Clean up by deleting results files after completion.
  --class=<class>           Specify the simulation class (see below)
  --input_branch=<branch>   Specify an input branch in Git.
                            Can be specified multiple times to run for multiple
                            input branches.
  --results_branch=<branch> Specify a results branch in Git.
                            Does not allow multiple branches.
  --post=<command>          Specify a post-processing command.
  --sweep=<key:v1,v2,v3...> Specify a parametric sweep with multiple values for
                            parameters leading to multiple runs.
                            Multiple parameters can be specified independently,
                            such as --sweep='a:1,2' --sweep='b:2,4'
                            or connected, like --sweep='(a,b):(1,2),(2,4)'.
                            With separate specification, all combinations are
                            simulated (e.g. (1,2), (1,4), (2,2) and (2,4)).
                            With connected specification, only the given
                            combinations are simulated (e.g. (1,2) and (2,4)).
                            See below for more examples.

Options passed to Reproducible:
  --config <configfile>     Overwrite the location of Reproducible config file.
  --logfile <logfile>       Overwrite the name of the Reproducible log file.
  -l --runlog               Save a run log to file for each Reproducible run.
  -d --devel                Run Reproducible in developer mode.

Options passed to Reproducible run:
  --template <template>     the name of the file(s) that should be treated
                            as a template inside the command;
                            for multiple templates separate with commas
  --src <sourcedir>         check the source code at <sourcedir>
  --build                   build the code from source using `make install`
  --hash <oldhash>          pre-load parameters from this run
  --show                    print out rendered template
  --save                    save the rendered template to file
  --list-parameters         list all parameters that need to be set
  -p <key:value>            for several parameters use k1:v1,k2:v2 syntax

Simulation classes:
  impact                    Simulations with Impact-T or Impact-Z.
                            Input files: *.in *.data *.txt *.xlsx
                            Output files: fort.* *.dst *.plt
  bdsim                     Simulations with BDSIM.
                            Input files: *.gmad *.data *.txt *.xlsx
                            Output files: *.root *.png *.eps
  opal                      Simulations with OPAL.
                            Input files: *.in *.data *.txt *.xlsx
                            Output files: *.h5 *.lbal *.stat *.dat data

Examples:

run_batch.py "echo Hello world"

    Run a single command reproducibly.
    This will call `reproduce run -- "echo Hello world"` without other options.

run_batch.py -l "echo Hello world"

    Pass a command to the reproduce script.
    This will call `reproduce --runlog run -- "echo Hello world"`.

run_batch.py --class=impact -- ImpactTexe

    Run a simulation reproducibly in Impact-T.
    This will call `reproduce run -- ImpactTexe` without any other options.

run_batch.py --git --input_branch=develop --results_branch=results \\
             --class=impact -- ImpactTexe

    First checkout input files from the 'develop' branch and 'simulations.log'
    from the 'results' branch, then run the simulation in Impact, then check
    in 'simulations.log' as a new commit on the 'results' branch.
    The simulation will be run as `reproduce run -- ImpactTexe`.

run_batch.py --git --input_branch=input/full --input_branch=input/nospacecharge
             --results_branch=results --class=impact -- ImpactTexe

    Run the same simulation reproducibly using two different input branches.
    This will result in two separate commits in the results branch.

run_batch.py --template=ImpactT.in --sweep=I:0.0,0.2,0.4,0.6 \\
             --class=impact -- ImpactTexe

    Sweep through the parameter options for beam current I.
    The input file 'ImpactT.in' should be set up as a template with {{I}} in
    place of the beam current, and a separate simulation will be run for each
    parameter value:
        reproduce run --template ImpactT -p I:0.0 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.2 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.4 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.6 -- ImpactTexe
    If `--git` is specified, each run result will be in a separate commit.
    If `--archive` is specified, each run will be archived to a separate folder.
    If `--runlog` is specified, each run will produce a separate log.

run_batch.py --template=ImpactT.in --sweep=I:0.0,0.2,0.4,0.6 --sweep=E:1.0,1.5 \\
             --class=impact -- ImpactTexe

    Double parametric sweep for beam current I and energy E.
    The input file 'ImpactT.in' should be set up as a template with {{I}} in
    place of the beam current, and {{E}} in place of the beam energy.
    All combinations of parameter values for the two sweeps will run in separate
    simulations:
        reproduce run --template ImpactT -p I:0.0,E:1.0 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.2,E:1.0 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.4,E:1.0 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.6,E:1.0 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.0,E:1.5 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.2,E:1.5 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.4,E:1.5 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.6,E:1.5 -- ImpactTexe

run_batch.py --template=ImpactT.in --sweep=(I,E):(0.0,1.0),(0.2,1.0),(0.4,1.5) \\
             --class=impact -- ImpactTexe

    Single parametric sweep for multiple parameters I and E.
    The input file 'ImpactT.in' is set up as a template with {{I}} and {{E}}
    as above, but only the given combinations of parameter values will run:
        reproduce run --template ImpactT -p I:0.0,E:1.0 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.2,E:1.0 -- ImpactTexe
        reproduce run --template ImpactT -p I:0.4,E:1.5 -- ImpactTexe

"""

import sys
import os
import pathlib
import git
import subprocess
import shutil
from datetime import datetime
from docopt import docopt
import itertools
import unicodedata
import re

# User settings
REPRODUCIBLE = '~/Code/Reproducible'
PYENV        = '~/.pyenv'
LOGFILE      = 'simulations.log'
ARCHIVE_LOG  = 'simulation.log'
ARCHIVE_ROOT = '~/Simulations/'

# Utility methods
def get_folder(folder_path = '.'):
    """Get an absolute path object from a given path"""
    return pathlib.Path(folder_path).expanduser().absolute()

def get_date_as_folder_name():
    """Get a date string in YYYY-MM-DD format for making an archive folder"""
    return datetime.today().strftime('%Y-%m-%d')

def get_safe_folder_name(value):
    """Get a name for a folder without special characters."""
    value = unicodedata.normalize('NFKD', value)
    value = value.encode('ascii', 'replace').decode('ascii')
    value = re.sub(r'[^\.\w\s-]', '-', re.sub(r'\?', '_', value)).strip()
    value = re.sub(r'([^0-9])\.([^0-9])', r'\1-\2', value)
    value = re.sub(r'-$', '', re.sub(r'[-\s]+', '-', value))
    return value

def get_archive_folder(archive_root):
    """Get an absolute path object for a new archive folder with today's date"""
    if not archive_root.is_dir():
        raise OSError(f'Archive location not found: {archive_root}')
    else:
        return archive_root.joinpath(get_date_as_folder_name())

def get_python_for_reproducible(reproducible_folder, pyenv_folder):
    """Get the path of the correct version of Python to run Reproducible"""
    if not reproducible_folder.is_dir():
        raise OSError('Reproducible script folder not found: '
                      + str(reproducible_folder))
    if (reproducible_folder.joinpath('.python-version').is_file()
        and pyenv_folder.is_dir()):
        with open(reproducible_folder.joinpath('.python-version'), 'r') as f:
            python_version = f.readline().strip()
        return str(pyenv_folder.joinpath('versions')
                   .joinpath(python_version)
                   .joinpath('bin/python3'))
    else:
        return 'python3'

# Communication methods
def announce(message):
    """Announce a given message."""
    print(str(message))

def announce_error(message):
    """Announce a given error message."""
    print(str(message), file=sys.stderr)

def announce_start(this_run):
    """Announce that a particular run is about to start"""
    print('-----------------------------------------------------------------')
    announce(f'Start of run: {this_run["title"]}')
    print()

def announce_end(this_run):
    """Announce that a particular run has finished"""
    print()
    announce(f'End of run: {this_run["title"]}')
    print('-----------------------------------------------------------------')

# Git methods
def get_git_repo(repo_path):
    """Return a repo object for the Git repo at a given path"""
    if not pathlib.Path(repo_path).is_dir():
        raise OSError(f'Repo folder not found: {repo_path}')
    else:
        return git.Repo(repo_path)

def git_checkout(repo, branch_name):
    """Check out the given branch"""
    if not isinstance(repo, git.Repo):
        raise TypeError(f'Not a valid repo: {repo}')
    repo.heads[branch_name].checkout()

def git_switch(repo, branch_name):
    """Switch branches without checking anything out"""
    if not isinstance(repo, git.Repo):
        raise TypeError(f'Not a valid repo: {repo}')
    repo.head.reference = repo.heads[branch_name]
    repo.head.reset(index=True, working_tree=False)

def git_get_file(repo, branch_name, file_name):
    """Checkout a single file from a given branch"""
    if not isinstance(repo, git.Repo):
        raise TypeError(f'Not a valid repo: {repo}')
    repo.git.checkout(branch_name, '--', file_name)

def git_commit(repo, commit_files, commit_message):
    """Add and commit the given files"""
    if not isinstance(repo, git.Repo):
        raise TypeError(f'Not a valid repo: {repo}')
    repo.index.add(commit_files)
    repo.index.commit(commit_message)

def get_input_branch(repo, given_input_branch):
    """Get a list of all input branches, unless given an override"""
    if given_input_branch:
        if not (   isinstance(given_input_branch, str)
                or isinstance(given_input_branch, list)):
            raise ValueError('Invalid input for input branch: '
                             + str(given_input_branch))
        else:
            input_branch = given_input_branch
    else:
        input_branch = [head.name for head in repo.heads
                        if 'input' in head.name]
        if len(input_branch) == 1:
            input_branch = input_branch[0]
    if isinstance(input_branch, str):
        if input_branch not in [head.name for head in repo.heads]:
            raise ValueError(f'Input branch not found: {input_branch}')
    elif isinstance(input_branch, list):
        for branch in input_branch:
            if branch not in [head.name for head in repo.heads]:
                raise ValueError(f'Input branch not found: {branch}')
    else:
        raise ValueError(f'Invalid value for input branch: {input_branch}')
    return input_branch

def get_results_branch(repo, given_results_branch):
    """Get the default result branch, unless given an override"""
    if given_results_branch:
        if not isinstance(given_results_branch, str):
            raise ValueError('Invalid input for results branch: '
                             + str(given_results_branch))
        else:
            results_branch = given_results_branch
    else:
        results_branch = [head.name for head in repo.heads
                          if 'results' in head.name]
        if len(results_branch) > 1:
            raise ValueError('Ambiguous results branches in Git')
        else:
            results_branch = results_branch[0]
    if results_branch not in [head.name for head in repo.heads]:
        raise ValueError(f'Results branch not found: {results_branch}')
    else:
        return results_branch

def get_commit_files(settings, parameters):
    """Get a list of files to commit to the results branch"""
    patterns = list()
    patterns.append(settings['logfile'])
    return patterns

def get_commit_message(this_run):
    """Create a message string to commit results of the current run"""
    return 'Results for ' + this_run['title']

# Template methods
def get_valid_templates(run_folder, templates):
    """Check which files exist and return lists of valid and invalid files."""
    valid_templates = []
    invalid_templates = []
    for template in templates.split(','):
        if run_folder.joinpath(template).is_file():
            valid_templates.append(template)
        else:
            invalid_templates.append(template)
    return (None if len(valid_templates) == 0 else ','.join(valid_templates),
            None if len(invalid_templates) == 0 else ','.join(invalid_templates))

# Post-processing methods
def post_process(settings, command):
    """Run the given post-processing command in the run folder"""
    folder = settings['current_folder']
    environ = os.environ.copy()
    this_env = str(pathlib.Path.home().joinpath('.pyenv/versions/scripts/bin'))
    environ['PATH'] = environ['PATH'].replace(f'{this_env}:', '')
    environ['PYENV_DIR'] = ''
    environ['PYENV_VERSION'] = ''
    return subprocess.run(command.split(), cwd=folder, env=environ)

# Archive methods
def create_archive_folder(archive_folder):
    """Make a new folder at the given location, or access an existing folder"""
    if not archive_folder.is_dir():
        archive_folder.mkdir(parents=True)

def get_copy_list(simulation_class):
    """Get list of file patterns to copy for a particular simulation type"""
    copy_list = ['*.data', '*.txt', '*.xlsx']
    if simulation_class == 'impact':
        copy_list.append('*.in')
    elif simulation_class == 'bdsim':
        copy_list.append('*.gmad')
    elif simulation_class == 'opal':
        copy_list.append('*.in')
    else:
        copy_list.append('*.in')
    return copy_list

def get_move_list(simulation_class, is_full_archive):
    """Get list of file patterns to move for a particular simulation type"""
    move_list = ['*.png', '*.eps', '*.ps', '*.jpg', '*.log']
    if simulation_class == 'impact':
        move_list.extend(['*.dst', '*.plt', '*.T7out'])
        if is_full_archive:
            move_list.append('fort.*')
    elif simulation_class == 'bdsim':
        if is_full_archive:
            move_list.append('*.root')
    elif simulation_class == 'opal':
        if is_full_archive:
            move_list.extend(['*.h5', '*.lbal', '*.stat', '*.dat', 'data'])
    return move_list

def get_delete_list(simulation_class):
    """Get list of file patterns to delete - same as full archive list."""
    return get_move_list(simulation_class, is_full_archive=True)

def copy_to_archive(run_folder, archive_folder, copy_patterns):
    """Copy files to the given archive folder based on glob patterns"""
    if not run_folder.is_dir():
        raise OSError(f'Cannot access source folder: {run_folder}')
    if not archive_folder.is_dir():
        raise OSError(f'Cannot access archive folder: {archive_folder}')
    if not isinstance(copy_patterns, list):
        raise ValueError(f'Invalid archive copy pattern: {copy_patterns}')
    file_list = set()
    for pattern in copy_patterns:
        file_list.update(set(run_folder.glob(pattern)))
    for this_file in file_list:
        if this_file.name != LOGFILE:
            shutil.copy2(str(this_file), str(archive_folder))

def move_to_archive(run_folder, archive_folder, move_patterns):
    """Move files to the given archive folder based on glob patterns"""
    if not run_folder.is_dir():
        raise OSError(f'Cannot access source folder: {run_folder}')
    if not archive_folder.is_dir():
        raise OSError(f'Cannot access archive folder: {archive_folder}')
    if not isinstance(move_patterns, list):
        raise ValueError(f'Invalid archive move pattern: {move_patterns}')
    file_list = set()
    for pattern in move_patterns:
        file_list.update(set(run_folder.glob(pattern)))
    for this_file in file_list:
        if this_file.name != LOGFILE:
            shutil.move(str(this_file), str(archive_folder))

def move_rendered_templates(run_folder, archive_folder):
    """Save rendered template files to archive folder"""
    if not run_folder.is_dir():
        raise OSError(f'Cannot access source folder: {run_folder}')
    if not archive_folder.is_dir():
        raise OSError(f'Cannot access archive folder: {archive_folder}')
    file_list = run_folder.glob('*.rendered')
    for this_file in file_list:
        new_filename = str(this_file.name).replace('.rendered','')
        shutil.move(str(this_file), str(archive_folder.joinpath(new_filename)))

def archive_log(settings, archive_folder):
    """Get a log of the last run and save it to the archive folder"""
    log_command = [str(settings['python']),
                   str(settings['reproduce']), 'log', '-n1']
    log_output = subprocess.run(log_command,
                                cwd=settings['current_folder'],
                                capture_output=True).stdout
    with open(archive_folder.joinpath(settings['archive_log']), 'wb') as f:
        f.write(log_output)

def archive_output(settings, this_run):
    """Archive the input, output and log files of the latest run"""
    create_archive_folder(this_run['archive'])
    copy_to_archive(settings['current_folder'],
                    this_run['archive'],
                    this_run['archive_copy'])
    move_to_archive(settings['current_folder'],
                    this_run['archive'],
                    this_run['archive_move'])
    move_rendered_templates(settings['current_folder'], this_run['archive'])
    archive_log(settings, this_run['archive'])

def delete_output(settings, this_run):
    """Delete any output files that haven't been archived."""
    delete_list = get_delete_list(this_run['--class'])
    for pattern in delete_list:
        files = settings['current_folder'].glob(pattern)
        for this_file in files:
            if this_file.name != LOGFILE:
                this_file.unlink()

# Sweep methods
def get_sweep_parameters(sweep_definition):
    """Return the parameter name for a given sweep string"""
    if not ':' in sweep_definition:
        raise ValueError('Invalid sweep definition: ' + sweep_definition)
    sweep_parameters = sweep_definition.split(':')[0]
    if ',' in sweep_parameters:
        return sweep_parameters.replace('(', '').replace(')', '').split(',')
    else:
        return [sweep_parameters]

def get_sweep_values(sweep_definition):
    """Return the parameter name for a given sweep string"""
    if not ':' in sweep_definition:
        raise ValueError('Invalid sweep definition: ' + sweep_definition)
    sweep_values = sweep_definition.split(':')[1]
    if '(' in sweep_values:
        return [tuple(this_set.replace('(', '').replace(')', '').split(','))
                for this_set in sweep_values.split('),')]
    else:
        return sweep_values.split(',')

def get_sweep_strings(sweep_definition):
    """Get all single-value parameter strings for a given sweep string"""
    if not ':' in sweep_definition:
        raise ValueError('Invalid sweep definition: ' + sweep_definition)
    sweep_parameters = get_sweep_parameters(sweep_definition)
    if len(sweep_parameters) > 1:
        return [','.join([sweep_parameters[i] + ':' + values[i]
                        for i in range(len(sweep_parameters))])
                for values in get_sweep_values(sweep_definition)]
    else:
        return [sweep_parameters[0] + ':' + value
                for value in get_sweep_values(sweep_definition)]

def get_sweep_combinations(sweeps):
    """Get all combinations of parameter values for multiple sweeps"""
    sweep_strings = [get_sweep_strings(sweep) for sweep in sweeps]
    combinations = itertools.product(*sweep_strings)
    combination_strings = [','.join(item) for item in combinations]
    return combination_strings

# Define run settings and parameters
def get_settings(arguments):
    """Get the required settings as a dictionary"""
    settings = dict()
    settings['current_folder'] = get_folder('.')
    settings['archive_root'] = get_folder(ARCHIVE_ROOT)
    settings['reproducible'] = get_folder(REPRODUCIBLE)
    settings['pyenv'] = get_folder(PYENV)
    settings['python'] = get_python_for_reproducible(settings['reproducible'],
                                                     settings['pyenv'])
    settings['reproduce'] = get_folder(REPRODUCIBLE).joinpath('reproduce')
    settings['logfile'] = LOGFILE
    settings['archive_log'] = ARCHIVE_LOG
    return settings

def get_parameters(settings, arguments):
    """Get the parameters for the batch run based on the given arguments"""
    parameters = arguments.copy()
    if parameters['--archive']:
        parameters['archive'] = (
            get_archive_folder(settings['archive_root']))
        parameters['archive_copy'] = get_copy_list(parameters['--class'])
        parameters['archive_move'] = (
            get_move_list(parameters['--class'], parameters['--full']))
    else:
        parameters['archive'] = None
    if parameters['--git']:
        repo = get_git_repo(settings['current_folder'])
        parameters['--input_branch'] = (
            get_input_branch(repo, arguments['--input_branch']))
        parameters['--results_branch'] = (
            get_results_branch(repo, arguments['--results_branch']))
    return parameters

def get_title(this_run):
    """Create a title string including the run date and main command"""
    if 'title' in this_run:
        if this_run['title']:
            return this_run['title']
    title = ' '.join([datetime.today().strftime('%Y-%m-%d'),
                      this_run['<command>'].split()[0]])
    return title

# Run methods
def reproducible_run(settings, this_run):
    """Run the given command using Reproducible"""
    command = [str(settings['python']), str(settings['reproduce'])]
    if this_run['--config']:
        command.append('--config')
        command.append(this_run['--config'])
    if this_run['--logfile']:
        command.append('--logfile=')
        command.append(this_run['--logfile'])
    if this_run['--runlog']:
        command.append('--runlog')
    if this_run['--devel']:
        command.append('--devel')
    command.append('run')
    if this_run['--template']:
        command.append('--template')
        command.append(this_run['--template'])
    if this_run['--src']:
        command.append('--src')
        command.append(this_run['--src'])
    if this_run['--build']:
        command.append('--build')
    if this_run['--hash']:
        command.append('--hash')
        command.append(this_run['--hash'])
    if this_run['--show']:
        command.append('--show')
    if this_run['--save']:
        command.append('--save')
    if this_run['--list-parameters']:
        command.append('--list-parameters')
    if this_run['-p']:
        command.append('-p')
        command.append(this_run['-p'])
    command.append('--')
    command.append(this_run['<command>'])
    return subprocess.run(command, cwd=settings['current_folder'])

def run_single(settings, this_run):
    """Work through the simulation steps for each individual run"""
    announce_start(this_run)
    if this_run['--git']:
        repo = get_git_repo(settings['current_folder'])
        git_checkout(repo, this_run['--input_branch'])
        git_get_file(repo, this_run['--results_branch'], settings['logfile'])
    if this_run['--template']:
        valid, invalid = get_valid_templates(settings['current_folder'],
                                             this_run['--template'])
        if invalid:
            announce_error(f'Skipping missing templates: {invalid}')
        this_run['--template'] = valid
    reproducible_run(settings, this_run)
    if this_run['--post']:
        post_process(settings, this_run['--post'])
    if this_run['--git']:
        git_switch(repo, this_run['--results_branch'])
        git_commit(repo, this_run['commit_files'], this_run['commit_message'])
        git_switch(repo, this_run['--input_branch'])
    if this_run['--archive']:
        archive_output(settings, this_run)
    if this_run['--clean']:
        delete_output(settings, this_run)
    announce_end(this_run)

def run_with_git(settings, this_run):
    """Run for a single or multiple input branches"""
    this_run['commit_files'] = get_commit_files(settings, this_run)
    this_run['commit_message'] = get_commit_message(this_run)
    if isinstance(this_run['--input_branch'], list):
        for this_branch in this_run['--input_branch']:
            branch_run = this_run.copy()
            branch_run['--input_branch'] = this_branch
            branch_run['title'] = (
                this_run['title'] + ' for branch ' + this_branch)
            branch_run['commit_message'] = (
                this_run['commit_message'] + ' for branch ' + this_branch)
            if branch_run['--archive']:
                branch_run['archive'] = this_run['archive'].joinpath(
                    this_branch.replace('input/', ''))
            run_single(settings, branch_run)
    else:
        run_single(settings, this_run)

# Main batch method
def run_batch(settings, parameters):
    """Run through the batch for different parameter values and input files"""
    batch_run = parameters.copy()
    batch_run['title'] = get_title(batch_run)
    if batch_run['--sweep']:
        for this_combination in get_sweep_combinations(batch_run['--sweep']):
            this_run = batch_run.copy()
            this_run['title'] = batch_run['title'] + ' for ' + this_combination
            if this_run['-p']:
                this_run['-p'] += ',' + this_combination
            else:
                this_run['-p'] = this_combination
            if this_run['--archive']:
                folder_name = get_safe_folder_name(this_combination)
                this_run['archive'] = batch_run['archive'].joinpath(folder_name)
            if this_run['--git']:
                run_with_git(settings, this_run)
            else:
                run_single(settings, this_run)
    else:
        if batch_run['--git']:
            run_with_git(settings, batch_run)
        else:
            run_single(settings, batch_run)

# What to do when run as a script
if __name__ == '__main__':
    arguments = docopt(__doc__)
    settings = get_settings(arguments)
    parameters = get_parameters(settings, arguments)
    run_batch(settings, parameters)