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

import numpy as np

from Utility_functions.plot_validators import *
from Models.Logistic_Regression import *
from prettytable import PrettyTable
from Models.PCA_LDA import *

def validation_LR(DTR, LTR, L, k):
    for l in L:
        for pi in [0.1, 0.5, 0.9]:
            kFold_LR(DTR, LTR, l, k, pi)

'''
 #algoritmo che serve per trovare il miglior hyper parameter L

    x = numpy.logspace(-5, 1, 20)
    y = numpy.array([])
    y_05 = numpy.array([])
    y_09 = numpy.array([])
    y_01 = numpy.array([])
    y_05_PCA = numpy.array([])
    y_09_PCA = numpy.array([])
    y_01_PCA = numpy.array([])

    for xi in x:
        scores, scoresPCA, labels = kFold_LR_calibration(DTR, LTR, xi, k)
        y_09 = numpy.hstack((y_09, compute_dcf_min_effPrior(0.9, scores, labels)))
        y_05 = numpy.hstack((y_05, compute_dcf_min_effPrior(0.5, scores, labels)))
        y_01 = numpy.hstack((y_01, compute_dcf_min_effPrior(0.1, scores, labels)))
        y_09_PCA = numpy.hstack((y_09_PCA, compute_dcf_min_effPrior(0.9, scoresPCA, labels)))
        y_05_PCA = numpy.hstack((y_05_PCA, compute_dcf_min_effPrior(0.5, scoresPCA, labels)))
        y_01_PCA = numpy.hstack((y_01_PCA, compute_dcf_min_effPrior(0.1, scoresPCA, labels)))

    y = numpy.hstack((y, y_09))
    y = numpy.vstack((y, y_05))
    y = numpy.vstack((y, y_01))
    y = numpy.vstack((y, y_09_PCA))
    y = numpy.vstack((y, y_05_PCA))
    y = numpy.vstack((y, y_01_PCA))

    plot_DCF_PCA(x, y, 'lambda', 'LR_PCA_minDCF_comparison', folder='validation/')
'''

def kFold_LR(DTR, LTR, l, k, pi):
    FoldedData_List = numpy.split(DTR, k, axis=1)
    FoldedLabel_List = numpy.split(LTR, k)

    scores_append = []
    PCA_m9_scores = []
    PCA_m8_scores = []
    LR_labels = []

    for fold in range(k):
        Dtr, Ltr, Dte, Lte = kfold(fold, k, FoldedData_List, FoldedLabel_List)

        # Calcolo scores con RAW data
        scores_append.append(weighted_logistic_reg_score(Dtr, Ltr, Dte, l, pi))

        ''' calcolo scores con PCA with m = 9'''
        s, P = PCA(Dtr, 9)
        DTR_PCA = numpy.dot(P.T,Dtr)
        DTE_PCA = numpy.dot(P.T,Dte)
        PCA_m9_scores.append(weighted_logistic_reg_score(DTR_PCA,Ltr,DTE_PCA,l, pi))

        ''' calcolo scores PCA with m = 8'''
        s, P = PCA(Dtr, 8)
        DTR_PCA = numpy.dot(P.T,Dtr)
        DTE_PCA = numpy.dot(P.T,Dte)
        PCA_m8_scores.append(weighted_logistic_reg_score(DTR_PCA, Ltr, DTE_PCA, l, pi))

        LR_labels = np.append(LR_labels, Lte, axis=0)
        LR_labels = np.hstack(LR_labels)

    '''RAW data'''
    evaluation(scores_append, LR_labels, "LR, RAW data ", l, pi)
    '''PCA with m = 9'''
    evaluation(PCA_m9_scores, LR_labels, "LR, PCA m=9, ", l, pi)
    '''PCA with m  = 8'''
    evaluation(PCA_m8_scores, LR_labels, "LR, PCA m=8, ", l, pi)

def evaluation(scores, LR_labels, appendToTitle, l, pi):
    scores_append = np.hstack(scores)
    scores_tot_01 = compute_dcf_min_effPrior(0.1, scores_append, LR_labels)
    scores_tot_05 = compute_dcf_min_effPrior(0.5, scores_append, LR_labels)
    scores_tot_09 = compute_dcf_min_effPrior(0.9, scores_append, LR_labels)

    t = PrettyTable(["Type", "π=0.5", "π=0.1", "π=0.9"])
    t.title = appendToTitle
    t.add_row(['WEIGHTED_LR, lambda=' + str(l) + " π_t=" + str(pi), round(scores_tot_05, 3), round(scores_tot_01, 3), round(scores_tot_09, 3)])
    print(t)

def kFold_LR_calibration(DTR, LTR, l, k):
    FoldedData_List = numpy.split(DTR, k, axis=1)
    FoldedLabel_List = numpy.split(LTR, k)

    scores_append = []
    LR_labels = []
    PCA_m9_scores = []

    for fold in range(k):
        Dtr, Ltr, Dte, Lte = kfold(fold, k, FoldedData_List, FoldedLabel_List)

        scores = weighted_logistic_reg_score(Dtr, Ltr, Dte, l, pi=0.5)
        scores_append.append(scores)

        s, P = PCA(Dtr, 9)
        DTR_PCA = numpy.dot(P.T,Dtr)
        DTE_PCA = numpy.dot(P.T,Dte)
        PCA_m9_scores.append(weighted_logistic_reg_score(DTR_PCA,Ltr,DTE_PCA,l, pi=0.5))

        LR_labels = np.append(LR_labels, Lte, axis=0)
        LR_labels = np.hstack(LR_labels)

    return np.hstack(scores_append), np.hstack(PCA_m9_scores), LR_labels


import os
import logging
import time
import unittest
import uuid
import tempfile
import shutil
from django.core.wsgi import get_wsgi_application
from django.conf import settings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toxapp.settings")
application = get_wsgi_application()

from django.contrib.auth.models import User
from tp.tasks import add, make_leiden_csv, process_user_files
from tp.models import Experiment

logger = logging.getLogger(__name__)

# NOTE - the delays below are appropriate for a test system with no other tasks running
# tests may fail is running on a server with other celery tasks running and this job waiting in the queue

class TestCelery(unittest.TestCase):

    def test_add(self):
        res = add.delay(5, 2)
        time.sleep(5)
        status = res.ready()
        result = res.get(timeout=1)
        logger.info('Obtained celery status %s and result of %s', status, result)
        return self.assertEqual(result, 7)

    def test_make_leiden_csv(self):

        loc = os.path.join(settings.COMPUTATION['url_dir'], 'leiden_exports')
        if not os.path.isdir(loc):
            os.makedirs(loc)

        code = uuid.uuid4().hex
        localf = code + '.csv'
        fullfile = os.path.join(loc, localf)

        # get the first experiment
        exp = Experiment.objects.first()
        res = make_leiden_csv.delay(fullfile, [exp.id])

        time.sleep(40)
        status = res.ready()
        result = res.get(timeout=1)
        logger.info('Obtained celery status %s', status)
        return self.assertTrue(status) and self.assertTrue(result)

    def test_process_user_files(self):

        tmpdir = os.path.join(tempfile.gettempdir(), '{}'.format(hash(time.time())))
        sourcedir = os.path.join(settings.BASE_DIR, 'tests/test_data/')
        if not os.path.exists(sourcedir):
            logging.error('Test data directory %s is not readable', sourcedir)
            exit(1)

        shutil.copytree(sourcedir, tmpdir)
        logger.debug('temp directory is %s', tmpdir)
        user = User.objects.get(username='admin')
        config_file = os.path.join(tmpdir, 'computation_data.json')

        logger.debug('Running process_user_files')
        res = process_user_files.delay(tmpdir, config_file, user.email, True)
        time.sleep(20)
        status = res.ready()
        message = res.get(timeout=1)

        logger.info('Obtained celery status %s and message %s', status, message)
        return self.assertTrue(status) and self.assertTrue(message)

if __name__ == '__main__':
    unittest.main()


# # Put the following in your ipython default profile OR
# ... for PyCharm
# # Load ipython default config
# config_file = r'~/Code/dotfiles/ipython_science_config.py
# exec(open(config_file).read())

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
pd.set_option('display.max_columns', 12)
pd.set_option('display.max_rows', 10)
pd.set_option('display.width', 500)

#get_ipython().magic('precision %.3e')# Floats to display 3 decimal places in exponential notation

custom_style = {
    'axes.edgecolor': '#FAFAFA',
    'axes.facecolor': '#313131',
    'figure.facecolor': '#313131',
    'axes.labelcolor': '#FAFAFA',
    'axes.grid': True,
    'grid.linestyle': u'-',
    'axes.linewidth': 1.5,
    'grid.color': '#717171',
    'text.color': '#FAFAFA',
    'xtick.color': '#FAFAFA',
    'ytick.color': '#FAFAFA',
    'legend.numpoints': 2
}
# import warnings
# warnings.filterwarnings('always')
#import seaborn as sns
#sns.set_style("darkgrid", rc=custom_style)
#PALERED = '#d65f5f'
#RED = sns.xkcd_rgb["pale red"]
#BLUE = '#5C81C4'
#DARKBLUE = sns.xkcd_rgb["denim blue"]
#GREEN = sns.xkcd_rgb["medium green"]

# Use a theme and fix the plotting
# jt -t chesterish  # Install a theme
from jupyterthemes import jtplot
jtplot.style()


import pandas as pd
import matplotlib.pyplot as plt
import re

df = pd.read_csv("P&V-Daniel Torres\steamGames.csv",
                 names=["Nombre", "Precio", "Fecha", "Lenguajes", "Desarrollador", "Generos", "Rating", "Link"],
                 header=0)

for index, row in df.iterrows():
    row["Rating"] = pd.to_numeric(row["Rating"]);

#¿Cuántos juegos tienen una valoración mayor al 50% y una valoración menor al 50%?
def pregunta1(df):
    higher = 0
    lower = 0

    for index, row in df.iterrows():
        if row["Rating"] >= 50:
            higher += 1
        else:
            lower += 1

    print("Mayores a 50%: {}".format(higher))
    print("Menores a 50%: {}".format(lower))

    labels = ['Mayor a 50%', 'Menor a 50%']
    values = [higher, lower]

    plt.barh(labels, values)

    for i, v in enumerate(values):
        plt.text(v, i, str(v),va='center', ha='left')

    plt.show()

#¿Cuáles son los 10 juegos mejor puntuados del género de Supervivencia/Accion?
def pregunta2(df):
    df = df.sort_values("Rating",ascending=False)
    values = list()
    labels = list()
    count = 0
    for index, row in df.iterrows():

        if count == 10:
            break

        genre = str(row["Generos"])
        if genre != "nan":
            print("genero")
            print(genre)

            if genre.lower().find("action"):
                labels.append(row["Nombre"])
                values.append(row["Rating"])
                count += 1

    print(labels)
    print(values)
    plt.bar(labels,values)
    for i, v in enumerate(values):
        plt.text(i - 0.25, v + 0.5, str(v))
    plt.show()

#¿Cuántos juegos fueron lanzados por año (en orden descendente)?
def pregunta3(df):

    year_dict = dict()

    for index, row in df.iterrows():

        print('fecha')
        print(row['Fecha'])

        result = re.search(r'\d{4}', row['Fecha'])
        if result:

            year = result.group()

            if year in year_dict.keys():
                year_dict[year] +=1
            else:
                year_dict[year] = 1

    print(year_dict)

    sorted_dict = sorted(year_dict.items(), key=lambda x: x[1], reverse=True)
    year_dict = dict(sorted_dict)

    labels = list(year_dict.keys())
    values = list(year_dict.values())

    plt.bar(labels,values)
    for i, v in enumerate(values):
        plt.text(i - 0.25, v + 0.5, str(v))
    plt.show()

# 3 funciones para las preguntas 1, 2, y 3 repectivamente segun la reparticion de preguntas.

# pregunta1(df)
# pregunta2(df)
# pregunta3(df)


from os.path import exists
from docx2pdf import convert #pip install docx2pdf
from comtypes import client #pip install comtypes
import winreg

#https://github.com/robsonlimadeveloper/msoffice2pdf/blob/develop/src/msoffice2pdf/__init__.py

from AbstractPdfConverter import *

class UknowExtensionError(Exception): ...

##
## Class to convert file to pdf based on MS-Office
class MsOfficeConverter(AbstractPdfConverter)  :

  def __init__(self, skipeCheck=False):
    AbstractPdfConverter.__init__(self)
    self.versionNum = self.__getMicrosoftWordVersion()

    if(not skipeCheck) :
      if(self.versionNum == 0) :
        raise ModuleNotFoundError(f"MS-Office not found")

  def __getMicrosoftWordVersion(self):
    key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Office", 0, winreg.KEY_READ)
    versionNum = 0
    i = 0
    while True:
        try:
            subkey = winreg.EnumKey(key, i)
            i+=1
            if versionNum < float(subkey):
                versionNum = float(subkey)
        except: #relies on error handling WindowsError as e as well as type conversion when we run out of numbers
            break
    return versionNum

  ##
  ## Function to convert file to pdf
  ## input_file : the path of document
  ## output_folder : the output path folder
  def convert_to_pdf(self, input_file, output_folder):

    ## Checks
    if(self.versionNum == 0) :
        raise ValueError(f"MS-Office not found")

    if(not exists(input_file)) :
        raise FileNotFoundError(f"File {input_file} not found")

    fileName, ext = os.path.splitext(input_file)
    outputFile = output_folder+"/"+fileName+".pdf"

    if ext in [".doc", ".docx", ".txt", ".xml"]:
      self.__convert_Word_to_pdf(input_file, outputFile)
    elif ext in [".xls", ".xlsx"]:
      self.__convert_Excel_to_pdf(input_file, outputFile)
    elif ext in [".ppt", ".pptx"]:
      self.__convert_PowerPoint_to_pdf(input_file, outputFile)
    else :
      raise UknowExtensionError(f"File {input_file} not found")

  ##
  ## Function to convert Word document to pdf
  ## input_file : the path of document
  ## output_folder : the output path folder
  def __convert_Word_to_pdf(self, input_file, outputFile):
    # convert(input_file, outputFile)
    ws_pdf_format: int = 17
    app = client.CreateObject("Word.Application")
    try:
      doc = app.Documents.Open(input_file)
      doc.ExportAsFixedFormat(outputFile, ws_pdf_format, Item=7, CreateBookmarks=0)
    finally:
      app.Quit()

  ##
  ## Function to convert Excel document to pdf
  ## input_file : the path of document
  ## output_folder : the output path folder
  def __convert_Excel_to_pdf(self, input_file, outputFile):
    app = client.CreateObject("Excel.Application")
    try:
      sheets = app.Workbooks.Open(input_file)
      sheets.ExportAsFixedFormat(0, outputFile)
    finally:
      app.Quit()

  ##
  ## Function to convert powerPoint document to pdf
  ## input_file : the path of document
  ## output_folder : the output path folder
  def __convert_PowerPoint_to_pdf(self, input_file, outputFile):
    app = client.CreateObject("PowerPoint.Application")
    try:
      obj = app.Presentations.Open(input_file, False, False, False)
      obj.ExportAsFixedFormat(outputFile, 2, PrintRange=None)
    finally:
      app.Quit()


#!/usr/bin/env python

import argparse
import os
import subprocess
import tempfile

def get_s3_cfg():
    return os.environ["S3CFG"] if "S3CFG" in os.environ else "%s/%s" % (os.environ["HOME"], ".s3cfg")

def parse_build_between( build_between_info ):
    """
    parse the build between in format "[start_time-]end_time", the start_time and end_time is in
    format "d+[s|m|M|w|h|d|y]"
    """
    def to_seconds( build_time_info ):
        """
        convert the build time into a seconds

        Args:
            build_time_info - the build time in format "d+[s|m|M|w|h|d|y]", d stands for digital, s - seconds,
            m - minutes, M - months, w - weeks, h - hours, d - days, y - years
        Returns:
            the build time in seconds
        """
        time_units = {'s': 1, 'm': 60, 'M': 31*24*3600, 'h': 3600, 'w':7*24*3600,'d': 24*3600, 'y': 365*24*3600}
        return int(build_time_info) if build_time_info.isdigit() else (int(build_time_info[0:-1]) * time_units[ build_time_info[-1] ] )

    #if no start time
    pos = build_between_info.find( '-' )
    if pos == -1:
        return (-1, to_seconds( build_between_info ) )
    else:
        start_time = to_seconds( build_between_info[0:pos] ) if len( build_between_info[0:pos] )  > 0 else -1
        end_time = to_seconds( build_between_info[pos+1:] ) if len( build_between_info[pos+1:] ) > 0 else -1
        return (start_time, end_time )

def is_build_between( build_time, build_between):
    """
    check if the image is build between
    """
    if build_between[0] <= 0:
        return True if build_between[1] <= 0 else build_time <= build_between[1]
    else:
        if build_time < build_between[0]:
            return False

        return True if build_between[1] <= 0 else build_time <= build_between[1]

def get_images_build_between( build_between_info ):
    """
    get all images build matches build_between_info

    Args:
        build_between_info - the image build between information in format "start-end"
    Returns:
        all the images whose build time matches the build_between_info
    """
    out = subprocess.check_output( ['docker', 'images'] )
    time_units = {'seconds': 1, 'minutes': 60, 'hours': 3600, 'days': 24 * 3600, 'years': 365*24*3600, 'weeks': 7 * 24 * 3600, 'months': 31*24*3600 }
    build_between= parse_build_between( build_between_info )
    result = []
    for lineno, line in enumerate(out.split("\n")):
        if lineno == 0: continue
        words = line.split()
        if len( words ) > 5 and words[3].isdigit() and words[4] in time_units:
            build_time = time_units[ words[4] ] * int( words[3] )
            if is_build_between( build_time, build_between):
                result.append( words[0] + ":" + words[1] )
    return result

def save_images( args ):
    if args.images:
        images = args.images
    elif args.build_between:
        images = get_images_build_between( args.build_between)
    else:
        images = []

    for image in images:
        save_image( image, args.dest, args.public, config = args.config )

def save_image( image, dest, public, config = None ):
    base_image_name = image.split( '/' )[-1]
    tmp = base_image_name.split(':' )
    if dest.startswith( 's3://'):
        dest_dir = os.path.abspath( tempfile.mkdtemp() )
    else:
        dest_dir = os.path.abspath( dest )
    if not os.path.exists( dest_dir ):
        os.makedirs( dest_dir )

    filename = '%s/%s-%s.tar' % ( dest_dir, tmp[0], tmp[1] if len( tmp ) == 2 else 'latest' )
    print( "try to save image %s to file %s" % (image, filename ) )
    os.system( "docker save -o %s %s" % ( filename, image ) )
    print( "compress file %s" % filename )
    os.system( "gzip %s" % filename )
    filename = "%s.gz" % filename
    if dest.startswith( 's3://' ):
        command = "s3cmd -c %s put %s %s %s" % ( config or get_s3_cfg(), filename, "-P" if public else "", dest )
        print( command )
        os.system( command )
        os.remove( filename )
        os.removedirs( dest_dir )

def load_images( args ):
    for src in args.src:
        load_image( src, args.force, config = args.config )

def list_containers():
    result = []
    out = subprocess.check_output( ['docker', 'ps', '-a'] )
    for index, line in enumerate( out.split( "\n" ) ):
        if index > 0 and len(line) > 0 and not line[0].isspace():
            words = line.split()
            if len( words ) > 3:
                result.append( {'container-id': words[0], 'image-id': words[1], 'status': 'running' if line.find( "Exited" ) == -1 else 'exited'} )
    return result

def find_container_with_image( image ):
    result = []
    image_id = find_image_id( image )
    containers = list_containers()
    for container in containers:
        if container['image-id'] == image_id or container['image-id'] == image:
            result.append( container )
    return result

def list_images():
    result = []
    out = subprocess.check_output( ['docker', 'images'] )
    for index, line in enumerate( out.split("\n" ) ):
        if index != 0:
            words = line.split()
            if len( words ) > 4:
                result.append( {'image': "%s:%s" % (words[0], words[1]), 'image-id': words[3] } )
    return result

def find_image_id( image ):
    images = list_images()
    for image_info in images:
        if image_info['image'] == image:
            return image_info['image-id']
    return None

def remove_image( image ):
    """
    remove the docker image

    Args:
        image - the docker image with version
    """
    containers = find_container_with_image( image )
    for container in containers:
        if container['status'] == 'exited':
            os.system( 'docker rm %s' % container['container-id'] )
    os.system( 'docker rmi %s' % image )

def load_image( src, force, config = None ):
    """
    load docker image

    Args:
        src - the docker image source, the source can be: local file, s3 file
    """
    if src.startswith( 's3://' ):
        dest_dir = os.path.abspath( tempfile.mkdtemp() )
        os.system( "s3cmd -c %s get %s %s" % ( config or get_s3_cfg(), src, dest_dir ) )
        src_file = "%s/%s" % (dest_dir, os.path.basename( src ) )
    else:
        src_file = src
    images = list_images() if force else []
    command = ['docker', 'load', '-i', src_file]
    out = subprocess.check_output( command )
    result = parse_image_load_result( out )
    for image_info in images:
        if result['image'] == image_info['image']:
            remove_image( result['image'] )
            os.system( ' '.join( command ) )
            break

    if src.startswith( 's3://' ):
        os.remove( src_file )
        os.removedirs( dest_dir )

def parse_image_load_result( result ):
    layers = []
    image = ""
    for line in result.split( "\n" ):
        print( line )
        pos = line.find( ": Loading layer" )
        if pos != -1:
            layers.append( line[0:pos] )
        else:
            pos = line.find( "Loaded image:")
            if pos != -1:
                image = line[pos+len( "Loaded image:"):].strip()
    return { 'layers': layers, 'image': image }

def remove_images( args ):
    if args.images:
        images = args.images
    elif args.build_between:
        images = get_images_build_between( args.build_between)
    else:
        images = []
    for image in images:
        remove_image( image )

def parse_args():
    parser = argparse.ArgumentParser( description = "docker tools")
    parser.add_argument( "-c", "--config", help = "the .s3cfg configuration file" )
    subparsers = parser.add_subparsers( help = "docker tools" )
    save_parser = subparsers.add_parser( "save", help = "save save images")
    save_parser.add_argument( "-P", "--public", help = "share the image", action = "store_true" )
    save_parser.add_argument( "--build-between", help = 'the image build between time in format "[start-]end" , the "start" and "end" is in format "d+[s|m|h|d|w|M|y" (s:seonds,m:minutes,h:hours,d:days,M:months,y:years), example: 10m(build less than 10 minutes), 10m-20m(build between 10 minutes to 20 minutes), 10m- (build greater than 10 minutes)', required = False )
    save_parser.add_argument( "dest", default = ".", help = "the destination to save the image, the destination can be a directory, s3 storage")
    save_parser.add_argument( "--images", nargs='*', help = "the image with version", required = False )
    save_parser.set_defaults( func = save_images )
    load_parser = subparsers.add_parser( "load", help = "load docker images")
    load_parser.add_argument( "src", nargs='+', help = "load docker image" )
    load_parser.add_argument( "--force", action="store_true", help="force to load the image")
    load_parser.set_defaults( func = load_images )
    rmi_parser = subparsers.add_parser( "rmi", help = "remove docker images")
    rmi_parser.add_argument( "--build-between", help = 'the image build between time in format "[start-]end" , the "start" and "end" is in format "d+[s|m|h|d|w|M|y" (s:seonds,m:minutes,h:hours,d:days,M:months,y:years), example: 10m(build less than 10 minutes), 10m-20m(build between 10 minutes to 20 minutes), 10m- (build greater than 10 minutes)', required = False )
    rmi_parser.add_argument( "--images", nargs='*', help = "docker image with version", required = False )
    rmi_parser.set_defaults( func = remove_images )

    return parser.parse_args()

def main():
    args = parse_args()
    args.func( args )

if __name__ == "__main__":
    main()


import asyncio
import json

from confluent_kafka import Consumer

from src import logger
from src.mappers.prediction_mapers import json_msg_to_prediction
from src.repository.prediction_repository import PredictionRepository

class KafkaConsumer:
    def __init__(
        self,
        bootstrap_servers: str,
        topic_name: str,
        prediction_repository: PredictionRepository,
    ) -> None:
        self.bootstrap_servers = bootstrap_servers
        self.topic_name = topic_name
        self.prediction_repository = prediction_repository
        self.config = {
            "bootstrap.servers": self.bootstrap_servers,
            "group.id": "dataservice",
            "auto.offset.reset": "earliest",
        }
        self.consumer = Consumer(self.config)
        logger.info(
            "Subscribing to %s kafka topic using config=%s",
            self.topic_name,
            str(self.config),
        )
        self.consumer.subscribe([self.topic_name])

    def consume_and_save_topic_predictions(self):
        while True:
            msg = self.consumer.poll(1.0)
            if msg is None:
                continue
            if msg.error():
                logger.error("Consumer error: %s", msg.error())
                continue
            json_msg = json.loads(msg.value().decode("utf-8"))
            logger.debug("Received message: %s", {str(json_msg)})
            prediction = json_msg_to_prediction(json_msg)
            self.prediction_repository.save_prediction(prediction)

    def start_kafka_consumer(self):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.run_until_complete(self.consume_and_save_topic_predictions())


# -*- coding: UTF-8 -*-

import requests
import sys, os
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
import time
import threading
import psutil
import logging
from logging.handlers import RotatingFileHandler
import BusTimeToDisplay

# Define running folder
if os.name == 'nt':
    ProgramFolder = 'C:\\Users\\rober\\ideaProjects\\HongKongBus\\src\\main\\HongKongBusPythonDisplay\\'
    from Adafruit_RGBmatrix import Adafruit_RGBmatrix
else:
    ProgramFolder = "/home/pi/Downloads/rpi-rgb-led-matrix-master/"
    from rgbmatrix import Adafruit_RGBmatrix

# Logging mechanism
myLogger = logging.getLogger('RotatingLogs')
myHandler = RotatingFileHandler(ProgramFolder + "CityBus.log", maxBytes=20000000, backupCount=5)
myHandler.setLevel(logging.INFO)
myHandler.setFormatter(logging.Formatter('(%(threadName)s) %(asctime)s %(message)s'))
myLogger.addHandler(myHandler)
myLogger.setLevel(logging.INFO)

# Define colors
DarkRed = (50, 0, 0)
LightRed = (100, 0, 0)
DarkWhite = (100, 100, 100)
PreciousCyan = (102, 245, 173)

# Root URL
rootUrl = "https://hong-kong-bus-us.herokuapp.com"
# rootUrl = "http://127.0.0.1:5000"

# Global variable to be used between the 2 threads
NextArrivalTimes = []

def GetDisplayColor(busNumber):
    displayColor = (0, 0, 0)
    if busNumber == '11' or busNumber == '-1':
        displayColor = DarkRed
    if busNumber == '511':
        displayColor = LightRed
    return displayColor

def GetCityBusProcess():
    global myLogger
    for myProcess in psutil.process_iter():
        if 'sudo' in myProcess.cmdline():
            if 'python' in myProcess.cmdline():
                if ProgramFolder + 'CityBus.py' in myProcess.cmdline() or 'CityBus.py' in myProcess.cmdline():
                    if myProcess.pid == os.getppid():
                        myLogger.debug(
                            "Found sudo, python, and CityBuys.py process, but only current process, no already "
                            "running one")
                    else:
                        myLogger.debug("Found CityBuys.py process")
                        return myProcess
                else:
                    myLogger.debug("Found sudo and python, but not CityBus")
            else:
                myLogger.debug("Found sudo, but not python")
    return None

def RefreshBusTimeData():
    global NextArrivalTimes
    global myLogger

    # sessionIdWhichIKnowEqualsOne = "222"
    sessionIdWhichIKnowEqualsOne = requests.post(rootUrl + '/users/login', data={"userName": "pi"}).json()
    myLogger.info("Logged in as pi, sessionId = %s",sessionIdWhichIKnowEqualsOne)
    answerToChangeConfigName = requests.post(rootUrl + '/sessions/changeConfigName', data={"sessionId": sessionIdWhichIKnowEqualsOne, "configName":"CastleDown   🏰 👇"})
    if answerToChangeConfigName.status_code == 200:
        myLogger.info("%s : SUCCESS",answerToChangeConfigName)
    else:
        myLogger.error("Couldn't changeConfigName - error code %s, error message %s", answerToChangeConfigName.status_code, answerToChangeConfigName.text)

    FoundArrivalTimes = []
    while True:
        try:
            myLogger.info("Beginning of While True Loop")
            FoundArrivalTimes = []
            response = requests.get(rootUrl + '/busTimes/nextFor?sessionId='+str(sessionIdWhichIKnowEqualsOne), timeout=60)
            if response.status_code == 200:
                data = response.json()
                for obj in data.get('arrivalTimes'):
                    FoundArrivalTimes.append(
                        BusTimeToDisplay.BusTimeToDisplay(obj.get('busNumber'), obj.get('arrivalTime'), obj.get('distance'),
                                                          obj.get('isAnError'), GetDisplayColor(obj.get('busNumber'))))
                myLogger.info("RefreshBusTimeData successfully retrieved %s elements", len(FoundArrivalTimes))
            else:
                myLogger.error("Received Status Code = %s when trying to refresh NextBusTimes", response.status_code)
                if response.status_code == 401:
                    # I need to login again
                    myLogger.info("I will try to login as pi user again")
                    responseBis = requests.post(rootUrl + '/users/login', data={"userName": "pi"})
                    if responseBis.status_code != 200 :
                        myLogger.error("Couldn't relogin for pi : status = %s, message = ", responseBis.status_code, responseBis.text)
                    else:
                        myLogger.info("Successfully re logged in as pi")
                        myLogger.info("I will try to change my configBusName to CastleDown")
                        responseBis = requests.post(rootUrl + '/sessions/changeConfigName', data={"sessionId": sessionIdWhichIKnowEqualsOne, "configName":"CastleDown   🏰 👇"})
                        if responseBis.status_code != 200 :
                            myLogger.error("Couldn't change busConfigName after relogging in : status = %s, message = ", responseBis.status_code, responseBis.text)
                        else:
                            myLogger.info("Successfully change busConfigName to CastleDown")
                else:
                    myLogger.error("I don't know how to handle this exception : code %s, message %s", response.status_code, response.text)
        except requests.ConnectionError as err:
            FoundArrivalTimes.append(
                BusTimeToDisplay.BusTimeToDisplay('0', 'OFFLN', 'OFFLN', DarkRed)
            )
            myLogger.error('Connection error while refreshing BusTimeData')
            myLogger.exception(err)
            time.sleep(30)
        except requests.exceptions.Timeout as err:
            FoundArrivalTimes.append(
                BusTimeToDisplay.BusTimeToDisplay('0', 'TMOUT', 'TMOUT', DarkRed)
            )
            myLogger.error('Time out while refreshing BusTimeData')
            myLogger.exception(err)
            time.sleep(30)
        except requests.exceptions.RequestException as err:
            myLogger.error("Couldn't GET the BusTimeData")
            myLogger.exception(err)
        except Exception as err:
            myLogger.error("Unmanaged exception caught during GET nextBusTimesFor - response code %s",
                           str(err))
        finally:
            NextArrivalTimes = sorted(FoundArrivalTimes,
                                      key=lambda myBusTime: myBusTime.arrivalTime.replace("00:", "24:"))
            time.sleep(30)

def KeepDisplayUpdated():
    global NextArrivalTimes
    global myLogger

    # Rows and chain length are both required parameters:
    matrix = Adafruit_RGBmatrix(32, 2)

    localNextArrivalTimesToAvoidConcurrencyIssues = []

    myLogger.info("Starting KeepDisplayUpdated")
    fontSize = 10
    # font = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeSans.ttf", fontSize)
    # font = ImageFont.truetype("/home/pi/Downloads/5by7.ttf", fontSize)
    # font = ImageFont.truetype("/home/pi/Downloads/ufonts.com_subway-ticker.ttf", fontSize)
    font = ImageFont.truetype(ProgramFolder + "LEDCounter7.ttf", fontSize)
    displaying = 'arrivalTime'

    while True:
        myImage = Image.new("RGB", (64, 32), "black")
        myDraw = ImageDraw.Draw(myImage)
        myDraw.text((19, 0), time.strftime('%H:%M', time.localtime()), DarkWhite, font=font)

        localNextArrivalTimesToAvoidConcurrencyIssues = NextArrivalTimes
        for nextArrivalTime in NextArrivalTimes:
            myLogger.debug(nextArrivalTime)

        if len(localNextArrivalTimesToAvoidConcurrencyIssues) == 0:
            myDraw.text((3, fontSize), "No bus", DarkRed, font=font)
        elif len(localNextArrivalTimesToAvoidConcurrencyIssues) == 1:
            if localNextArrivalTimesToAvoidConcurrencyIssues[0].isAnError:
                # myDraw.text((1, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].arrivalTime,
                myDraw.text((3, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[0].color, font=font)
            else:
                if displaying == 'arrivalTime':
                    myDraw.text((3, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].arrivalTime,
                                localNextArrivalTimesToAvoidConcurrencyIssues[0].color, font=font)
                else:
                    myDraw.text((3, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].distance,
                                localNextArrivalTimesToAvoidConcurrencyIssues[0].color, font=font)
        elif len(localNextArrivalTimesToAvoidConcurrencyIssues) == 2:
            if displaying == 'arrivalTime':
                myDraw.text((3, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[0].color, font=font)
                myDraw.text((35, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[1].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[1].color, font=font)
            else:
                myDraw.text((3, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].distance,
                            localNextArrivalTimesToAvoidConcurrencyIssues[0].color, font=font)
                myDraw.text((35, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[1].distance,
                            localNextArrivalTimesToAvoidConcurrencyIssues[1].color, font=font)
        elif len(localNextArrivalTimesToAvoidConcurrencyIssues) == 3:
            if displaying == 'arrivalTime':
                myDraw.text((3, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[0].color, font=font)
                myDraw.text((35, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[1].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[1].color, font=font)
                myDraw.text((3, fontSize * 2), localNextArrivalTimesToAvoidConcurrencyIssues[2].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[2].color, font=font)
            else:
                myDraw.text((3, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].distance,
                            localNextArrivalTimesToAvoidConcurrencyIssues[0].color, font=font)
                myDraw.text((35, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[1].distance,
                            localNextArrivalTimesToAvoidConcurrencyIssues[1].color, font=font)
                myDraw.text((3, fontSize * 2), localNextArrivalTimesToAvoidConcurrencyIssues[2].distance,
                            localNextArrivalTimesToAvoidConcurrencyIssues[2].color, font=font)
        else:
            if displaying == 'arrivalTime':
                myDraw.text((3, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[0].color, font=font)
                myDraw.text((35, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[1].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[1].color, font=font)
                myDraw.text((3, fontSize * 2), localNextArrivalTimesToAvoidConcurrencyIssues[2].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[2].color, font=font)
                myDraw.text((35, fontSize * 2), localNextArrivalTimesToAvoidConcurrencyIssues[3].arrivalTime,
                            localNextArrivalTimesToAvoidConcurrencyIssues[3].color, font=font)
            else:
                myDraw.text((3, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[0].distance,
                            localNextArrivalTimesToAvoidConcurrencyIssues[0].color, font=font)
                myDraw.text((35, fontSize), localNextArrivalTimesToAvoidConcurrencyIssues[1].distance,
                            localNextArrivalTimesToAvoidConcurrencyIssues[1].color, font=font)
                myDraw.text((3, fontSize * 2), localNextArrivalTimesToAvoidConcurrencyIssues[2].distance,
                            localNextArrivalTimesToAvoidConcurrencyIssues[2].color, font=font)
                myDraw.text((35, fontSize * 2), localNextArrivalTimesToAvoidConcurrencyIssues[3].distance,
                            localNextArrivalTimesToAvoidConcurrencyIssues[3].color, font=font)

        myLogger.debug("DisplayUpdated : Before RGBMatrix.Clear")
        matrix.Clear()

        myLogger.debug("DisplayUpdated : Before RGBMatrix.SetImage")
        matrix.SetImage(myImage.im.id, 0, 0)

        if displaying == 'arrivalTime':
            myLogger.debug("KeepDisplayUpdated sleeping for 3 seconds")
            time.sleep(3)
            myLogger.debug("KeepDisplayUpdated arrivalTime SLEEP DONE")
            displaying = 'distance'
        else:
            myLogger.debug("KeepDisplayUpdated sleeping for 1 second")
            time.sleep(1)
            myLogger.debug("KeepDisplayUpdated distance SLEEP DONE")
            displaying = 'arrivalTime'

def ThreadManager():
    global myLogger

    # wakes up every 5 minutes to check if the 2 threads are running
    # restarts missing ones if needed

    print("Arrived in ThreadManager")
    myLogger.warning("Arrived in ThreadManager")

    myRefreshBusTimeDataThread = threading.Thread(name='RefreshBusTimeData', target=RefreshBusTimeData)
    myKeepDisplayUpdatedThread = threading.Thread(name='KeepDisplayUpdated', target=KeepDisplayUpdated)

    while True:
        print("ThreadManager While True Loop started")
        if not myRefreshBusTimeDataThread.isAlive():
            myRefreshBusTimeDataThread.start()
            myLogger.info('ThreadManager : RefreshBusTimeData started')
        # if not myKeepDisplayUpdatedThread.isAlive() and os.name != 'nt':
        if not myKeepDisplayUpdatedThread.isAlive():
            myKeepDisplayUpdatedThread.start()
            myLogger.info('ThreadManager : KeepDisplayUpdated started')
        time.sleep(300)

try:
    # Create 1 thread as follows
    myThreadManagerThread = threading.Thread(name='ThreadManager', target=ThreadManager)

    if os.name == 'nt':
        myThreadManagerThread.start()
        print("Main : CityBus NT started")
        myLogger.warning("CityBus NT ThreadManager Started")
    else:
        myProcess = GetCityBusProcess()
        if len(sys.argv) < 2:
            print("Wrong arguments, you need to specify start or stop")
            myLogger.error("Wrong argument, only start and stop are supported")
            sys.exit()

        # If process is running already
        if myProcess is not None:
            if sys.argv[1] == "start":
                print("CityBus is already running, cannot start a second time")
                myLogger.error("Main : CityBus is already running, cannot start a second time")
            elif sys.argv[1] == "stop":
                myLogger.warning("Main : CityBus stopping")
                myProcess.terminate()
                print("CityBus stopped")
            else:
                print("Wrong argument, only start and stop are supported")
                myLogger.error("Main : Wrong argument, only start and stop are supported")
        else:
            if sys.argv[1] == "start":
                myLogger.warning('Starting')
                myThreadManagerThread.start()
                print("CityBus started")
                myLogger.warning("Main : CityBus ThreadManager Started")
            elif sys.argv[1] == "stop":
                print("CityBus is not running, cannot stop it")
                myLogger.error("Main : CityBus is not running, cannot stop it")
            else:
                print("Wrong argument, only start and stop are supported")
                myLogger.error("Main : Wrong argument, only start and stop are supported")
except:
    myLogger.exception("Unable to run the program")


import scrapy
from scrapy.crawler import CrawlerProcess

class staffs(scrapy.Spider):
	name = 'animePlanet'
	# start_urls = ['https://www.anime-planet.com/people/fumihiko-shimo']
	start_urls = []

	custom_settings = {
		'CONCURRENT_REQUESTS': 100,
		'CONCURRENT_REQUESTS_PER_DOMAIN': 100,
		'USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:39.0) Gecko/20100101 Firefox/39.0'
	}

	def __init__(self, category='', c_type='', **kwargs):

		if self.start_urls != []:
			self.start_urls = []

		self.start_urls.append('https://www.anime-planet.com/people/' + category)

		super().__init__(**kwargs)

	def parse(self, response):

		name = response.xpath("//h1[@itemprop='name']//text()").get()
		image = response.xpath("//div[@class='mainEntry']/img/@src").get()
		text = response.xpath("//div[@class='pure-g entrySynopsis synopsisPeople']//div[@class='pure-1 md-2-3']//text()").getall()
		if 'https' not in image:
			image = 'https://www.anime-planet.com' + image

		yield{
			'Name': name,
			'Image': image,
			'Text':''.join(text).replace('\n','')
		}

		#Known for
		title = response.xpath("//ul[@class='cardDeck cardGrid cardGrid5']/preceding::h3[1]").get()
		length = len(response.xpath("//ul[@class='cardDeck cardGrid cardGrid5']//li"))
		res = []
		for n in range(1, length+1):
			res.append({
				'kfImg': response.xpath(f"//ul[@class='cardDeck cardGrid cardGrid5']//li[{n}]//div[@class='crop']//img//@data-src").get(),
				'kfText': response.xpath(f"//ul[@class='cardDeck cardGrid cardGrid5']//li[{n}]/a//h3//text()").get()
			})
		yield{
			'Known for': res
		}

		#Additional Details
		length = len(response.xpath("//table[@class='pure-table striped expandedDetails']"))
		d = {}
		main_res = []
		for n in range(1, length+1):
			dname = {}
			dname.update({
				'AdTitle': response.xpath(f"//table[@class='pure-table striped expandedDetails'][{n}]/preceding::h3[1]//text()").get()
			})
			trLength = len(response.xpath(f"//table[@class='pure-table striped expandedDetails'][{n}]//tr"))
			d1 = {}
			res = []
			for tr in range(1, trLength+1):
				d1.update({tr:{
					'AdImage': response.xpath(f"//table[@class='pure-table striped expandedDetails'][{n}]//tr[{tr}]//img//@src").get(),
					'AdName': response.xpath(f"//table[@class='pure-table striped expandedDetails'][{n}]//tr[{tr}]//td//b//text()").get()
				}})

			res.append(dname)
			res.append(d1)
			main_res.append({'DATA': res})
		yield{
			'Final': main_res
		}

# if __name__ == '__main__':
# 	process = CrawlerProcess()
# 	process.crawl(staffs, category='Tetsurou ARAKI',c_type='')
# 	process.start()


from dataclasses import dataclass, field
import math
import random
import string
from typing import List, Tuple

import numpy as np
import pandas as pd

from base_logger import logger
from interactibles import Item

FALLBACK_ATTACK_NAME = 'basic'
PARTY_DEATH_PENALTY = 0
ID_LENGTH = 8

@dataclass
class Agent:
    name: str
    position: int
    hp: int
    mp: int
    attack: int
    defense: int = 0
    statuses: List[Tuple[str, int]] = field(default_factory=list) # list of (status_name, time_applied)
    status_resist: float = 0
    special_moves: List[str] = field(default_factory=list)
    has_gone: bool = False
    crit_p: float = 0.05

    def __post_init__(self):
        self.max_hp = self.hp
        self.max_mp = self.mp
        self.special_moves = self.special_moves.strip().split()
        self.id = f"{self.name}@{self.position}"

    def short_repr(self):
        repr_str = ""
        if isinstance(self, Ally):
            repr_str += "PARTY"
        else:
            repr_str += "ENEMY"
        repr_str += f" {self.name} (id: {self.id})"
        return repr_str

    def effect_scale(self, target_field: str, action, action_field: str):
        if action.effect_scaling == 'proportional':
            return getattr(self, target_field) * getattr(action, action_field)
        else:
            return getattr(action, action_field)

    def execute(self, state, action, target_list):
        target_ids = [target.id for target in target_list]
        logger.debug(f"AGENT {self.id} --({action.name})-> AGENT {target_ids}\nParameters:{action.pretty_repr()}")
        reward = 0
        effective_hp_cost = self.effect_scale('hp', action, 'hp_cost')
        effective_mp_cost = self.effect_scale('mp', action, 'mp_cost')
        effective_attack_cost = self.effect_scale('attack', action, 'attack_cost')
        effective_defense_cost = self.effect_scale('defense', action, 'defense_cost')
        effective_hp_delta = self.effect_scale('attack', action, 'hp_delta')

        # TODO: effective_mp/atk/def_delta seems very niche, probably not going to implement now

        self.hp = np.clip(self.hp - effective_hp_cost, 0, self.max_hp)
        self.mp = np.clip(self.mp - effective_mp_cost, 0, self.max_mp)
        self.attack = max(self.attack - effective_attack_cost, 0)
        self.defense = max(self.defense - effective_defense_cost, 0)
        if random.random() < action.status_self_p:
            self.status = action.status_self
        for target in target_list:
            target.hp = np.clip(target.hp + effective_hp_delta, 0, target.max_hp)
            if target.hp == 0:
                info_str = target.short_repr()
                if isinstance(target, Ally):
                    logger.info(f"KILL: {info_str} (-{-PARTY_DEATH_PENALTY})")
                    state.party.remove(target)
                    reward += PARTY_DEATH_PENALTY
                else: # Enemy
                    logger.info(f"KILL: {info_str} (+{target.gold})")
                    state.enemies.remove(target)
                    state.gold += target.gold
                    reward += target.gold
                continue
            target.mp = np.clip(target.mp + action.mp_delta, 0, target.max_mp)
            target.attack = max(target.attack + action.attack_delta, 0)
            target.defense = max(target.defense + action.defense_delta, 0)
            if random.random() < action.status_target_p:
                target.status = action.status_target
        state = self.apply_status_effects(state)

        self.has_gone = True
        state.turns_left -= 1
        state.global_step += 1
        return state, reward

    def apply_status_effects(self, state):
        for agent in state.party + state.enemies:
            for status in agent.statuses:
                if status.check_needed(state.global_step):
                    status.tick()
                    if status.can_escape():
                        agent.statuses.remove(status)
        return state

    def get_candidate_moves(self):
        return self.special_moves

    def get_legal_actions(self, entity_bank):
        action_names = [FALLBACK_ATTACK_NAME]
        for move_name in self.get_candidate_moves():
            if self.mp >= entity_bank.action_data.loc[move_name].get('mp_cost'):
                action_names.append(move_name)
        info_str = self.short_repr()
        logger.debug(f"{info_str} ACTIONS: {action_names}")
        return action_names

    def __eq__(self, other):
        return self is other

@dataclass
class Ally(Agent):
    weapon: Item = None
    armor: Item = None
    items: List[Item] = field(default_factory=list)

    def equip(self, item: Item):
        self.attack += item.attack_bonus
        self.hp += item.hp_bonus
        self.mp += item.mp_bonus
        self.max_hp += item.hp_bonus
        self.max_mp += item.mp_bonus
        self.crit_p += item.crit_bonus
        self.items.append(item)

    def get_candidate_moves(self):
        return self.special_moves + [item.move for item in self.items if not pd.isnull(item.move)]

@dataclass
class Enemy(Agent):
    min_level: int = 0
    max_level: int = 0
    gold: int = 1


import copy
import ctypes
import ctypes.util
from functools import wraps

import numpy
from numpy.ctypeslib import ndpointer
from scipy import interpolate

from ..util import _load_extension_libs, multi
from ..util.conversion import physical_conversion
from .Potential import Potential

_DEBUG = False

_lib, ext_loaded = _load_extension_libs.load_libgalpy()

def scalarVectorDecorator(func):
    """Decorator to return scalar outputs as a set"""

    @wraps(func)
    def scalar_wrapper(*args, **kwargs):
        if (
            numpy.array(args[1]).shape == () and numpy.array(args[2]).shape == ()
        ):  # only if both R and z are scalars
            scalarOut = True
            args = (args[0], numpy.array([args[1]]), numpy.array([args[2]]))
        elif (
            numpy.array(args[1]).shape == () and not numpy.array(args[2]).shape == ()
        ):  # R scalar, z vector
            scalarOut = False
            args = (args[0], args[1] * numpy.ones_like(args[2]), args[2])
        elif (
            not numpy.array(args[1]).shape == () and numpy.array(args[2]).shape == ()
        ):  # R vector, z scalar
            scalarOut = False
            args = (args[0], args[1], args[2] * numpy.ones_like(args[1]))
        else:
            scalarOut = False
        result = func(*args, **kwargs)
        if scalarOut:
            return result[0]
        else:
            return result

    return scalar_wrapper

def zsymDecorator(odd):
    """Decorator to deal with zsym=True input; set odd=True if the function is an odd function of z (like zforce)"""

    def wrapper(func):
        @wraps(func)
        def zsym_wrapper(*args, **kwargs):
            if args[0]._zsym:
                out = func(args[0], args[1], numpy.fabs(args[2]), **kwargs)
            else:
                out = func(*args, **kwargs)
            if odd and args[0]._zsym:
                return sign(args[2]) * out
            else:
                return out

        return zsym_wrapper

    return wrapper

def scalarDecorator(func):
    """Decorator to return scalar output for 1D functions (vcirc,etc.)"""

    @wraps(func)
    def scalar_wrapper(*args, **kwargs):
        if numpy.array(args[1]).shape == ():
            scalarOut = True
            args = (args[0], numpy.array([args[1]]))
        else:
            scalarOut = False
        result = func(*args, **kwargs)
        if scalarOut:
            return result[0]
        else:
            return result

    return scalar_wrapper

class interpRZPotential(Potential):
    """Class that interpolates a given potential on a grid for fast orbit integration"""

    def __init__(
        self,
        RZPot=None,
        rgrid=(numpy.log(0.01), numpy.log(20.0), 101),
        zgrid=(0.0, 1.0, 101),
        logR=True,
        interpPot=False,
        interpRforce=False,
        interpzforce=False,
        interpDens=False,
        interpvcirc=False,
        interpdvcircdr=False,
        interpepifreq=False,
        interpverticalfreq=False,
        ro=None,
        vo=None,
        use_c=False,
        enable_c=False,
        zsym=True,
        numcores=None,
    ):
        """
        Initialize an interpRZPotential instance.

        Parameters
        ----------
        RZPot : RZPotential or list of such instances
            RZPotential to be interpolated.
        rgrid : tuple, optional
            R grid to be given to linspace as in rs= linspace(*rgrid).
        zgrid : tuple, optional
            z grid to be given to linspace as in zs= linspace(*zgrid).
        logR : bool, optional
            If True, rgrid is in the log of R so logrs= linspace(*rgrid).
        interpPot : bool, optional
            If True, interpolate the potential.
        interpRforce : bool, optional
            If True, interpolate the radial force.
        interpzforce : bool, optional
            If True, interpolate the vertical force.
        interpDens : bool, optional
            If True, interpolate the density.
        interpvcirc : bool, optional
            If True, interpolate the circular velocity.
        interpdvcircdr : bool, optional
            If True, interpolate the derivative of the circular velocity with respect to R.
        interpepifreq : bool, optional
            If True, interpolate the epicyclic frequency.
        interpverticalfreq : bool, optional
            If True, interpolate the vertical frequency.
        ro : float, optional
            Distance scale for translation into internal units (default from configuration file).
        vo : float, optional
            Velocity scale for translation into internal units (default from configuration file).
        use_c : bool, optional
            Use C to speed up the calculation of the grid.
        enable_c : bool, optional
            Enable use of C for interpolations.
        zsym : bool, optional
            If True (default), the potential is assumed to be symmetric around z=0 (so you can use, e.g.,  zgrid=(0.,1.,101)).
        numcores : int, optional
            If set to an integer, use this many cores (only used for vcirc, dvcircdR, epifreq, and verticalfreq; NOT NECESSARILY FASTER, TIME TO MAKE SURE).

        Notes
        -----
        - 2010-07-21 - Written - Bovy (NYU)
        - 2013-01-24 - Started with new implementation - Bovy (IAS)

        """
        if isinstance(RZPot, interpRZPotential):
            from ..potential import PotentialError

            raise PotentialError(
                "Cannot setup interpRZPotential with another interpRZPotential"
            )
        # Propagate ro and vo
        roSet = True
        voSet = True
        if ro is None:
            if isinstance(RZPot, list):
                ro = RZPot[0]._ro
                roSet = RZPot[0]._roSet
            else:
                ro = RZPot._ro
                roSet = RZPot._roSet
        if vo is None:
            if isinstance(RZPot, list):
                vo = RZPot[0]._vo
                voSet = RZPot[0]._voSet
            else:
                vo = RZPot._vo
                voSet = RZPot._voSet
        Potential.__init__(self, amp=1.0, ro=ro, vo=vo)
        # Turn off physical if it hadn't been on
        if not roSet:
            self._roSet = False
        if not voSet:
            self._voSet = False
        self._origPot = RZPot
        self._rgrid = numpy.linspace(*rgrid)
        self._logR = logR
        if self._logR:
            self._rgrid = numpy.exp(self._rgrid)
            self._logrgrid = numpy.log(self._rgrid)
        self._zgrid = numpy.linspace(*zgrid)
        self._interpPot = interpPot
        self._interpRforce = interpRforce
        self._interpzforce = interpzforce
        self._interpDens = interpDens
        self._interpvcirc = interpvcirc
        self._interpdvcircdr = interpdvcircdr
        self._interpepifreq = interpepifreq
        self._interpverticalfreq = interpverticalfreq
        self._enable_c = enable_c * ext_loaded
        self.hasC = self._enable_c
        self._zsym = zsym
        if interpPot:
            if use_c * ext_loaded:
                self._potGrid, err = calc_potential_c(
                    self._origPot, self._rgrid, self._zgrid
                )
            else:
                from ..potential import evaluatePotentials

                potGrid = numpy.zeros((len(self._rgrid), len(self._zgrid)))
                for ii in range(len(self._rgrid)):
                    for jj in range(len(self._zgrid)):
                        potGrid[ii, jj] = evaluatePotentials(
                            self._origPot, self._rgrid[ii], self._zgrid[jj]
                        )
                self._potGrid = potGrid
            if self._logR:
                self._potInterp = interpolate.RectBivariateSpline(
                    self._logrgrid, self._zgrid, self._potGrid, kx=3, ky=3, s=0.0
                )
            else:
                self._potInterp = interpolate.RectBivariateSpline(
                    self._rgrid, self._zgrid, self._potGrid, kx=3, ky=3, s=0.0
                )
            if enable_c * ext_loaded:
                self._potGrid_splinecoeffs = calc_2dsplinecoeffs_c(self._potGrid)
        if interpRforce:
            if use_c * ext_loaded:
                self._rforceGrid, err = calc_potential_c(
                    self._origPot, self._rgrid, self._zgrid, rforce=True
                )
            else:
                from ..potential import evaluateRforces

                rforceGrid = numpy.zeros((len(self._rgrid), len(self._zgrid)))
                for ii in range(len(self._rgrid)):
                    for jj in range(len(self._zgrid)):
                        rforceGrid[ii, jj] = evaluateRforces(
                            self._origPot, self._rgrid[ii], self._zgrid[jj]
                        )
                self._rforceGrid = rforceGrid
            if self._logR:
                self._rforceInterp = interpolate.RectBivariateSpline(
                    self._logrgrid, self._zgrid, self._rforceGrid, kx=3, ky=3, s=0.0
                )
            else:
                self._rforceInterp = interpolate.RectBivariateSpline(
                    self._rgrid, self._zgrid, self._rforceGrid, kx=3, ky=3, s=0.0
                )
            if enable_c * ext_loaded:
                self._rforceGrid_splinecoeffs = calc_2dsplinecoeffs_c(self._rforceGrid)
        if interpzforce:
            if use_c * ext_loaded:
                self._zforceGrid, err = calc_potential_c(
                    self._origPot, self._rgrid, self._zgrid, zforce=True
                )
            else:
                from ..potential import evaluatezforces

                zforceGrid = numpy.zeros((len(self._rgrid), len(self._zgrid)))
                for ii in range(len(self._rgrid)):
                    for jj in range(len(self._zgrid)):
                        zforceGrid[ii, jj] = evaluatezforces(
                            self._origPot, self._rgrid[ii], self._zgrid[jj]
                        )
                self._zforceGrid = zforceGrid
            if self._logR:
                self._zforceInterp = interpolate.RectBivariateSpline(
                    self._logrgrid, self._zgrid, self._zforceGrid, kx=3, ky=3, s=0.0
                )
            else:
                self._zforceInterp = interpolate.RectBivariateSpline(
                    self._rgrid, self._zgrid, self._zforceGrid, kx=3, ky=3, s=0.0
                )
            if enable_c * ext_loaded:
                self._zforceGrid_splinecoeffs = calc_2dsplinecoeffs_c(self._zforceGrid)
        if interpDens:
            from ..potential import evaluateDensities

            densGrid = numpy.zeros((len(self._rgrid), len(self._zgrid)))
            for ii in range(len(self._rgrid)):
                for jj in range(len(self._zgrid)):
                    densGrid[ii, jj] = evaluateDensities(
                        self._origPot, self._rgrid[ii], self._zgrid[jj]
                    )
            self._densGrid = densGrid
            if self._logR:
                self._densInterp = interpolate.RectBivariateSpline(
                    self._logrgrid,
                    self._zgrid,
                    numpy.log(self._densGrid + 10.0**-10.0),
                    kx=3,
                    ky=3,
                    s=0.0,
                )
            else:
                self._densInterp = interpolate.RectBivariateSpline(
                    self._rgrid,
                    self._zgrid,
                    numpy.log(self._densGrid + 10.0**-10.0),
                    kx=3,
                    ky=3,
                    s=0.0,
                )
        if interpvcirc:
            from ..potential import vcirc

            if not numcores is None:
                self._vcircGrid = multi.parallel_map(
                    (lambda x: vcirc(self._origPot, self._rgrid[x])),
                    list(range(len(self._rgrid))),
                    numcores=numcores,
                )
            else:
                self._vcircGrid = numpy.array(
                    [vcirc(self._origPot, r) for r in self._rgrid]
                )
            if self._logR:
                self._vcircInterp = interpolate.InterpolatedUnivariateSpline(
                    self._logrgrid, self._vcircGrid, k=3
                )
            else:
                self._vcircInterp = interpolate.InterpolatedUnivariateSpline(
                    self._rgrid, self._vcircGrid, k=3
                )
        if interpdvcircdr:
            from ..potential import dvcircdR

            if not numcores is None:
                self._dvcircdrGrid = multi.parallel_map(
                    (lambda x: dvcircdR(self._origPot, self._rgrid[x])),
                    list(range(len(self._rgrid))),
                    numcores=numcores,
                )
            else:
                self._dvcircdrGrid = numpy.array(
                    [dvcircdR(self._origPot, r) for r in self._rgrid]
                )
            if self._logR:
                self._dvcircdrInterp = interpolate.InterpolatedUnivariateSpline(
                    self._logrgrid, self._dvcircdrGrid, k=3
                )
            else:
                self._dvcircdrInterp = interpolate.InterpolatedUnivariateSpline(
                    self._rgrid, self._dvcircdrGrid, k=3
                )
        if interpepifreq:
            from ..potential import epifreq

            if not numcores is None:
                self._epifreqGrid = numpy.array(
                    multi.parallel_map(
                        (lambda x: epifreq(self._origPot, self._rgrid[x])),
                        list(range(len(self._rgrid))),
                        numcores=numcores,
                    )
                )
            else:
                self._epifreqGrid = numpy.array(
                    [epifreq(self._origPot, r) for r in self._rgrid]
                )
            indx = True ^ numpy.isnan(self._epifreqGrid)
            if numpy.sum(indx) < 4:
                if self._logR:
                    self._epifreqInterp = interpolate.InterpolatedUnivariateSpline(
                        self._logrgrid[indx], self._epifreqGrid[indx], k=1
                    )
                else:
                    self._epifreqInterp = interpolate.InterpolatedUnivariateSpline(
                        self._rgrid[indx], self._epifreqGrid[indx], k=1
                    )
            else:
                if self._logR:
                    self._epifreqInterp = interpolate.InterpolatedUnivariateSpline(
                        self._logrgrid[indx], self._epifreqGrid[indx], k=3
                    )
                else:
                    self._epifreqInterp = interpolate.InterpolatedUnivariateSpline(
                        self._rgrid[indx], self._epifreqGrid[indx], k=3
                    )
        if interpverticalfreq:
            from ..potential import verticalfreq

            if not numcores is None:
                self._verticalfreqGrid = multi.parallel_map(
                    (lambda x: verticalfreq(self._origPot, self._rgrid[x])),
                    list(range(len(self._rgrid))),
                    numcores=numcores,
                )
            else:
                self._verticalfreqGrid = numpy.array(
                    [verticalfreq(self._origPot, r) for r in self._rgrid]
                )
            if self._logR:
                self._verticalfreqInterp = interpolate.InterpolatedUnivariateSpline(
                    self._logrgrid, self._verticalfreqGrid, k=3
                )
            else:
                self._verticalfreqInterp = interpolate.InterpolatedUnivariateSpline(
                    self._rgrid, self._verticalfreqGrid, k=3
                )
        return None

    @scalarVectorDecorator
    @zsymDecorator(False)
    def _evaluate(self, R, z, phi=0.0, t=0.0):
        from ..potential import evaluatePotentials

        if self._interpPot:
            out = numpy.empty(R.shape)
            indx = (
                (R >= self._rgrid[0])
                * (R <= self._rgrid[-1])
                * (z <= self._zgrid[-1])
                * (z >= self._zgrid[0])
            )
            if numpy.sum(indx) > 0:
                if self._enable_c:
                    out[indx] = eval_potential_c(self, R[indx], z[indx])[0] / self._amp
                else:
                    if self._logR:
                        out[indx] = self._potInterp.ev(numpy.log(R[indx]), z[indx])
                    else:
                        out[indx] = self._potInterp.ev(R[indx], z[indx])
            if numpy.sum(True ^ indx) > 0:
                out[True ^ indx] = evaluatePotentials(
                    self._origPot, R[True ^ indx], z[True ^ indx]
                )
            return out
        else:
            return evaluatePotentials(self._origPot, R, z)

    @scalarVectorDecorator
    @zsymDecorator(False)
    def _Rforce(self, R, z, phi=0.0, t=0.0):
        from ..potential import evaluateRforces

        if self._interpRforce:
            out = numpy.empty(R.shape)
            indx = (
                (R >= self._rgrid[0])
                * (R <= self._rgrid[-1])
                * (z <= self._zgrid[-1])
                * (z >= self._zgrid[0])
            )
            if numpy.sum(indx) > 0:
                if self._enable_c:
                    out[indx] = eval_force_c(self, R[indx], z[indx])[0] / self._amp
                else:
                    if self._logR:
                        out[indx] = self._rforceInterp.ev(numpy.log(R[indx]), z[indx])
                    else:
                        out[indx] = self._rforceInterp.ev(R[indx], z[indx])
            if numpy.sum(True ^ indx) > 0:
                out[True ^ indx] = evaluateRforces(
                    self._origPot, R[True ^ indx], z[True ^ indx]
                )
            return out
        else:
            return evaluateRforces(self._origPot, R, z)

    @scalarVectorDecorator
    @zsymDecorator(True)
    def _zforce(self, R, z, phi=0.0, t=0.0):
        from ..potential import evaluatezforces

        if self._interpzforce:
            out = numpy.empty(R.shape)
            indx = (
                (R >= self._rgrid[0])
                * (R <= self._rgrid[-1])
                * (z <= self._zgrid[-1])
                * (z >= self._zgrid[0])
            )
            if numpy.sum(indx) > 0:
                if self._enable_c:
                    out[indx] = (
                        eval_force_c(self, R[indx], z[indx], zforce=True)[0] / self._amp
                    )
                else:
                    if self._logR:
                        out[indx] = self._zforceInterp.ev(numpy.log(R[indx]), z[indx])
                    else:
                        out[indx] = self._zforceInterp.ev(R[indx], z[indx])
            if numpy.sum(True ^ indx) > 0:
                out[True ^ indx] = evaluatezforces(
                    self._origPot, R[True ^ indx], z[True ^ indx]
                )
            return out
        else:
            return evaluatezforces(self._origPot, R, z)

    def _Rzderiv(self, R, z, phi=0.0, t=0.0):
        from ..potential import evaluateRzderivs

        return evaluateRzderivs(self._origPot, R, z)

    @scalarVectorDecorator
    @zsymDecorator(False)
    def _dens(self, R, z, phi=0.0, t=0.0):
        from ..potential import evaluateDensities

        if self._interpDens:
            out = numpy.empty(R.shape)
            indx = (
                (R >= self._rgrid[0])
                * (R <= self._rgrid[-1])
                * (z <= self._zgrid[-1])
                * (z >= self._zgrid[0])
            )
            if numpy.sum(indx) > 0:
                if self._logR:
                    out[indx] = (
                        numpy.exp(self._densInterp.ev(numpy.log(R[indx]), z[indx]))
                        - 10.0**-10.0
                    )
                else:
                    out[indx] = (
                        numpy.exp(self._densInterp.ev(R[indx], z[indx])) - 10.0**-10.0
                    )
            if numpy.sum(True ^ indx) > 0:
                out[True ^ indx] = evaluateDensities(
                    self._origPot, R[True ^ indx], z[True ^ indx]
                )
            return out
        else:
            return evaluateDensities(self._origPot, R, z)

    @physical_conversion("velocity", pop=True)
    @scalarDecorator
    def vcirc(self, R):
        from ..potential import vcirc

        if self._interpvcirc:
            indx = (R >= self._rgrid[0]) * (R <= self._rgrid[-1])
            out = numpy.empty(R.shape)
            if numpy.sum(indx) > 0:
                if self._logR:
                    out[indx] = self._vcircInterp(numpy.log(R[indx]))
                else:
                    out[indx] = self._vcircInterp(R[indx])
            if numpy.sum(True ^ indx) > 0:
                out[True ^ indx] = vcirc(self._origPot, R[True ^ indx])
            return out
        else:
            return vcirc(self._origPot, R)

    @physical_conversion("frequency", pop=True)
    @scalarDecorator
    def dvcircdR(self, R):
        from ..potential import dvcircdR

        if self._interpdvcircdr:
            indx = (R >= self._rgrid[0]) * (R <= self._rgrid[-1])
            out = numpy.empty(R.shape)
            if numpy.sum(indx) > 0:
                if self._logR:
                    out[indx] = self._dvcircdrInterp(numpy.log(R[indx]))
                else:
                    out[indx] = self._dvcircdrInterp(R[indx])
            if numpy.sum(True ^ indx) > 0:
                out[True ^ indx] = dvcircdR(self._origPot, R[True ^ indx])
            return out
        else:
            return dvcircdR(self._origPot, R)

    @physical_conversion("frequency", pop=True)
    @scalarDecorator
    def epifreq(self, R):
        from ..potential import epifreq

        if self._interpepifreq:
            indx = (R >= self._rgrid[0]) * (R <= self._rgrid[-1])
            out = numpy.empty(R.shape)
            if numpy.sum(indx) > 0:
                if self._logR:
                    out[indx] = self._epifreqInterp(numpy.log(R[indx]))
                else:
                    out[indx] = self._epifreqInterp(R[indx])
            if numpy.sum(True ^ indx) > 0:
                out[True ^ indx] = epifreq(self._origPot, R[True ^ indx])
            return out
        else:
            return epifreq(self._origPot, R)

    @physical_conversion("frequency", pop=True)
    @scalarDecorator
    def verticalfreq(self, R):
        from ..potential import verticalfreq

        if self._interpverticalfreq:
            indx = (R >= self._rgrid[0]) * (R <= self._rgrid[-1])
            out = numpy.empty(R.shape)
            if numpy.sum(indx) > 0:
                if self._logR:
                    out[indx] = self._verticalfreqInterp(numpy.log(R[indx]))
                else:
                    out[indx] = self._verticalfreqInterp(R[indx])
            if numpy.sum(True ^ indx) > 0:
                out[True ^ indx] = verticalfreq(self._origPot, R[True ^ indx])
            return out
        else:
            return verticalfreq(self._origPot, R)

def calc_potential_c(pot, R, z, rforce=False, zforce=False):
    """
    Calculate the potential on a grid.

    Parameters
    ----------
    pot : Potential or list of such instances
        Potential object(s) to calculate the potential from.
    R : numpy.ndarray
        Grid in R.
    z : numpy.ndarray
        Grid in z.
    rforce : bool, optional
        If True, calculate the radial force instead. Default is False.
    zforce : bool, optional
        If True, calculate the vertical force instead. Default is False.

    Returns
    -------
    numpy.ndarray
        Potential on the grid (2D array).

    Notes
    -----
    - 2013-01-24 - Written - Bovy (IAS)
    - 2013-01-29 - Added forces - Bovy (IAS)

    """
    from ..orbit.integrateFullOrbit import (  # here bc otherwise there is an infinite loop
        _parse_pot,
    )
    from ..orbit.integratePlanarOrbit import _prep_tfuncs

    # Parse the potential
    npot, pot_type, pot_args, pot_tfuncs = _parse_pot(pot)
    pot_tfuncs = _prep_tfuncs(pot_tfuncs)

    # Set up result arrays
    out = numpy.empty((len(R), len(z)))
    err = ctypes.c_int(0)

    # Set up the C code
    ndarrayFlags = ("C_CONTIGUOUS", "WRITEABLE")
    if rforce:
        interppotential_calc_potentialFunc = _lib.calc_rforce
    elif zforce:
        interppotential_calc_potentialFunc = _lib.calc_zforce
    else:
        interppotential_calc_potentialFunc = _lib.calc_potential
    interppotential_calc_potentialFunc.argtypes = [
        ctypes.c_int,
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.c_int,
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.c_int,
        ndpointer(dtype=numpy.int32, flags=ndarrayFlags),
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.c_void_p,
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.POINTER(ctypes.c_int),
    ]

    # Array requirements, first store old order
    f_cont = [R.flags["F_CONTIGUOUS"], z.flags["F_CONTIGUOUS"]]
    R = numpy.require(R, dtype=numpy.float64, requirements=["C", "W"])
    z = numpy.require(z, dtype=numpy.float64, requirements=["C", "W"])
    out = numpy.require(out, dtype=numpy.float64, requirements=["C", "W"])

    # Run the C code
    interppotential_calc_potentialFunc(
        len(R),
        R,
        len(z),
        z,
        ctypes.c_int(npot),
        pot_type,
        pot_args,
        pot_tfuncs,
        out,
        ctypes.byref(err),
    )

    # Reset input arrays
    if f_cont[0]:
        R = numpy.asfortranarray(R)
    if f_cont[1]:
        z = numpy.asfortranarray(z)

    return (out, err.value)

def calc_2dsplinecoeffs_c(array2d):
    """
    Calculate spline coefficients for a 2D array.

    Parameters
    ----------
    array2d : numpy.ndarray
        2D array to calculate spline coefficients for.

    Returns
    -------
    ndarray
        New array with spline coefficients.

    Notes
    -----
    - 2013-01-24 - Written - Bovy (IAS)
    """
    # Set up result arrays
    out = copy.copy(array2d)
    out = numpy.require(out, dtype=numpy.float64, requirements=["C", "W"])

    # Set up the C code
    ndarrayFlags = ("C_CONTIGUOUS", "WRITEABLE")
    interppotential_calc_2dsplinecoeffs = _lib.samples_to_coefficients
    interppotential_calc_2dsplinecoeffs.argtypes = [
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.c_int,
        ctypes.c_int,
    ]

    # Run the C code
    interppotential_calc_2dsplinecoeffs(out, out.shape[1], out.shape[0])

    return out

def eval_potential_c(pot, R, z):
    """
    Use C to evaluate the interpolated potential.

    Parameters
    ----------
    pot : Potential or list of such instances
        The potential
    R : numpy.ndarray
        Galactocentric cylindrical radius.
    z : numpy.ndarray
        Galactocentric height.

    Returns
    -------
    numpy.ndarray
        Potential evaluated at R and z.

    Notes
    -----
    - 2013-01-24: Written - Bovy (IAS)
    """
    from ..orbit.integrateFullOrbit import (  # here bc otherwise there is an infinite loop
        _parse_pot,
    )
    from ..orbit.integratePlanarOrbit import _prep_tfuncs

    # Parse the potential
    npot, pot_type, pot_args, pot_tfuncs = _parse_pot(pot, potforactions=True)
    pot_tfuncs = _prep_tfuncs(pot_tfuncs)

    # Set up result arrays
    out = numpy.empty(len(R))
    err = ctypes.c_int(0)

    # Set up the C code
    ndarrayFlags = ("C_CONTIGUOUS", "WRITEABLE")
    interppotential_calc_potentialFunc = _lib.eval_potential
    interppotential_calc_potentialFunc.argtypes = [
        ctypes.c_int,
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.c_int,
        ndpointer(dtype=numpy.int32, flags=ndarrayFlags),
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.c_void_p,
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.POINTER(ctypes.c_int),
    ]

    # Array requirements, first store old order
    f_cont = [R.flags["F_CONTIGUOUS"], z.flags["F_CONTIGUOUS"]]
    R = numpy.require(R, dtype=numpy.float64, requirements=["C", "W"])
    z = numpy.require(z, dtype=numpy.float64, requirements=["C", "W"])
    out = numpy.require(out, dtype=numpy.float64, requirements=["C", "W"])

    # Run the C code
    interppotential_calc_potentialFunc(
        len(R),
        R,
        z,
        ctypes.c_int(npot),
        pot_type,
        pot_args,
        pot_tfuncs,
        out,
        ctypes.byref(err),
    )

    # Reset input arrays
    if f_cont[0]:
        R = numpy.asfortranarray(R)
    if f_cont[1]:
        z = numpy.asfortranarray(z)

    return (out, err.value)

def eval_force_c(pot, R, z, zforce=False):
    """
    Use C to evaluate the interpolated potential's forces

    Parameters
    ----------
    pot : Potential or list of such instances
        The potential
    R : numpy.ndarray
        Galactocentric cylindrical radius.
    z : numpy.ndarray
        Galactocentric height.
    zforce : bool, optional
        If True, return the vertical force, otherwise return the radial force. Default is False.

    Returns
    -------
    numpy.ndarray
        Force evaluated at R and z.

    Notes
    -----
    - 2013-01-29: Written - Bovy (IAS)

    """
    from ..orbit.integrateFullOrbit import (  # here bc otherwise there is an infinite loop
        _parse_pot,
    )
    from ..orbit.integratePlanarOrbit import _prep_tfuncs

    # Parse the potential
    npot, pot_type, pot_args, pot_tfuncs = _parse_pot(pot)
    pot_tfuncs = _prep_tfuncs(pot_tfuncs)

    # Set up result arrays
    out = numpy.empty(len(R))
    err = ctypes.c_int(0)

    # Set up the C code
    ndarrayFlags = ("C_CONTIGUOUS", "WRITEABLE")
    if zforce:
        interppotential_calc_forceFunc = _lib.eval_zforce
    else:
        interppotential_calc_forceFunc = _lib.eval_rforce
    interppotential_calc_forceFunc.argtypes = [
        ctypes.c_int,
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.c_int,
        ndpointer(dtype=numpy.int32, flags=ndarrayFlags),
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.c_void_p,
        ndpointer(dtype=numpy.float64, flags=ndarrayFlags),
        ctypes.POINTER(ctypes.c_int),
    ]

    # Array requirements, first store old order
    f_cont = [R.flags["F_CONTIGUOUS"], z.flags["F_CONTIGUOUS"]]
    R = numpy.require(R, dtype=numpy.float64, requirements=["C", "W"])
    z = numpy.require(z, dtype=numpy.float64, requirements=["C", "W"])
    out = numpy.require(out, dtype=numpy.float64, requirements=["C", "W"])

    # Run the C code
    interppotential_calc_forceFunc(
        len(R),
        R,
        z,
        ctypes.c_int(npot),
        pot_type,
        pot_args,
        pot_tfuncs,
        out,
        ctypes.byref(err),
    )

    # Reset input arrays
    if f_cont[0]:
        R = numpy.asfortranarray(R)
    if f_cont[1]:
        z = numpy.asfortranarray(z)

    return (out, err.value)

def sign(x):
    out = numpy.ones_like(x)
    out[(x < 0.0)] = -1.0
    return out


"""AOC 2020 Day 12"""

import pathlib
import time

TEST_INPUT = """F10
N3
F7
R90
F11"""

LEFT, RIGHT = 'L', 'R'
FORWARD = 'F'
NORTH, SOUTH, EAST, WEST = 'N', 'S', 'E', 'W'

def read_input(input_path: str) -> str:
    """take input file path and return a str with the file's content"""
    with open(input_path, 'r') as input_file:
        input_data = input_file.read().strip()
        return input_data

def extract(input_data: str) -> list:
    """take input data and return the appropriate data structure"""
    entries = []
    for line in input_data.split('\n'):
        instruction = (line[0], int(line[1:]))
        entries.append(instruction)
    return entries

def rotate_ship(direction: str, rotation: str) -> str:
    """rotate the ship"""
    if direction == NORTH:
        return WEST if rotation == LEFT else EAST
    if direction == SOUTH:
        return EAST if rotation == LEFT else WEST
    if direction == EAST:
        return NORTH if rotation == LEFT else SOUTH
    if direction == WEST:
        return SOUTH if rotation == LEFT else NORTH
    return None

def move(direction: str, x: int, y: int, value: int) -> tuple:
    """move the ship or the waypoint"""
    if direction == NORTH:
        return (x, y+value)
    if direction == SOUTH:
        return (x, y-value)
    if direction == EAST:
        return (x+value, y)
    if direction == WEST:
        return (x-value, y)
    return None

def rotate_waypoint(direction: str, x: int, y: int, value: int) -> tuple:
    """rotate waypoint around ship"""
    if (direction == LEFT and value == 90) or (direction == RIGHT and value == 270):
        return (-y, x)
    if ((direction == LEFT and value == 180) or (direction == RIGHT and value == 180)):
        return (-x, -y)
    if ((direction == LEFT and value == 270) or (direction == RIGHT and value == 90)):
        return (y, -x)
    return None

def part1(entries: list) -> int:
    """part1 solver"""
    direction = EAST
    x_ship, y_ship = 0, 0

    for action, value in entries:
        if action == FORWARD:
            x_ship, y_ship = move(direction, x_ship, y_ship, value)
        elif action in (LEFT, RIGHT):
            for _ in range(value//90):
                direction = rotate_ship(direction, action)
        else:
            x_ship, y_ship = move(action, x_ship, y_ship, value)
    return abs(x_ship) + abs(y_ship)

def part2(entries: list) -> int:
    """part2 solver"""
    x_ship, y_ship = 0, 0
    x_wp, y_wp = 10, 1

    for action, value in entries:
        if action == FORWARD:
            x_ship += x_wp * value
            y_ship += y_wp * value
        elif action in (LEFT, RIGHT):
            x_wp, y_wp = rotate_waypoint(action, x_wp, y_wp, value)
        else:
            x_wp, y_wp = move(action, x_wp, y_wp, value)
    return abs(x_ship) + abs(y_ship)

def test_input_day_12():
    """pytest testing function"""
    entries = extract(TEST_INPUT)
    assert part1(entries) == 25
    assert part2(entries) == 286

def test_bench_day_12(benchmark):
    """pytest-benchmark function"""
    benchmark(main)

def main():
    """main function"""
    input_path = str(pathlib.Path(__file__).resolve().parent.parent) + "/inputs/" + str(pathlib.Path(__file__).stem)
    start_time = time.time()
    input_data = read_input(input_path)
    entries = extract(input_data)
    print("Part 1: %d" % part1(entries))
    print("Part 2: %d" % part2(entries))
    end_time = time.time()
    print("Execution time: %f" % (end_time-start_time))

if __name__ == "__main__":
    main()


import streamlit as st
import numpy as np
import pandas as pd
import wbdata
import plotly.express as px

@st.cache
def get_data():
	indicators = {'SI.POV.GINI':'Gini Index',
	              'NY.GDP.PCAP.PP.KD':'GDP per capita (constant 2010 US$)',
	              'SP.POP.TOTL':'Population'}

	data = wbdata.get_dataframe(indicators=indicators)

	data = data.reset_index()

	df_region = pd.DataFrame()
	df_region["Country"]=[row['name'] for row in wbdata.get_country("")]
	df_region["Region"]=[row['region']['value'] for row in wbdata.get_country("")]
	df_region = df_region.set_index("Country")

	df = pd.DataFrame()
	for country in data["country"].unique():
	    if data[data["country"]==country]['Gini Index'].notna().sum() != 0 and data[data["country"]==country]['GDP per capita (constant 2010 US$)'].notna().sum() != 0:
	        df_auxiliar = data[data["country"]==country].fillna(method="bfill").dropna()
	        df_auxiliar["Region"]=[df_region.loc[country].values[0] for i in range(len(df_auxiliar))]
	        df_auxiliar=df_auxiliar.sort_values(by="date")
	        df = pd.concat([df, df_auxiliar], ignore_index=True)
	df["date"] = df["date"].astype('int64')
	df = pd.concat([df[df["country"]=="Austria"],df[df["country"]!="Austria"]], ignore_index=True)
	df = pd.concat([df[df["country"]=="Algeria"],df[df["country"]!="Algeria"]], ignore_index=True)
	df = pd.concat([df[df["country"]=="Botswana"],df[df["country"]!="Botswana"]], ignore_index=True)
	df = pd.concat([df[df["country"]=="Australia"],df[df["country"]!="Australia"]], ignore_index=True)
	df = pd.concat([df[df["country"]=="India"],df[df["country"]!="India"]], ignore_index=True)
	df = pd.concat([df[df["country"]=="United States"],df[df["country"]!="United States"]], ignore_index=True)
	df = pd.concat([df[df["country"]=="Chile"],df[df["country"]!="Chile"]], ignore_index=True)
	return df

df = get_data()
df_wb = df.copy()
option = st.multiselect('Elige regiones de interes', df_wb["Region"].unique().tolist(), df_wb["Region"].unique().tolist())
selected = st.multiselect('Elige países de interés', ['Todos']+df_wb["country"].unique().tolist())
df_aux = pd.DataFrame()
for i,region in enumerate(option):
	df_aux = pd.concat([df_aux, df_wb[df_wb["Region"]==option[i]]], ignore_index=True)
if len(df_aux) == 0:
	st.error("Por favor, ingrese una región")
else:
	df_aux["selected country"] = ['' for i in range(len(df_aux))]
	if len(selected) != 0:
		for i, cntry in enumerate(selected):
			if selected[i] == 'Todos':
				selected = df_wb["country"].unique().tolist()
	for selection in selected:
		df_aux.loc[df_aux.index[df_aux["country"]==f"{selection}"].tolist(), "selected country"] = f"{selection}"
	fig = px.scatter(df_aux,
	                x='GDP per capita (constant 2010 US$)',
	                y='Gini Index',
					text='selected country',
	                animation_frame="date",
	                animation_group="country",
	                size="Population",
	                color="Region",
	                hover_name="country",
	                log_x=True,
	                size_max=60,
					range_x=[df_aux['GDP per capita (constant 2010 US$)'].min(),df_aux['GDP per capita (constant 2010 US$)'].max()],
					range_y=[df_aux['Gini Index'].min(), df_aux['Gini Index'].max()],
					labels={"Gini Index":"Índice de Gini",
	 "GDP per capita (constant 2010 US$)":"PIB per cápita (dólares ajustados por inflación a valor 2010) (escala log)"},
					title="Índice de Gini vs PIB per cápita"
	                )
	st.plotly_chart(fig)
	st.write("Elaboración propia en base a datos del Banco Mundial")
	st.write("Autor: Alonso Silva")


from tasks import load_model
from data import H5Dataset
from tasks.utils import log_spherical_image_helper
from torchmetrics import JaccardIndex
from tqdm import tqdm
import torch

def get_trained_model (checkpt_lidar_path, checkpt_img_path, device):
    model_lidar = load_model(checkpt_lidar_path)
    model_img = load_model(checkpt_img_path)
    model_lidar = model_lidar['spherical_model']
    model_img = model_img['spherical_model']
    model_lidar.eval().to(device)
    model_img.eval().to(device)
    return model_lidar, model_img

def fuse_outputs(model_img, model_lidar, x, device, weight1=0.6, weight2=0.4):
    x_lidar = x['range_image'].to(device)
    x_img = x['proj_pixel'].to(device)

    y_lidar = model_lidar(x_lidar)
    y_img = model_img(x_img)

    y = y_lidar*weight1 + y_img*weight2

    mask = torch.all(x_img == 0, dim=1, keepdim=True)
    masked_output = torch.where(mask, y_lidar, y)

    return masked_output, y_lidar

def late_fusion_naive_eval (checkpt_lidar_path, checkpt_img_path, device, h5_path, batch_size=30, weight_lidar=0.5, weight_img=0.5):
    model_lidar, model_img = get_trained_model(checkpt_lidar_path, checkpt_img_path, device)

    key2load = ['range_image', 'proj_pixel', 'ri1_label']
    dset = H5Dataset(h5_path, key2load)
    data_loader = torch.utils.data.DataLoader(dset, shuffle=False, batch_size=batch_size, num_workers=8)

    jaccard = JaccardIndex(num_classes=23, ignore_index=0, absent_score=1.0, reduction='elementwise_mean').to(device)

    total_mIoU_comb = 0
    total_mIoU_lidar = 0
    total_mIoU_diff = 0
    with torch.no_grad():
        for batch in tqdm(data_loader, total=len(data_loader)):
            y, y_lidar = fuse_outputs(model_img, model_lidar, batch, device, weight_lidar, weight_img)
            label = batch["ri1_label"][:, 1, :].long().to(device)

            pred = y.argmax(dim=1)
            pred_only_lidar = y_lidar.argmax(dim=1)

            mIoU_comb = jaccard(pred, label)
            total_mIoU_comb += mIoU_comb

            mIoU_lidar = jaccard(pred_only_lidar, label)
            total_mIoU_lidar += mIoU_lidar

            total_mIoU_diff +=  mIoU_comb-mIoU_lidar

        mean_mIoU_comb = total_mIoU_comb/len(data_loader)
        mean_mIoU_lidar = total_mIoU_lidar/len(data_loader)
        mean_mIoU_diff = total_mIoU_diff/len(data_loader)

    return mean_mIoU_comb, mean_mIoU_lidar, mean_mIoU_diff


from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput

class RuleOfTree(App):
    def build(self):
        # front-end
        self.window = GridLayout()
        self.window.cols = 2
        self.window.size_hint = (0.5, 0.7)
        self.window.pos_hint = {"center_x": 0.5, "center_y": 0.5}

        # back-end
        # add widgets to window

        # Text input widget 1
        self.base = TextInput(
                         multiline=False,
                         padding_y=(70, 70),
                         padding_x=70,
                         size_hint=(0.5, 0.6)
                         )
        self.window.add_widget(self.base)

        # Text input widget 2
        self.new_value = TextInput(
                         multiline=False,
                         padding_y=(70, 70),
                         padding_x=70,
                         size_hint=(0.5, 0.6)
                         )
        self.window.add_widget(self.new_value)

        # Text input widget 3
        self.base_result = TextInput(
                         multiline=False,
                         padding_y=(70, 70),
                         padding_x=70,
                         size_hint=(0.5, 0.6)
                         )
        self.window.add_widget(self.base_result)

        self.space1 = Label(
                    text=" ",
                    size_hint=(0.5, 0.6)
                    )
        self.window.add_widget(self.space1)

        # Button
        self.button = Button(
                      text="CONVERT",
                      size_hint=(0.25, 0.25),
                      bold=True,
                      background_color='#00FCE'
                      )
        self.button.bind(on_press=self.callback)
        self.window.add_widget(self.button)

        return self.window

    def rule_of_tree(self, base, base_result, new_value):
        x = base_result * new_value
        new_result = x / base
        new_result = '{:.2f}'.format(new_result)
        return new_result

    def to_float(self, string):
        if string == "":
            string = 1
        else:
            string = float(string)
        return string

    def callback(self, instance):
        base = self.to_float(self.base.text)
        base_result = self.to_float(self.base_result.text)
        new_value = self.to_float(self.new_value.text)
        new_result = self.rule_of_tree(base, base_result, new_value)
        self.space1.text = new_result

if __name__ == "__main__":
    RuleOfTree().run()


import asyncio
import json
import os, sys

from django.contrib.auth import get_user_model

proj = os.path.dirname(os.path.abspath('manage.py'))
sys.path.append(proj)
os.environ['DJANGO_SETTINGS_MODULE'] = 'app.settings'

import django

django.setup()

from scraping.scraping_scripts import *
from scraping.models import City, Vacancy, Language, Error, Url
from django.db import DatabaseError

User = get_user_model()

parsers = (
    (work_ua, 'work'),
    (dou_ua, 'dou'),
)
jobs, errors = [], []

def get_settings():
    qs = User.objects.filter(receive_email = True).values()
    list_settings = set((q['city_id'], q['language_id']) for q in qs)
    return list_settings

def get_urls(settings):
    qs = Url.objects.all().values()
    url_dict = {(q['city_id'], q['language_id']): q['url_data'] for q in qs}
    urls = []
    for pair in settings:
        temp = {}
        temp['city'] = pair[0]
        temp['language'] = pair[1]
        temp['url_data'] = url_dict[pair]
        urls.append(temp)
    return urls

async def main(value):
    func, url_data, city, language = value
    job, error = await loop.run_in_executor(None, func, url_data, city, language)
    jobs.extend(job)
    errors.extend(error)

setting = get_settings()
url_list = get_urls(setting)

# import time
# start = time.time()
loop = asyncio.new_event_loop()
list_tasks = [
    (func, data['url_data'][key], data['city'], data['language'])
    for data in url_list
    for func, key in parsers
]
tasks = asyncio.wait([loop.create_task(main(f)) for f in list_tasks])

# for data in url_list:
#
#     for func, key in parsers:
#         url = data['url_data'][key]
#         j, e = func(url, city = data['city'], language = data['language'])
#         jobs += j
#         errors += e

loop.run_until_complete(tasks)
loop.close()
# end = time.time() - start
# print(end)
for job in jobs:
    v = Vacancy(**job)
    try:
        v.save()
    except DatabaseError:
        pass

if errors:
    er = Error(data = errors).save()


# coding=utf-8
from docx import Document

__author__ = 'Sprout'
__doc__ = u'''
    提供转化 data 为 docx 的类
'''

class FinalPaperWeekPlan:
    def __init__(self, cells=None, title=None, template='sample.docx'):
        u'''
            params:
                cells: {} 允许使用者自己定义自己的模板中的属性
                title: '' 允许使用者定义自己生成文档的题目
                template: '' 允许使用者定义自己的模板, 默认为 sample.docx
        '''
        self.report_template = Document(template)
        self.table = self.report_template.tables[0]
        self.title_template = title or u"软件学院本科实习&毕业设计（论文）周报-%s-%s.docx"
        cells = cells or {
            "name": (1, 1),
            "tel": (1, 3),
            "email": (1, 6),
            "base": (2, 1),
            "mentor": (2, 4),
            "inner_mentor": (2, 8),
            "report_time": (3, 1),
            "week": (3, 4),
            "duration": (4, 1),
            "point": (5, 1),
            "content": (6, 1),
            "problem": (7, 1),
            "summary": (8, 1),
            "nextweekplan": (9, 1),
        }
        self.cells = {}
        for k, v in cells.iteritems():
            row, col = v
            self.cells[k] = self.table.cell(row, col)

    def convert(self, data={}, destnation='', titleparams=()):
        u'''
            params:
                data :
                {
                    "name": value,
                    "tel": value,
                    "email": value,
                    "base": value,
                    "mentor": value,
                    "inner_mentor": value,
                    "report_time": value,
                    "week": value,
                    "duration": value,
                    "point": value,
                    "content": value,
                    "problem": value,
                    "summary": value,
                    "nextweekplan": value,
                }
                destnation :
                the path doc will be store, end with '/'
        '''
        if not data:
            raise RuntimeError('missing doc data')
        for k in self.cells:
            self.cells[k].text = data.get(k) or self.cells[k].text

        title = self.title_template % titleparams
        self.report_template.save(destnation+title)