import sys
import json
import numpy as np
import methods as m
import performance as perf
import simulation as sim
from distribution import Demand, pdf
from model import Cost, QRModel

def main():
	lines = read_stdin()

	# parse args
	A = float(lines['A'])
	h = float(lines['h'])
	b = float(lines['b'])
	P = float(lines['P'])
	i0 = int(lines['i0'])
	b0 = 0
	L = int(lines['L'])
	X = lines['X']
	Y = np.asarray(lines['Y'], dtype=np.int64) # must be np.array
	Q = np.asarray(lines['Q'], dtype=np.int64)
	I = np.asarray(lines['I'], dtype=np.int64)
	B = np.asarray(lines['B'], dtype=np.int64)

	# pass args to qr model and calculate result
	y = np.bincount(Y)
	x = np.arange(np.max(Y) + 1)
	params, perr, bic = m.fit_distribution(pdf, x, y)
	mu, sig, p = params
	demand = Demand(mu, sig, p)

	# extrapolate directly from sample
	demand = m.extrapolate_sample(Y, L)

	# for normal distribution
	# from scipy.stats import norm
	# normpdf = lambda x, mu, sig: norm.cdf(np.round(x)+0.5, mu, sig) - norm.cdf(np.round(x)-0.5, mu, sig)
	# params, perr, bic = m.fit_distribution(normpdf, X, Y)
	# mu, sig = params

	# calculate result
	cost = Cost(A, h, b, 0)
	model = QRModel(demand, cost)
	kyu, r, total_cost = model.numeric_optimize_backorder()

	qr_model = sim.SimulationModel(Y, sim.Cost(A, h, b), sim.QRStrategy(kyu, r), i0, lead_time=L)
	_, _, _, result = qr_model.run()
	I_optimized = result["inventory"]
	B_optimized = result["backorders"]
	Q_optimized = result["orders"]
	f = result["fill_rate"] * 100
	# Qc_optimized = result["fixed_cost"]
	# Ic_optimized = result["holding_cost"]
	# Bc_optimized = result["backorder_cost"]
	# Tc_optimized = np.array(Qc_optimized) + np.array(Ic_optimized) + np.array(Bc_optimized)

	Q_opt = np.asarray(Q_optimized, dtype=np.int64)
	I_opt = np.asarray(I_optimized, dtype=np.int64)
	B_opt = np.asarray(B_optimized, dtype=np.int64)
	# get cost data for original and optimized QIB params
	Qc, Ic, Bc, Tc = perf.simulateQIBCost(Q, P, I, h, B, b, A)
	Qc_optimized, Ic_optimized, Bc_optimized, Tc_optimized = perf.simulateQIBCost(Q_opt, P, I_opt, h, B_opt, b, A)

	# pack result into dictionary for json dumping
	result = {}
	result["A"] = A
	result["h"] = h
	result["b"] = b
	result["L"] = L

	result["mu"] = mu
	result["std"] = sig
	result["kyu"] = kyu
	result["r"] = r
	result["c"] = total_cost
	result["f"] = f

	result["y"] = y.tolist()
	result["x"] = x.tolist()
	result["Q_optimized"] = Q_optimized
	result["I_optimized"] = I_optimized
	result["B_optimized"] = B_optimized

	result["Qc"] = Qc
	result["Ic"] = Ic
	result["Bc"] = Bc
	result["Tc"] = Tc
	result["Qc_optimized"] = Qc_optimized
	result["Ic_optimized"] = Ic_optimized
	result["Bc_optimized"] = Bc_optimized
	result["Tc_optimized"] = Tc_optimized

	# return the result to runPy.js via stdOut
	print(json.dumps(result))

# Read data from stdin
def read_stdin():
  lines = sys.stdin.readlines()
  # Since our input would only be having one line, parse our JSON data from that
  return json.loads(lines[0])

if __name__ == '__main__':
	main()


#!/usr/bin/env python
# -*- coding: utf8 -*-
# *****************************************************************
# **       PTS -- Python Toolkit for working with SKIRT          **
# **       © Astronomical Observatory, Ghent University          **
# *****************************************************************

## \package pts.core.simulation.output Contains the SimulationOutput class.

# -----------------------------------------------------------------

# Ensure Python 3 compatibility
from __future__ import absolute_import, division, print_function

# Import standard modules
from collections import defaultdict

# Import the relevant PTS classes and modules
from ..tools import filesystem as fs
from ..basics.map import Map
from ..tools.utils import lazyproperty

# -----------------------------------------------------------------

# The various output types
output_types = Map()
output_types.isrf = "isrf"
output_types.absorption = "abs"
output_types.temperature = "temp"
output_types.seds = "sed"
output_types.images = "image"
output_types.total_images = "image-total"
output_types.direct_images = "image-direct"
output_types.transparent_images = "image-transparent"
output_types.scattered_images = "image-scattered"
output_types.dust_images = "image-dust"
output_types.dust_scattered_images = "image-dustscattered"
output_types.cell_temperature = "celltemp"
output_types.logfiles = "log"
output_types.wavelengths = "wavelengths"
output_types.grid = "grid"
output_types.gdensity = "grho"
output_types.tdensity = "trho"
output_types.tree = "tree"
output_types.convergence = "convergence"
output_types.dust_mass = "mass"
output_types.dust_mix_properties = "mean"
output_types.dust_optical_properties = "optical"
output_types.dust_grain_sizes = "size"
output_types.parameters = "parameters"

# -----------------------------------------------------------------

# Description of the output types
output_type_choices = dict()
output_type_choices[output_types.isrf] = "interstellar radiation field strength"
output_type_choices[output_types.absorption] = "absorption luminosities"
output_type_choices[output_types.temperature] = "temperature"
output_type_choices[output_types.seds] = "all SEDs"
output_type_choices[output_types.images] = "all datacubes"
output_type_choices[output_types.total_images] = "datacubes of total emission"
output_type_choices[output_types.direct_images] = "datacubes of direct emission"
output_type_choices[output_types.transparent_images] = "datacubes of transparent emission"
output_type_choices[output_types.scattered_images] = "datacubes of scattered emission"
output_type_choices[output_types.dust_images] = "datacubes of dust emission"
output_type_choices[output_types.dust_scattered_images] = "datacubes of scattered dust emission"
output_type_choices[output_types.cell_temperature] = "temperature per dust cell"
output_type_choices[output_types.logfiles] = "log files"
output_type_choices[output_types.wavelengths] = "wavelength files"
output_type_choices[output_types.grid] = "grid files"
output_type_choices[output_types.gdensity] = "grid dust density"
output_type_choices[output_types.tdensity] = "theoretical dust density"
output_type_choices[output_types.tree] = "dust grid tree data file"
output_type_choices[output_types.convergence] = "convergence file"
output_type_choices[output_types.dust_mass] = "dust population masses"
output_type_choices[output_types.dust_mix_properties] = "combined dust mix properties"
output_type_choices[output_types.dust_optical_properties] = "optical dust population properties"
output_type_choices[output_types.dust_grain_sizes] = "dust grain size information"
output_type_choices[output_types.parameters] = "parameter files"

# -----------------------------------------------------------------

def get_output_type(filename):

    """
    This function ...
    :param filename:
    :return:
    """

    ## ISRF
    if filename.endswith("_ds_isrf.dat"): return output_types.isrf

    ## Absorption
    elif filename.endswith("_ds_abs.dat"): return output_types.absorption

    ## Temperature
    elif "_ds_temp" in filename and filename.endswith(".fits"): return output_types.temperature

    ## SED
    elif filename.endswith("_sed.dat"): return output_types.seds

    ## Total datacubes
    elif filename.endswith("_total.fits"): return output_types.total_images

    ## Direct datacubes
    elif filename.endswith("_direct.fits"): return output_types.direct_images

    ## Transparent datacubes
    elif filename.endswith("_transparent.fits"): return output_types.transparent_images

    ## Scattered datacubes
    elif filename.endswith("_scattered.fits"): return output_types.scattered_images

    ## Dust datacubes
    elif filename.endswith("_dust.fits"): return output_types.dust_images

    ## Dust scattered datacubes
    elif filename.endswith("_dustscattered.fits"): return output_types.dust_scattered_images

    ## Cell temperature data
    elif filename.endswith("_ds_celltemps.dat"): return output_types.cell_temperature

    ## Log files
    elif "_log" in filename and filename.endswith(".txt"): return output_types.logfiles

    ## Wavelength files
    elif filename.endswith("_wavelengths.dat"): return output_types.wavelengths

    ## Grid structure data
    elif "_ds_grid" in filename and filename.endswith(".dat"): return output_types.grid

    ## Grid dust density
    elif "_ds_grho" in filename and filename.endswith(".fits"): return output_types.gdensity

    ## Theoretical dust density
    elif "_ds_trho" in filename and filename.endswith(".fits"): return output_types.tdensity

    ## Dust grid onvergence
    elif filename.endswith("_ds_convergence.dat"): return output_types.convergence

    ## Dust grid tree data
    elif "_ds_tree" in filename and filename.endswith(".dat"): return output_types.tree

    ## Dust mass
    elif "ds_mix_" in filename and "_mass" in filename: return output_types.dust_mass

    ## Dust mix properties
    elif "ds_mix_" in filename and "_mean" in filename: return output_types.dust_mix_properties

    ## Dust optical properties
    elif "ds_mix_" in filename and "_opti" in filename: return output_types.dust_optical_properties

    ## Dust grain sizes
    elif "ds_mix_" in filename and "_size" in filename: return output_types.dust_grain_sizes

    ## Parameter files
    elif filename.endswith("_parameters.xml") or filename.endswith("_parameters.tex"): return output_types.parameters

    # No match
    return None

# -----------------------------------------------------------------

parent_output_types = dict()
parent_output_types[output_types.total_images] = output_types.images
parent_output_types[output_types.direct_images] = output_types.images
parent_output_types[output_types.transparent_images] = output_types.images
parent_output_types[output_types.scattered_images] = output_types.images
parent_output_types[output_types.dust_images] = output_types.images
parent_output_types[output_types.dust_scattered_images] = output_types.images

# -----------------------------------------------------------------

def get_parent_type(output_type):

    """
    This function ...
    :param output_type:
    :return:
    """

    if output_type in parent_output_types: return parent_output_types[output_type]
    else: return output_type

# -----------------------------------------------------------------

other_name = "other"

# -----------------------------------------------------------------

class SimulationOutput(object):

    """
    This class ...
    """

    def __init__(self, *args):

        """
        The constructor ...
        :param args:
        """

        # The simulation prefix
        self.prefix = None

        # Dictionary of filepaths
        self.paths = defaultdict(list)

        # Loop over the filepaths, categorize
        for filepath in args:
            filename = fs.name(filepath)
            prefix = filename.split("_")[0]
            if self.prefix is None: self.prefix = prefix
            elif self.prefix != prefix: raise ValueError("Cannot add files with different simulation prefixes")
            output_type = get_output_type(filename)
            if output_type is None: self.paths[other_name].append(filepath)
            else: self.paths[output_type].append(filepath)

    # -----------------------------------------------------------------

    @classmethod
    def from_paths(cls, paths):

        """
        This function ...
        :param paths:
        :return:
        """

        return cls(*paths)

    # -----------------------------------------------------------------

    @classmethod
    def from_cwd(cls, prefix=None):

        """
        This function ...
        :param prefix:
        :return:
        """

        return cls.from_directory(fs.cwd(), prefix=prefix)

    # -----------------------------------------------------------------

    @classmethod
    def from_directory(cls, path, prefix=None):

        """
        This function ...
        :param path:
        :param prefix:
        :return:
        """

        return cls.from_paths(fs.files_in_path(path, startswith=prefix, not_extension="ski"))

    # -----------------------------------------------------------------

    @classmethod
    def from_remote_directory(cls, path, remote, prefix=None):

        """
        This function ...
        :param path:
        :param remote:
        :param prefix:
        :return:
        """

        return cls.from_paths(remote.files_in_path(path, startswith=prefix, not_extension="ski"))

    # -----------------------------------------------------------------

    def __iter__(self):

        """
        This function ...
        :return:
        """

        for path in self.isrf: yield path
        for path in self.absorption: yield path
        for path in self.temperature: yield path
        for path in self.seds: yield path
        for path in self.total_images: yield path
        for path in self.direct_images: yield path
        for path in self.transparent_images: yield path
        for path in self.scattered_images: yield path
        for path in self.dust_images: yield path
        for path in self.dust_scattered_images: yield path
        for path in self.cell_temperature: yield path
        for path in self.logfiles: yield path
        for path in self.wavelengths: yield path
        for path in self.grid: yield path
        for path in self.gdensity: yield path
        for path in self.tdensity: yield path
        for path in self.tree: yield path
        for path in self.convergence: yield path
        for path in self.dust_mass: yield path
        for path in self.dust_mix_properties: yield path
        for path in self.dust_optical_properties: yield path
        for path in self.dust_grain_sizes: yield path
        for path in self.parameters: yield path
        for path in self.other: yield path

    # -----------------------------------------------------------------

    def __len__(self):

        """
        This function ...
        :return:
        """

        total = 0
        if self.has_isrf: total += self.nisrf
        if self.has_absorption: total += self.nabsorption
        if self.has_temperature: total += self.ntemperature
        if self.has_seds: total += self.nseds
        if self.has_total_images: total += self.ntotal_images
        if self.has_direct_images: total += self.ndirect_images
        if self.has_transparent_images: total += self.ntransparent_images
        if self.has_scattered_images: total += self.nscattered_images
        if self.has_dust_images: total += self.ndust_images
        if self.has_dust_scattered_images: total += self.ndust_scattered_images
        if self.has_cell_temperature: total += self.ncell_temperature
        if self.has_logfiles: total += self.nlogfiles
        if self.has_wavelengths: total += self.nwavelengths
        if self.has_grid: total += self.ngrid
        if self.has_gdensity: total += self.ngdensity
        if self.has_tdensity: total += self.ntdensity
        if self.has_tree: total += self.ntree
        if self.has_convergence: total += self.nconvergence
        if self.has_dust_mass: total += self.ndust_mass
        if self.has_dust_mix_properties: total += self.ndust_mix_properties
        if self.has_dust_optical_properties: total += self.ndust_optical_properties
        if self.has_dust_grain_sizes: total += self.ndust_grain_sizes
        if self.has_parameters: total += self.nparameters
        if self.has_other: total += self.nother
        return total

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def nisrf(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.isrf])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_isrf(self):

        """
        This function ...
        :return:
        """

        return output_types.isrf in self.paths and self.nisrf > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def isrf(self):

        """
        This function ...
        :return:
        """

        if not self.has_isrf: return []
        return self.paths[output_types.isrf]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def nabsorption(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.absorption])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_absorption(self):

        """
        This function ...
        :return:
        """

        return output_types.absorption in self.paths and self.nabsorption > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def absorption(self):

        """
        This function ...
        :return:
        """

        if not self.has_absorption: return []
        return self.paths[output_types.absorption]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ntemperature(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.temperature])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_temperature(self):

        """
        This function ...
        :return:
        """

        return output_types.temperature in self.paths and self.ntemperature > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def temperature(self):

        """
        This function ...
        :return:
        """

        if not self.has_temperature: return []
        return self.paths[output_types.temperature]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def nseds(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.seds])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_seds(self):

        """
        This function ...
        :return:
        """

        return output_types.seds in self.paths and self.nseds > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def seds(self):

        """
        This function ...
        :return:
        """

        if not self.has_seds: return []
        return self.paths[output_types.seds]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def nimages(self):

        """
        This function ...
        :return:
        """

        total = 0
        if self.has_total_images: total += self.ntotal_images
        if self.has_direct_images: total += self.ndirect_images
        if self.has_transparent_images: total += self.ntransparent_images
        if self.has_scattered_images: total += self.nscattered_images
        if self.has_dust_images: total += self.ndust_images
        if self.has_dust_scattered_images: total += self.ndust_scattered_images
        return total

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_images(self):

        """
        This function ...
        :return:
        """

        return self.nimages > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def images(self):

        """
        This function ...
        :return:
        """

        paths = []
        if self.has_total_images: paths.extend(self.total_images)
        if self.has_direct_images: paths.extend(self.direct_images)
        if self.has_transparent_images: paths.extend(self.transparent_images)
        if self.has_scattered_images: paths.extend(self.scattered_images)
        if self.has_dust_images: paths.extend(self.dust_images)
        if self.has_dust_scattered_images: paths.extend(self.dust_scattered_images)
        return paths

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ntotal_images(self):

        """
        Thisf unctin ...
        :return:
        """

        return len(self.paths[output_types.total_images])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_total_images(self):

        """
        This function ...
        :return:
        """

        return output_types.total_images in self.paths and self.ntotal_images > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def total_images(self):

        """
        This function ...
        :return:
        """

        if not self.has_total_images: return []
        else: return self.paths[output_types.total_images]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ndirect_images(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.direct_images])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_direct_images(self):

        """
        This function ...
        :return:
        """

        return output_types.direct_images in self.paths and self.ndirect_images > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def direct_images(self):

        """
        This function ...
        :return:
        """

        if not self.has_direct_images: return []
        else: return self.paths[output_types.direct_images]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ntransparent_images(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.transparent_images])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_transparent_images(self):

        """
        This function ...
        :return:
        """

        return output_types.transparent_images in self.paths and self.ntransparent_images > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def transparent_images(self):

        """
        This function ...
        :return:
        """

        if not self.has_transparent_images: return []
        else: return self.paths[output_types.transparent_images]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def nscattered_images(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.scattered_images])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_scattered_images(self):

        """
        This function ...
        :return:
        """

        return output_types.scattered_images in self.paths and self.nscattered_images > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def scattered_images(self):

        """
        This function ...
        :return:
        """

        if not self.has_scattered_images: return []
        else: return self.paths[output_types.scattered_images]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ndust_images(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.dust_images])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_dust_images(self):

        """
        This function ...
        :return:
        """

        return output_types.dust_images in self.paths and self.ndust_images > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def dust_images(self):

        """
        This function ...
        :return:
        """

        if not self.has_dust_images: return []
        else: return self.paths[output_types.dust_images]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ndust_scattered_images(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.dust_scattered_images])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_dust_scattered_images(self):

        """
        This function ...
        :return:
        """

        return output_types.dust_scattered_images in self.paths and self.ndust_scattered_images > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def dust_scattered_images(self):

        """
        This function ...
        :return:
        """

        if not self.has_dust_scattered_images: return []
        else: return self.paths[output_types.dust_scattered_images]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ncell_temperature(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.cell_temperature])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_cell_temperature(self):

        """
        This function ...
        :return:
        """

        return output_types.cell_temperature in self.paths and self.ncell_temperature > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def cell_temperature(self):

        """
        This function ...
        :return:
        """

        if not self.has_cell_temperature: return []
        else: return self.paths[output_types.cell_temperature]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def nlogfiles(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.logfiles])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_logfiles(self):

        """
        This funtion ...
        :return:
        """

        return output_types.logfiles in self.paths and self.nlogfiles > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def logfiles(self):

        """
        This function ...
        :return:
        """

        if not self.has_logfiles: return []
        else: return self.paths[output_types.logfiles]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def nwavelengths(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.wavelengths])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_wavelengths(self):

        """
        This function ...
        :return:
        """

        return output_types.wavelengths in self.paths and self.nwavelengths > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def wavelengths(self):

        """
        This function ...
        :return:
        """

        if not self.has_wavelengths: return []
        else: return self.paths[output_types.wavelengths]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ngrid(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.grid])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_grid(self):

        """
        Thisf unction ...
        :return:
        """

        return output_types.grid in self.paths and self.ngrid > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def grid(self):

        """
        This function ...
        :return:
        """

        if not self.has_grid: return []
        else: return self.paths[output_types.grid]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ngdensity(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.gdensity])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_gdensity(self):

        """
        This function ...
        :return:
        """

        return output_types.gdensity in self.paths and self.ngdensity > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def gdensity(self):

        """
        This fucntion ...
        :return:
        """

        if not self.has_gdensity: return []
        else: return self.paths[output_types.gdensity]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ntdensity(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.tdensity])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_tdensity(self):

        """
        This function ...
        :return:
        """

        return output_types.tdensity in self.paths and self.ntdensity > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def tdensity(self):

        """
        This function ...
        :return:
        """

        if not self.has_tdensity: return []
        else: return self.paths[output_types.tdensity]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def ntree(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.tree])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_tree(self):

        """
        Thisf unction ...
        :return:
        """

        return output_types.tree in self.paths and self.ntree > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def tree(self):

        """
        This function ...
        :return:
        """

        if not self.has_tree: return []
        else: return self.paths[output_types.tree]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def nconvergence(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.convergence])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_convergence(self):

        """
        This function ...
        :return:
        """

        return output_types.convergence in self.paths and self.nconvergence > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def convergence(self):

        """
        This function ...
        :return:
        """

        if not self.has_convergence: return []
        else: return self.paths[output_types.convergence]

    # -----------------------------------------------------------------

    @lazyproperty
    def ndust_mass(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.dust_mass])

    # -----------------------------------------------------------------

    @lazyproperty
    def has_dust_mass(self):

        """
        This function ...
        :return:
        """

        return output_types.dust_mass in self.paths and self.ndust_mass > 0

    # -----------------------------------------------------------------

    @lazyproperty
    def dust_mass(self):

        """
        This function ...
        :return:
        """

        if not self.has_dust_mass: return []
        else: return self.paths[output_types.dust_mass]

    # -----------------------------------------------------------------

    @lazyproperty
    def ndust_mix_properties(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.dust_mix_properties])

    # -----------------------------------------------------------------

    @lazyproperty
    def has_dust_mix_properties(self):

        """
        This function ...
        :return:
        """

        return output_types.dust_mix_properties in self.paths and self.ndust_mix_properties > 0

    # -----------------------------------------------------------------

    @lazyproperty
    def dust_mix_properties(self):

        """
        This function ...
        :return:
        """

        if not self.has_dust_mix_properties: return []
        else: return self.paths[output_types.dust_mix_properties]

    # -----------------------------------------------------------------

    @lazyproperty
    def ndust_optical_properties(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.dust_optical_properties])

    # -----------------------------------------------------------------

    @lazyproperty
    def has_dust_optical_properties(self):

        """
        This function ...
        :return:
        """

        return output_types.dust_optical_properties in self.paths and self.ndust_optical_properties > 0

    # -----------------------------------------------------------------

    @lazyproperty
    def dust_optical_properties(self):

        """
        This function ...
        :return:
        """

        if not self.has_dust_optical_properties: return []
        else: return self.paths[output_types.dust_optical_properties]

    # -----------------------------------------------------------------

    @lazyproperty
    def ndust_grain_sizes(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.dust_grain_sizes])

    # -----------------------------------------------------------------

    @lazyproperty
    def has_dust_grain_sizes(self):

        """
        This function ...
        :return:
        """

        return output_types.dust_grain_sizes in self.paths and self.ndust_grain_sizes > 0

    # -----------------------------------------------------------------

    @lazyproperty
    def dust_grain_sizes(self):

        """
        This function ...
        :return:
        """

        if not self.has_dust_grain_sizes: return []
        else: return self.paths[output_types.dust_grain_sizes]

    # -----------------------------------------------------------------

    @lazyproperty
    def nparameters(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[output_types.parameters])

    # -----------------------------------------------------------------

    @lazyproperty
    def has_parameters(self):

        """
        This function ...
        :return:
        """

        return output_types.parameters in self.paths and self.nparameters > 0

    # -----------------------------------------------------------------

    @lazyproperty
    def parameters(self):

        """
        This function ...
        :return:
        """

        if not self.has_parameters: return []
        else: return self.paths[output_types.parameters]

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def nother(self):

        """
        This function ...
        :return:
        """

        return len(self.paths[other_name])

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def has_other(self):

        """
        This function ...
        :return:
        """

        return other_name in self.paths and self.nother > 0

    # -----------------------------------------------------------------

    #@property
    @lazyproperty
    def other(self):

        """
        This function ...
        :return:
        """

        if not self.has_other: return []
        else: return self.paths[other_name]

    # -----------------------------------------------------------------

    @lazyproperty
    def directory(self):

        """
        This function ...
        :return:
        """

        path = None
        for filepath in self.isrf:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.absorption:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.temperature:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.seds:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.total_images:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.direct_images:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.transparent_images:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.scattered_images:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.dust_images:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.dust_scattered_images:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.cell_temperature:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.logfiles:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.wavelengths:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.grid:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.gdensity:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.tdensity:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.tree:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.convergence:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.dust_mass:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.dust_mix_properties:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.dust_optical_properties:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.dust_grain_sizes:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.parameters:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")
        for filepath in self.other:
            if path is None: path = fs.directory_of(filepath)
            elif path != fs.directory_of(filepath): raise ValueError("Output files are not in the same directory")

        # Return the directory path
        return path

    # -----------------------------------------------------------------

    def relative_path(self, path):

        """
        Thisf unction ...
        :param path:
        :return:
        """

        return fs.relative_to(path, self.directory)

    # -----------------------------------------------------------------

    def __str__(self):

        """
        This function ...
        :return:
        """

        return self.to_string()

    # -----------------------------------------------------------------

    def to_string(self, line_prefix=""):

        """
        This function ...
        :param line_prefix:
        :return:
        """

        from ..tools import formatting as fmt

        lines = []

        # ISRF
        if self.has_isrf:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.isrf].capitalize() + fmt.reset + " (" + str(self.nisrf) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.isrf: lines.append(line_prefix + " - " + self.relative_path(path))

        # Absorption
        if self.has_absorption:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.absorption].capitalize() + fmt.reset + " (" + str(self.nabsorption) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.absorption: lines.append(line_prefix + " - " + self.relative_path(path))

        # Temperature
        if self.has_temperature:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.temperature].capitalize() + fmt.reset + " (" + str(self.ntemperature) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.temperature: lines.append(line_prefix + " - " + self.relative_path(path))

        # SEDs
        if self.has_seds:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.seds].capitalize() + fmt.reset + " (" + str(self.nseds) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.seds: lines.append(line_prefix + " - " + self.relative_path(path))

        # Total images
        if self.has_total_images:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.total_images].capitalize() + fmt.reset + " (" + str(self.ntotal_images) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.total_images: lines.append(line_prefix + " - " + self.relative_path(path))

        # Direct images
        if self.has_direct_images:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.direct_images].capitalize() + fmt.reset + " (" + str(self.ndirect_images) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.direct_images: lines.append(line_prefix + " - " + self.relative_path(path))

        # Transparent images
        if self.has_transparent_images:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.transparent_images].capitalize() + fmt.reset + " (" + str(self.ntransparent_images) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.transparent_images: lines.append(line_prefix + " - " + self.relative_path(path))

        # Scattered images
        if self.has_scattered_images:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.scattered_images].capitalize() + fmt.reset + " (" + str(self.nscattered_images) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.scattered_images: lines.append(line_prefix + " - " + self.relative_path(path))

        # Dust images
        if self.has_dust_images:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.dust_images].capitalize() + fmt.reset + " (" + str(self.ndust_images) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.dust_images: lines.append(line_prefix + " - " + self.relative_path(path))

        # Dust scattered images
        if self.has_dust_scattered_images:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.dust_scattered_images].capitalize() + fmt.reset + " (" + str(self.ndust_scattered_images) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.dust_scattered_images: lines.append(line_prefix + " - " + self.relative_path(path))

        # Cell temperature
        if self.has_cell_temperature:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.cell_temperature].capitalize() + fmt.reset + " (" + str(self.ncell_temperature) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.cell_temperature: lines.append(line_prefix + " - " + self.relative_path(path))

        # Logfiles
        if self.has_logfiles:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.logfiles].capitalize() + fmt.reset + " (" + str(self.nlogfiles) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.logfiles: lines.append(line_prefix + " - " + self.relative_path(path))

        # Wavelengths
        if self.has_wavelengths:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.wavelengths].capitalize() + fmt.reset + " (" + str(self.nwavelengths) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.wavelengths: lines.append(line_prefix + " - " + self.relative_path(path))

        # Grid
        if self.has_grid:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.grid].capitalize() + fmt.reset + " (" + str(self.ngrid) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add path
            for path in self.grid: lines.append(line_prefix + " - " + self.relative_path(path))

        # Density
        if self.has_gdensity:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.gdensity].capitalize() + fmt.reset + " (" + str(self.ngdensity ) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add path
            for path in self.gdensity: lines.append(line_prefix + " - " + self.relative_path(path))

        # Density
        if self.has_tdensity:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.tdensity].capitalize() + fmt.reset + " (" + str(self.ntdensity) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add path
            for path in self.tdensity: lines.append(line_prefix + " - " + self.relative_path(path))

        # Tree
        if self.has_tree:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.tree].capitalize() + fmt.reset + " (" + str(self.ntree) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.tree: lines.append(line_prefix + " - " + self.relative_path(path))

        # Convergence
        if self.has_convergence:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.convergence].capitalize() + fmt.reset + " (" + str(self.nconvergence) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.convergence: lines.append(line_prefix + " - " + self.relative_path(path))

        # Dust mass
        if self.has_dust_mass:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.dust_mass].capitalize() + fmt.reset + " (" + str(self.ndust_mass) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.dust_mass: lines.append(line_prefix + " - " + self.relative_path(path))

        # Dust mix properties
        if self.has_dust_mix_properties:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.dust_mix_properties].capitalize() + fmt.reset + " (" + str(self.ndust_mix_properties) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.dust_mix_properties: lines.append(line_prefix + " - " + self.relative_path(path))

        # Dust optical properties
        if self.has_dust_optical_properties:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.dust_optical_properties].capitalize() + fmt.reset + " (" + str(self.ndust_optical_properties) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.dust_optical_properties: lines.append(line_prefix + " - " + self.relative_path(path))

        # Dust grain sizes
        if self.has_dust_grain_sizes:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.dust_grain_sizes].capitalize() + fmt.reset + " (" + str(self.ndust_grain_sizes) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.dust_grain_sizes: lines.append(line_prefix + " - " + self.relative_path(path))

        # Parameters
        if self.has_parameters:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + output_type_choices[output_types.parameters].capitalize() + fmt.reset + " (" + str(self.nparameters) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.parameters: lines.append(line_prefix + " - " + self.relative_path(path))

        # Other
        if self.has_other:
            lines.append(line_prefix)

            # Add title
            title = fmt.green + fmt.underlined + "Other output" + fmt.reset + " (" + str(self.nother) + "):"
            lines.append(line_prefix + title)
            lines.append(line_prefix)

            # Add paths
            for path in self.other: lines.append(line_prefix + " - " + self.relative_path(path))

        # Add new line
        lines.append(line_prefix)

        # Return
        return "\n".join(lines)

    # -----------------------------------------------------------------

    def show(self, line_prefix=""):

        """
        This function ...
        :param line_prefix:
        :return:
        """

        print(self.to_string(line_prefix=line_prefix))

# -----------------------------------------------------------------


# -*- coding: utf-8 -*-
"""
Created on Thu Feb  9 10:08:28 2017

@author: Travis Kemper
@email: travis.kemper.w@gmail.com

"""

import numpy
from stl import mesh
import datetime

class Order:
    def __init__(self):
        self.date = datetime.datetime.now()
        self.parts = {}
        self.number = 0
        self.df = None

class Material:

    def __init__(self,name):
        self.name = name
        self.density = 0.001 #g/mm^3
        self.cost_g = 100.0

class Part:
    def __init__(self,name):

        self.name = name
        self.mesh = None
        self.properties = {}
        self.units = 'mm'
        self.qty = 1
        self.materials = {}
        self.notes = ""
        self.auto_orient = True
        self.cost = 0.0

    def read_mesh(self,stl_file):
        self.stl_file = stl_file
        self.mesh = mesh.Mesh.from_file(stl_file)

    def calc_props(self):

        volume, cog, inertia = self.mesh.get_mass_properties()
        self.properties['Volume'] = volume

        mesh_size = self.mesh.max_ -  self.mesh.min_

        self.properties['Size'] = mesh_size

        surf_areas = numpy.zeros(3)
        surf_areas[0] =  mesh_size[0]* mesh_size[1] # XY
        surf_areas[1] =  mesh_size[1]* mesh_size[2] # YZ
        surf_areas[2] =  mesh_size[2]* mesh_size[0] # XZ
        self.properties['SrufAreas'] = surf_areas #[ round(s,3) for s in surf_areas]

        # Find surface area and height based on given orientation
        self.properties['SrufArea'] = surf_areas[0]
        self.properties['Height'] =  mesh_size[2]

        if( self.auto_orient ):
            max_area_index = numpy.argmax(surf_areas)
            self.properties['SrufArea'] = surf_areas[max_area_index]

            if( max_area_index == 0 ):
                #area_id = 'XY'
                self.properties['Height'] = mesh_size[2]
                self.properties
            elif( max_area_index == 1 ):
                #area_id = 'YZ'
                self.properties['Height'] = mesh_size[0]
            elif( max_area_index == 2 ):
                #area_id = 'XZ'
                self.properties['Height'] = mesh_size[1]

    def calc_mass(self,material):

        self.properties['Mass'] = self.properties['Volume']*material.density


import pickle
import os
import re
import sqlalchemy as db
from sqlalchemy.sql import text
import cx_Oracle

#################################################################################

###################################################################################
def main() :
    student = []
    name = ''
    kor_s = 0
    math_s = 0
    eng_s = 0
    os.environ['NLS_LANG'] = '.UTF8'
    engine = db.create_engine("oracle://hr:hr@localhost:1521/xe")
    # student = []
    student = load_cust(student, engine)
    print(student)
    while True:
        print("""
    다음 중 작업하실 메뉴를 선택하세요
    I : 성적 입력
    S : 과목별 성적 조회
    P : 학생별 성적 조회
    U : 학생 성적 수정
    D : 학생 성적 삭제
    Q : 프로그램 종료""")
        sel_main = input("원하시는 작업을 선택해 주세요 :  ").strip().upper()
        if sel_main == "I":
            sel_I(student, name, kor_s, math_s, eng_s)
        elif sel_main == "S":
            sel_S(student)
        elif sel_main == "P":
            sel_P(student)
        elif sel_main == "U":
            sel_U(student)
        elif sel_main == "D":
            sel_D(student)
        elif sel_main == "Q":
            break
        else :
            print("다시 입력해 주세요.", end = "  ")
    return student
#################################################################################
#################################################################################
def sel_I(student, name, kor_s, math_s, eng_s):
    print("성적 입력란입니다.")
    name = ''
    point = 0
    i = 0
    score = {"name":"", "kor":"", "math":"", "eng":""}
    name = input("이름을 입력해 주세요 : " )
    #ins_score(kor_s, math_s, eng_s)
    while i < 4 :
        if i == 0 :
            sub = "국어"
            i = i  + 1
        elif i == 1 :
            sub = "수학"
            kor_s = point
            i = i  + 1
        elif i == 2 :
            sub = "영어"
            math_s = point
            i = i  + 1
        elif i == 3:
            eng_s = point
            i = i + 1
            break
        print('%s' % sub, "점수를 입력해 주세요.")
        point = input()
        if 0 <= int(point) <=100 :
            pass
        else:
            print("잘못 입력하셨습니다.")
            i = i-1
    score["name"] = name
    score["kor"] = kor_s
    score["math"] = math_s
    score["eng"] = eng_s
    student.append(score)
    print((len(student)-1), "번 학생이 저장 됐습니다.")
    return name, kor_s, math_s, eng_s, student

#################################################################################
#################################################################################
def ins_score(kor_s, math_s, eng_s) :
    i = 0
    point = 0
    kor_s = 0
    math_s = 0
    eng_s = 0
    while i < 4 :
        if i == 0 :
            sub = "국어"
            i = i  + 1
        elif i == 1 :
            sub = "수학"
            kor_s = point
            i = i  + 1
        elif i == 2 :
            sub = "영어"
            math_s = point
            i = i  + 1
        elif i == 3:
            eng_s = point
            i = i + 1
            break
        print('%s' % sub, "점수를 입력해 주세요.")
        point = input()
        if type(point) == "int" :
            if int(point) >= 0 and int(point) <= 100 :
                pass
            else :
                print("0 ~ 100의 숫자를 입력해 주세요.")
                i = i-1
        else :
            print("잘못입력하셨습니다.")
    return kor_s, math_s, eng_s
#################################################################################
#################################################################################
def sel_S(student):
    while True :
        print("""
과목별 점수 조회 화면입니다.
원하는 작업을 골라주세요
kor : 국어 점수 조회
math : 수학 점수 조회
eng : 영어 점수 조회
max : 최고 점수 조회
min : 최저 점수 조회
back : 뒤로 가기 """)
        sub_s = input()
        if sub_s in ("kor", "math", "eng") :
            sub_s2 = sub_s.replace("kor", "국어")
            sub_s2 = sub_s2.replace("math", "수학")
            sub_s2 = sub_s2.replace("eng", "영어")
            print('%s' % sub_s2, "과목입니다.")
            for i in range(len(student)) :
                print(student[i]["name"],"  ", student[i][sub_s], "점")
            break
        elif sub_s in ("max", "min") :
            comparison = []
            m_name = []
            while True :
                print ("""
어떤 과목을 조회할까요""")
                sub_s2 = input("""
kor : 국어점수
math : 수학점수
eng : 영어점수
""")
                if sub_s2 in ("kor", "math", "eng") :
                    for i in range(len(student)):
                        comparison.append(int(student[i][sub_s2]))

                    if sub_s == "max" :
                        a1 = max(comparison)
                    else :
                        a1 = min(comparison)
                    for i in range(len(student)):
                        if a1 == int(student[i][sub_s2]):
                            m_name.append(student[i]["name"])

                    sub_s2 = sub_s2.replace("kor", "국어 ")
                    sub_s2 = sub_s2.replace("math", "수학 ")
                    sub_s2 = sub_s2.replace("eng", "영어 ")
                    sub_s = sub_s.replace("max", "최고")
                    sub_s = sub_s.replace("min", "최저")
                    for i in range (len(m_name)):
                        print ("{} 득점자는".format(sub_s), m_name[i], "이고")
                    print("{} {}".format(sub_s2, sub_s), "점수는 ", a1, "점 입니다.")
                    break
                else :
                    print("다시 입력해 주세요.")
        elif sub_s in ("back") :
            break
        else :
            print("다시 입력해 주세요.")
#################################################################################
#################################################################################
def sel_P(student):
    stu_s = input("""
조회할 학생 이름을 입력해 주세요. :  """)
    r1 = 0
    for i in range(len(student)) :
        if student[i]["name"] == stu_s :
            print(student[i])
            r1 = r1+1
            break
    if r1 == 0:
        print("학생이 없습니다.")
#################################################################################
#################################################################################
def sel_U(student):
    while True :
        stu_u = input("""
    수정할 학생 이름을 입력해 주세요.
    취소를 원하시면 Q를 입력해 주세요 : """)
        if stu_u == "Q":
            break
        else :
            r1 = 0
            for i in range(len(student)) :
                if student[i]["name"] == stu_u :
                    r1 = r1+1
                    print (
        "%s" %stu_u,"의 정보입니다.",
        student[i])
                    while True :
                        stu_u2 = input(
            """수정할 과목을 입력해 주세요(국어, 수학, 영어, 취소)  """)
                        stu_u3 = stu_u2.replace("국어", "kor")
                        stu_u3 = stu_u3.replace("수학", "math")
                        stu_u3 = stu_u3.replace("영어", "eng")
                        # while True :
                        if stu_u3 in ("kor", "math", "eng"):
                            stu_u4 = input("""
        몇점으로 수정하시겠습니까  """)
                            student[i][stu_u3] = stu_u4
                            print("""
        {0}의 {1}점수가 {2}점으로 수정됐습니다.""".format(stu_u, stu_u2, stu_u4))
                            break
                        elif stu_u2 == "취소":
                            break
                        else :
                            print("잘못입력하셨습니다.")
            if r1 == 0:
                    print("다시 입력해 주세요")
            else :
                break
    return student
#################################################################################
#################################################################################
def sel_D(student):
    while True:
        r1 = 0
        stu_d = input("""
삭제할 학생 이름을 입력해 주세요.""")
        for i in range (len(student)):
            if stu_d == student[i]["name"]:
                del student[i]
                print("%s" %stu_d, "의 정보가 삭제됐습니다.")
                r1 = r1+1
                break
        if r1 == 0:
            print("학생 이름이 없습니다.")
        else : break
#################################################################################
#################################################################################
def load_cust(student, engine):
    with engine.connect() as conn:
        result = conn.execute("SELECT * FROM score")
        student = [dict(row) for row in result]
    return student

main()

##앞으로 구현해야 할것
##오라클 디벨로퍼랑 연결하기
##student list에 '반' 넣고 과목별, 개인별, 반별 평균, 표준편차 구해보기
##t분포,F분포 이용해서 검추정 및 회귀분석 이용해서 회귀분석 구현하기
##가능하다면 html 과 연결해서 html(f.e)-python-오라클(b.e)로 연결되게 만들기


from web3 import Web3
import json
import requests
import time

class BSC_Network:
    pancake_contract = None
    tau_contract = None
    usdc_contract = None
    router_address = None
    tau_lamdenbridge_contract = None
    tau_bridge_address = None
    bsc_rpc = 'https://bsc-dataseed.binance.org/'
    web3 = Web3(Web3.HTTPProvider(bsc_rpc))

    def __init__(self):
        pancake_abi_file = open('pancake_abi.json','r')
        pancake_abi = json.load(pancake_abi_file)
        self.router_address = self.web3.toChecksumAddress("0x10ED43C718714eb63d5aA57B78B54704E256024E")
        self.pancake_contract = self.web3.eth.contract(address=router_address, abi=pancake_abi)
        tau_abi_file = open('tau_abi.json','r')
        tau_abi = json.load(tau_abi_file)
        tau_address = self.web3.toChecksumAddress("0x70d7109d3afe13ee8f9015566272838519578c6b")
        self.tau_contract = self.web3.eth.contract(address=tau_address, abi=tau_abi)
        usdc_abi_file = open('usdc_abi.json','r')
        usdc_abi = json.load(usdc_abi_file)
        usdc_address = self.web3.toChecksumAddress("0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d")
        self.usdc_contract = self.web3.eth.contract(address=usdc_address, abi=usdc_abi)
        tau_bridge_abi_file = open('bridge_abi.json','r')
        tau_bridge_abi = json.load(tau_bridge_abi_file)
        self.tau_bridge_address = self.web3.toChecksumAddress("0x46e126489b7965ecc13e58f72f6d14b140614c18")
        self.tau_bridge_contract = self.web3.eth.contract(address=self.tau_bridge_address, abi=tau_bridge_abi)

    def getOutcomeBeforeSwap(self, pair, amount_in, swap_type):
        if(swap_type == "sell"):
            router_path = [self.web3.toChecksumAddress(pair["outputCurrency"]), self.web3.toChecksumAddress("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"), self.web3.toChecksumAddress(pair["inputCurrency"])] # Input is TAU, Outcome is USDC
        elif(swap_type == "buy"):
            router_path = [self.web3.toChecksumAddress(pair["inputCurrency"]), self.web3.toChecksumAddress("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"), self.web3.toChecksumAddress(pair["outputCurrency"])] # Input is USDC, Outcome is TAU
        amountsOut = self.pancake_contract.functions.getAmountsOut(self.web3.toWei(amount_in, 'ether'), router_path).call()
        return self.web3.fromWei(amountsOut[2], 'ether')

    def doSwap(self,wallet_address, private_key, pair, amount_in, swap_type):
        if(swap_type == "sell"):
            router_path = [self.web3.toChecksumAddress(pair["outputCurrency"]), self.web3.toChecksumAddress("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"), self.web3.toChecksumAddress(pair["inputCurrency"])] # Input is TAU, Outcome is USDC
            self.tau_contract.approve(wallet_address, self.router_address, amount_in, private_key)

        if(swap_type == "buy"):
            router_path = [self.web3.toChecksumAddress(pair["inputCurrency"]), self.web3.toChecksumAddress("0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"), self.web3.toChecksumAddress(pair["outputCurrency"])] # Input is USDC, Outcome is TAU
            self.usdc_contract.approve(wallet_address, self.router_address, amount_in, private_key)

        nonce = self.web3.eth.get_transaction_count(wallet_address)
        txn = self.pancake_contract.functions.swapExactTokensForTokens(
            self.web3.toWei(amount_in, 'ether'),
            0, # here setup the minimum destination token you want to have, you can do some math, or you can put a 0 if you don't want to care
            router_path,
            wallet_address,
            (int(time.time()) + 1000000)
        ).buildTransaction({
            'from': wallet_address,
            #'gas': 260947,
            'gasPrice': self.web3.toWei('5','gwei'),
            'nonce': nonce,
        })
        signed_txn = self.web3.eth.account.sign_transaction(txn, private_key=private_key)
        tx_token = self.web3.eth.send_raw_transaction(signed_txn.rawTransaction)
        return self.web3.toHex(tx_token)

    def doBridgeToLamden(self, wallet_address, private_key, amount, swap_type):
        if(swap_type == "sell"):
            self.usdc_contract.approve(wallet_address, self.bridge_address, amount, private_key)
            nonce = self.web3.eth.get_transaction_count(wallet_address)
            txn = self.bridge_contract.functions.deposit(
                self.web3.toHex(amount),
                "ff61544ea94eaaeb5df08ed863c4a938e9129aba6ceee5f31b6681bdede11b89"
            ).buildTransaction({
                'from': wallet_address,
                #'gas': 260947,
                'gasPrice': self.web3.toWei('5','gwei'),
                'nonce': nonce,
            })
            signed_txn = self.web3.eth.account.sign_transaction(txn, private_key=private_key)
            tx_token = self.web3.eth.send_raw_transaction(signed_txn.rawTransaction)
            return self.web3.toHex(tx_token)

        if(swap_type == "buy"):
            self.tau_contract.approve(wallet_address, self.bridge_address, amount, private_key)
            nonce = self.web3.eth.get_transaction_count(wallet_address)
            txn = self.bridge_contract.functions.deposit(
                self.web3.toHex(amount),
                "ff61544ea94eaaeb5df08ed863c4a938e9129aba6ceee5f31b6681bdede11b89"
            ).buildTransaction({
                'from': wallet_address,
                #'gas': 260947,
                'gasPrice': self.web3.toWei('5','gwei'),
                'nonce': nonce,
            })
            signed_txn = self.web3.eth.account.sign_transaction(txn, private_key=private_key)
            tx_token = self.web3.eth.send_raw_transaction(signed_txn.rawTransaction)
            return self.web3.toHex(tx_token)

class Lamden_Network:
    endpoint = "https://blockservice.nebulamden.finance/current/all/con_rocketswap_official_v1_1"
    fee_percent = 0.005

    def getOutcomeBeforeSwap(self, amount_in, pair, swap_type):
        if(swap_type == "sell"):
            url = self.endpoint + "/reserves/" + pair
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36',

            }
            response = requests.get(url, headers=headers)
            data = response.json()
            token_reserve = float(data["con_rocketswap_official_v1_1"]["reserves"][pair][1]["__fixed__"])
            currency_reserve = float(data["con_rocketswap_official_v1_1"]["reserves"][pair][0]["__fixed__"])

            k = currency_reserve * token_reserve
            new_currency_reserve = currency_reserve + amount_in
            new_token_reserve = k / new_currency_reserve
            tokens_purchased = token_reserve - new_token_reserve
            fee = tokens_purchased * self.fee_percent
            tokens_purchased = tokens_purchased - fee

            return tokens_purchased

        if(swap_type == "buy"):
            url = self.endpoint + "/reserves/" + pair
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36',

            }
            response = requests.get(url, headers=headers)
            data = response.json()
            token_reserve = float(data["con_rocketswap_official_v1_1"]["reserves"][pair][1]["__fixed__"])
            currency_reserve = float(data["con_rocketswap_official_v1_1"]["reserves"][pair][0]["__fixed__"])

            k = currency_reserve * token_reserve
            new_token_reserve = token_reserve + amount_in
            new_currency_reserve = k / new_token_reserve
            currency_purchased = currency_reserve - new_currency_reserve
            fee = currency_purchased * self.fee_percent
            currency_purchased = currency_purchased - fee

            return currency_purchased

bsc = BSC_Network()
lamden = Lamden_Network()

while(True):
    time.sleep(2)

    bsc_buy_amount = bsc.getOutcomeBeforeSwap({"inputCurrency":"0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", "outputCurrency": "0x70d7109D3AfE13EE8f9015566272838519578c6b"}, 100, "buy")
    lamden_sell = lamden.getOutcomeBeforeSwap(float(bsc_buy_amount), "con_lusd_lst001", "sell")

    if(lamden_sell > 103):
        print("Its worth to arbitrage now and buy TAU on Pancakeswap for 100 USDC and sell Outcome TAU on Rocketswap")

        bsc.doSwap(bsc.web3.toChecksumAddress("PUBKEY"), "PRIVKEY",{"inputCurrency":"0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", "outputCurrency": "0x70d7109D3AfE13EE8f9015566272838519578c6b"}, 100, "buy")
        print(f"Outcome BSC Buy using 100 USDC getting TAU: {bsc_buy_amount}")

        #bridge from bsc to lamden and sell tau
        print(f"Outcome Lamden Sell {bsc_buy_amount} TAU getting USDC: {lamden_sell}")

    lamden_buy_amount = lamden.getOutcomeBeforeSwap(100, "con_lusd_lst001", "buy")
    bsc_sell_amount = bsc.getOutcomeBeforeSwap({"inputCurrency":"0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", "outputCurrency": "0x70d7109D3AfE13EE8f9015566272838519578c6b"},float(lamden_buy_amount), "sell")

    if(bsc_sell_amount > 103):
        print("Its worth to arbitrage now and buy TAU on Rocketswap for 100 USDC and sell Outcome TAU on Pancakeswap")

        #buy on lamden and bridge tau to bsc
        print(f"Outcome Lamden Buy using 100 USDC getting TAU: {lamden_buy_amount}")

        bsc.doSwap(bsc.web3.toChecksumAddress("PUBKEY"), "PRIVKEY",{"inputCurrency":"0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", "outputCurrency": "0x70d7109D3AfE13EE8f9015566272838519578c6b"}, MISSING_AMOUNT, "sell")
        print(f"Outcome BSC Sell {lamden_buy_amount} TAU getting USDC: {bsc_sell_amount}")


import sys
from PyPDF2 import PdfFileWriter, PdfFileReader

pdf_name = sys.argv[1]
watermark_name = sys.argv[2]

def open_pdf(file_name):
    try:
        pdf = PdfFileReader(open(file_name, mode='rb'))
        return pdf
    except FileNotFoundError as e:
        print("Check the argument names:", e)
        sys.exit()

def write_pdf(output):
    with open("PDFS/watermarked.pdf", mode='wb') as file:
        output.write(file)
        print("Done.")

def make_watermark(pdf, watermark):
    try:
        input_pdf = open_pdf(pdf)
        wtr_pdf = open_pdf(watermark)
        wtr_page = wtr_pdf.getPage(0)

        output = PdfFileWriter()

        for i in range(input_pdf.getNumPages()):
            page = input_pdf.getPage(i)
            page.mergePage(wtr_page)
            output.addPage(page)
        return output
    except AttributeError:
        print("Check the argv names.")

if __name__ == "__main__":
    out = make_watermark(pdf_name, watermark_name)
    write_pdf(out)