# pylint: disable=too-many-public-methods, too-many-arguments, fixme
"""
CVE-bin-tool tests
"""
import importlib
import os
import shutil
import sys
import tempfile
import unittest

import pytest

from cve_bin_tool.cvedb import CVEDB
from cve_bin_tool.version_scanner import VersionScanner
from .test_data import __all__ as all_test_name
from .utils import download_file, LONG_TESTS

BINARIES_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), "binaries")
# load test data
test_data = list(
    map(lambda x: importlib.import_module(f"test.test_data.{x}"), all_test_name)
)
mapping_test_data = map(lambda x: x.mapping_test_data, test_data)
package_test_data = map(lambda x: x.package_test_data, test_data)

class TestScanner:
    """Runs a series of tests against our "faked" binaries.

    The faked binaries are very small c files containing the same string signatures we use
    in the cve-bin-tool.  They should trigger results as if they contained the library and
    version specified in the file name.

    At this time, the tests work only in python3.
    """

    @classmethod
    def setup_class(cls):
        cls.cvedb = CVEDB()
        if os.getenv("UPDATE_DB") == "1":
            cls.cvedb.get_cvelist_if_stale()
        else:
            print("Skip NVD database updates.")
        # Instantiate a scanner
        cls.scanner = VersionScanner(should_extract=True)
        # temp dir for mapping tests
        cls.mapping_test_dir = tempfile.mkdtemp(prefix="mapping-test-")
        # temp dir for tests that require downloads
        cls.package_test_dir = tempfile.mkdtemp(prefix="package_test-")

    @classmethod
    def teardown_class(cls):
        shutil.rmtree(cls.package_test_dir)
        shutil.rmtree(cls.mapping_test_dir)

    def test_false_positive(self):
        self.scanner.all_cves = []
        with tempfile.NamedTemporaryFile(
            "w+b",
            suffix="-test-false-positive.out",
            dir=self.mapping_test_dir,
            delete=False,
        ) as f:
            common_signatures = [
                # common strings generated by a compiler
                b"\x7f\x45\x4c\x46\x02\x01\x01\x03\n",
                b"GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0\n",
                b"GNU C17 8.1.0 -mtune=core2 -march=nocona -g -g -g -O2 -O2 -O2 -fno-ident -fbuilding-libgcc -fno-stack-protector\n",
                b"../../../../../src/gcc-8.1.0/libgcc/libgcc2.c\n",
                rb"C:\mingw810\x86_64-810-posix-seh-rt_v6-rev0\build\gcc-8.1.0\x86_64-w64-mingw32\libgcc\n",
                b"GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0\n",
                # bare version strings.
                b"1_0",
                b"1_2_3",
                b"1.4",
                b"1.2.3",
                b"6.7a",
                b"8.9.10-11",
                b"1-2",
                b"1-2-4",
                b"1.2.3-rc.1",
            ]
            f.writelines(common_signatures)
            filename = f.name
        for params in self.scanner.scan_file(
            os.path.join(self.mapping_test_dir, filename)
        ):
            if params:
                pytest.fail(msg=f"Checker has detected false positive: {params}")

    @pytest.mark.parametrize(
        "product, version, version_strings",
        (
            (d["product"], d["version"], d["version_strings"])
            for list_data in mapping_test_data
            for d in list_data
        ),
    )
    def test_version_mapping(self, product, version, version_strings):
        """Helper function to scan a binary and check that it contains
        certain cves for a version and doesn't contain others."""

        # Run the scan
        version_strings = list(map(lambda s: f"{s}\n".encode("ascii"), version_strings))
        # first filename will test "is" and second will test "contains"
        filenames = [
            f"-{product}-{version}.out",
            f"{'.'.join(list(product))}-{version}.out",
        ]
        for filename in filenames:
            with tempfile.NamedTemporaryFile(
                "w+b", suffix=filename, dir=self.mapping_test_dir, delete=False
            ) as f:
                f.write(b"\x7f\x45\x4c\x46\x02\x01\x01\x03\n")
                f.writelines(version_strings)
                filename = f.name

            list_products = set()
            list_versions = set()
            expected_path = os.path.join(self.mapping_test_dir, filename)
            for scan_info in self.scanner.recursive_scan(expected_path):
                if scan_info:
                    product_info, file_path = scan_info
                    list_products.add(product_info.product)
                    list_versions.add(product_info.version)
                    assert file_path == expected_path
            assert product in list_products
            assert version in list_versions

    @pytest.mark.parametrize(
        "url, package_name, product, version",
        (
            (d["url"], d["package_name"], d["product"], d["version"])
            for list_data in package_test_data
            for d in list_data
        ),
    )
    @unittest.skipUnless(LONG_TESTS() > 0, "Skipping long tests")
    def test_version_in_package(self, url, package_name, product, version):
        """Helper function to get a file (presumed to be a real copy
        of a library, probably from a Linux distribution) and run a
        scan on it.  Any test using this should likely be listed as a
        long test."""
        # get file
        tempfile = os.path.join(self.package_test_dir, package_name)
        download_file(url + package_name, tempfile)
        # new scanner for the new test.
        # self.scanner = VersionScanner(self.cve_scanner, should_extract=True)
        # run the tests
        list_products = set()
        list_versions = set()
        for scan_info in self.scanner.recursive_scan(tempfile):
            if scan_info:
                product_info, file_path = scan_info
                list_products.add(product_info.product)
                list_versions.add(product_info.version)

        # Make sure the product and version are in the results
        assert product in list_products
        assert version in list_versions

    def test_does_not_scan_symlinks(self):
        """ Test that the scanner doesn't scan symlinks """
        if sys.platform.startswith("linux"):
            # we can only do this in linux since symlink is privilege operation in windows
            os.symlink("non-existant-file", "non-existant-link")
            try:
                with pytest.raises(StopIteration):
                    next(
                        self.scanner.scan_file(
                            os.path.join(self.mapping_test_dir, "non-existant-link")
                        )
                    )
            finally:
                os.unlink("non-existant-link")

    def test_cannot_open_file(self, caplog):
        """ Test behaviour when file cannot be opened """
        with pytest.raises(StopIteration):
            next(
                self.scanner.scan_file(
                    os.path.join(self.mapping_test_dir, "non-existant-file")
                )
            )
        assert str.find("Invalid file", caplog.text)

    def test_clean_file_path(self):
        filepath = (
            "/tmp/cve-bin-tool/dhtei34fd/file_name.extracted/usr/bin/vulnerable_file"
        )
        expected_path = "/usr/bin/vulnerable_file"

        cleaned_path = self.scanner.clean_file_path(filepath)
        assert expected_path == cleaned_path


# -*- coding: utf-8 -*-
import re
import os
import sys
import json
import time
import random
import urllib2
import win32gui
import win32con
import win32api

# Current path
ROOT = ""
# Log path
LOG_PATH = "log.txt"
# Replace special chars to this char in file name
NAME_FILL_CHAR = "-"
# Access random index separate
INDEX_FILL_CHAR = "~"
# config path
CONFIG_PATH = "config.json"
# system encode type
SYS_ENCODE = sys.getfilesystemencoding()
# default config
DEFAULT_CONFIG = ('{\n'
                  '    "api_url": "http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8",\n'
                  '    "picture_url_locat": "images [0] url",\n'
                  '    "name_type": "url",\n'
                  '    "wallpaper_fill_type": "fill",\n'
                  '    "picture_store_path": "pics/",\n'
                  '    "picture_postfix": ""\n'
                  '}')

# CurrentDir can get current path
# Because the path is different between run this script and run pyinstaller file
# this func can return the right path
def CurrentDir():
    path = sys.argv[0]
    if path.endswith('.exe'):
        return path[:path.rindex('\\')]
    else:
        return sys.path[0]

# ConcatURL can concat two url
# when the rightURL is a absolute url (such as "http://xxx.com"), it will return the rightURL directly.
def ConcatURL(leftURL, rightURL):
    if rightURL.startswith('http'):
        return rightURL
    host = leftURL
    slashIndex = leftURL.find('/', 7)
    if slashIndex != -1:
        host = leftURL[:slashIndex]
    if rightURL.startswith('/'):
        return host + rightURL
    else:
        return host + "/" + rightURL

# SlashToBacklash as its name
# This func will replace slash("/") to backlash("\") in pathStr
def SlashToBacklash(pathStr):
    return pathStr.replace('/', '\\')

# ConcatPath can concatenate two path
# when the rightURL is a absolute path (such as "C:\Windows"), it will return the rightPath directly.
def ConcatPath(leftPath, rightPath):
    if rightPath.find(':') != -1:
        return rightPath
    if leftPath.endswith('\\'):
        return leftPath + rightPath.strip('\\')
    else:
        return leftPath + '\\' + rightPath.strip('\\')

# MultipleReplace will replace chars, which defined in "excludeStr", to NAME_FILL_CHAR in baseStr
# Example:
#   NAME_FILL_CHAR = "-"
#   > MultipleReplace("123#45*6","#*")
#   "123-45-6"
#
def MultipleReplace(baseStr, excludeStr):
    for char in excludeStr:
        baseStr = baseStr.replace(char, NAME_FILL_CHAR)
    return baseStr

# LegalizeStr can delete some special chars in "baseStr"
def LegalizeStr(baseStr):
    excludeStr = "/:*?<>|\""
    return MultipleReplace(baseStr, excludeStr)

# ReadFile will return file content
def ReadFile(path):
    if not os.path.exists(path):
        Log("%s dosn't exists!" % path, 3, True)
    else:
        if not os.access(path, os.R_OK):
            Log("Can't read %s" % path, 2, True)
    handler = open(path, 'r')
    data = handler.read()
    handler.close()
    return data

# WriteFile will write data to a file
def WriteFile(path, fileName, mode, data):
    if not os.path.exists(path):
        try:
            os.makedirs(path)
        except:
            Log("Can't write %s to %s" % (fileName, path), 2, True)
    else:
        if not os.access(path, os.W_OK):
            Log("Can't write %s to %s" % (fileName, path), 2, True)
    fullPath = ConcatPath(path, fileName)
    handler = open(fullPath, mode)
    handler.write(data)
    handler.close()
    return fullPath

# ReadJSON is a pack of read json
# If important is True, get item failed will lead program to exit
# If important is False, get item failed will return '' and would not lead program to exit
def ReadJSON(jsonObj, key, important=True, isRandom=False):
    if isinstance(jsonObj, dict):
        if jsonObj.has_key(key):
            return jsonObj[key]
        else:
            if important:
                Log("Can't get %s in JSON object!" % key, 1, True)
            else:
                return ''
    else:
        if len(jsonObj) < 1:
            if important:
                Log("The array length is 0", 1, True)
            else:
                return ''
        if isRandom:
            key = random.randrange(0, len(jsonObj))
            return jsonObj[key]
        if not isinstance(key, int):
            Log("Can't use a string key to access array item.key: %s" % key, 1, True)
        if key > len(jsonObj) - 1:
            if important:
                Log("The index is to large.key: %s" % key, 1, True)
            else:
                return ''
        else:
            return jsonObj[key]

# Log is record a log
# When doExit is True, the program will exit after log
def Log(message, code=0, doExit=False):
    errDict = {
        0: "Internal error: ",
        1: "Config file error: ",
        2: "Permission error: ",
        3: "File system error: "
    }
    log = time.ctime() + " " + errDict[code] + message
    WriteFile(ROOT, LOG_PATH, 'a', log + '\n')
    if doExit:
        sys.exit()

# LoadConfig can load a json config file
# and return an object
def LoadConfig(configFilePath):
    if not os.path.exists(configFilePath):
        Log("Config dose not exist!Creating a new config file.", 1)
        WriteFile(ROOT, CONFIG_PATH, 'w', DEFAULT_CONFIG)
    data = ReadFile(configFilePath)
    try:
        obj = json.loads(data)
    except:
        Log("Config file syntax error.",1,True)
    return obj

# Fetch can get data from url
def Fetch(url):
    handler = urllib2.urlopen(url)
    data = handler.read()
    handler.close()
    return data

# FindInJson can get item from json by a special way
# "location" is a new way to access json
# more detail see the README.md file
def FindInJson(json, location):
    steps = location.split(' ')
    restObj = json
    for step in steps:
        isRandomm = False
        if step == ("[" + INDEX_FILL_CHAR + "]"):
           step = 0
           isRandomm = True
        elif step.startswith('[') and step.endswith(']'):
            index = step.strip("[").rstrip("]")
            if index.find(INDEX_FILL_CHAR) != -1:
                indexList = index.split(INDEX_FILL_CHAR)
                minIndex = 0
                maxIndex = 0
                try:
                    if indexList[0] != '':
                        minIndex = int(indexList[0])
                    maxIndex = int(indexList[1])
                    step = random.randrange(minIndex, maxIndex)
                except:
                    Log('locat [] in config file are error.', 1)
                    step = 0
            else:
                step = int(index)
        restObj = ReadJSON(restObj, step, isRandom=isRandomm)
    return restObj

# set wallpaper at windows
def SetWallpaper(imagePath, fillType='fill'):
    tile = "0"
    if fillType == "tile":
        fillType = "center"
        tile = "1"
    fillDict = {
        "fill": "10",
        "fit": "6",
        "Stretch": "2",
        "center": "0",
        "span": "22"
    }
    style = fillDict[fillType]
    key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER,
                                r"Control Panel\Desktop", 0,
                                win32con.KEY_SET_VALUE)
    win32api.RegSetValueEx(key, "WallpaperStyle", 0, win32con.REG_SZ, style)
    win32api.RegSetValueEx(key, "TileWallpaper", 0, win32con.REG_SZ, tile)
    win32gui.SystemParametersInfo(
        win32con.SPI_SETDESKWALLPAPER, imagePath, 1 + 2)

# main script
if __name__ == "__main__":
    # Init root path
    ROOT = CurrentDir() + "\\"

    # Load config
    config = LoadConfig(CONFIG_PATH)

    # If 'name_fill_char' dose not exist in config file it will use the default 'NAME_FILL_CHAR'
    if ReadJSON(config, 'name_fill_char', important=False) != '':
        NAME_FILL_CHAR = ReadJSON(config, 'name_fill_char', important=False)

    # Get api url from config
    api = ReadJSON(config, 'api_url')
    # Get data from url
    data = Fetch(api)

    # load api content to json obj
    try:
        obj = json.loads(data)
    except:
        Log("The API content is not recognised to be a json type.")

#   # Get picture url from json with the special syntax
    picURL = FindInJson(obj, ReadJSON(config, 'picture_url_locat'))
    # Concat url can avoid the different between "http://xxx.com/xx.jpg" and "/xx.jpg"
    pic_host = ReadJSON(config, 'picture_url_host', important=False)
    if pic_host != "":
        picURL = ConcatURL(pic_host, picURL)
    else:
        picURL = ConcatURL(api, picURL)

    picName = ""
    # get nameType from config
    nameType = ReadJSON(config, 'name_type')
    # process different nameType
    if ReadJSON(config, 'picture_url_locat').find(INDEX_FILL_CHAR) != -1:
        if nameType != 'url':
            nameType = 'time'
    if nameType == "url":
        if picURL.find("?") == -1:
            picName = picURL[picURL.rindex("/") + 1:]
        else:
            picName = picURL[picURL.rindex("/") + 1:picURL.index("?")]
    elif nameType == "json":
        picName = FindInJson(obj, ReadJSON(config, 'name_locat'))
        picName = MultipleReplace(picName, ReadJSON(config, 'name_exclude_char'))
        try:
            picName.encode(SYS_ENCODE)
        except:
            Log(('The name has special charactor, '
                 'you should use "name_exclude_char" in the config'
                 ' to exclude it.'), 1, True)
    elif nameType == 'time':
        picName = str(int(time.time() * 1000))
    else:
        Log("Don't support this name_type.", 1, True)
    picName = LegalizeStr(picName)

    # Get picture data
    picData = Fetch(picURL)
    fullPath = WriteFile(
        ConcatPath(ROOT, SlashToBacklash(ReadJSON(config, 'picture_store_path'))),
        picName + ReadJSON(config, 'picture_postfix'), 'wb', picData)
    # Set wallpaper
    SetWallpaper(fullPath, ReadJSON(config, 'wallpaper_fill_type'))


"""
Test executors logic.
"""

import yaml

from unittest import TestCase
from voluptuous import Schema
from six import StringIO

from mock import (
    patch,
    call,
    MagicMock,
)

from plix.executors import (
    BaseExecutor,
    ShellExecutor,
)

from .common import MockDisplay

class ExecutorsTests(TestCase):
    def test_executors_are_yaml_representable(self):
        class MyExecutor(BaseExecutor):
            options_schema = Schema({'foo': str})
            __module__ = 'my_module'

        executor = MyExecutor(options={'foo': 'bar'})

        self.assertEqual(
            yaml.dump({
                'name': 'my_module.MyExecutor',
                'options': {
                    'foo': 'bar',
                }
            }),
            yaml.dump(executor),
        )

    def test_executors_are_yaml_representable_without_options(self):
        class MyExecutor(BaseExecutor):
            options_schema = Schema({'foo': str})
            __module__ = 'my_module'

        executor = MyExecutor(options={})

        self.assertEqual(
            yaml.dump('my_module.MyExecutor'),
            yaml.dump(executor),
        )

    def test_base_executors_requires_a_returncode(self):
        class MyExecutor(BaseExecutor):
            def execute_one(self, environment, command, output):
                pass

        display = MockDisplay()
        executor = MyExecutor()

        with self.assertRaises(RuntimeError):
            executor.execute(
                environment={},
                commands=['a'],
                display=display,
            )

    def test_base_executors_returns_true_on_success(self):
        class MyExecutor(BaseExecutor):
            def execute_one(self, environment, command, output):
                return 0

        display = MockDisplay()
        executor = MyExecutor()

        status = executor.execute(
            environment={},
            commands=['a'],
            display=display,
        )

        self.assertTrue(status)

    def test_base_executors_returns_false_on_failure(self):
        class MyExecutor(BaseExecutor):
            def execute_one(self, environment, command, output):
                return 1

        display = MockDisplay()
        executor = MyExecutor()

        status = executor.execute(
            environment={},
            commands=['a'],
            display=display,
        )

        self.assertFalse(status)

    def test_shell_executor_execute(self):
        environment = {"FOO": "BAR"}
        commands = ["alpha", "beta"]

        display = MockDisplay()
        executor = ShellExecutor()

        def Popen(command, env=None, *args, **kwargs):
            process = MagicMock()
            process.stdout = StringIO(command)
            process.returncode = 0

            self.assertEqual(environment, env)

            return process

        with patch('subprocess.Popen', side_effect=Popen):
            executor.execute(
                environment=environment,
                commands=commands,
                display=display,
            )

        self.assertEqual(
            display.start_command.mock_calls,
            [
                call(index=0, command="alpha"),
                call(index=1, command="beta"),
            ],
        )
        self.assertEqual(
            display.stop_command.mock_calls,
            [
                call(index=0, command="alpha", returncode=0),
                call(index=1, command="beta", returncode=0),
            ],
        )
        self.assertEqual(
            display.command_output.mock_calls,
            [
                call(0, "alpha"),
                call(1, "beta"),
            ],
        )


#!/usr/bin/python3
import pandas as pd
import numpy as np
import seaborn as sns
import statsmodels.formula.api as sm
import pylab as plt
import argparse
import os
import matplotlib
from matplotlib.gridspec import GridSpec
from pathlib import Path
from tqdm import tqdm

plt.style.use('bmh')

HOME = str(Path.home())

PLOT_DIR = os.path.join(HOME, 'source/presentationEAVM/plots')

def RAD(deg):
    return deg * np.pi / 180.0

def CREATE_STATS_PLOTS(num_seg, out_dir):
    df = data_lv.loc[data_lv.Segm == num_seg]
    result = sm.ols(formula="Voltage ~ WT", data=df).fit()

    fig = plt.figure(figsize=(5, 7))

    gs = GridSpec(3, 1)
    ax2 = fig.add_subplot(gs[2, 0])
    ax1 = fig.add_subplot(gs[0:2, 0])
    fig.tight_layout(w_pad=8)

    ax1.set_title('Regression plot for {} segment'.format(num_seg))

    sns.distplot(result.resid, ax=ax2, axlabel='Residuals distribution')
    sns.regplot(x='WT', y='Voltage', data=df, ax=ax1, x_jitter=.1, x_estimator=np.mean)

    box_text = 'r2 = {}\nslope = {}\np = {}\nNum. observations = {}'.format(round(GET_R2(num_seg), 5),
                                                                            round(GET_SLOPE(num_seg), 5),
                                                                            round(result.pvalues['WT'], 10),
                                                                            int(result.nobs))
    props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
    ax1.text(0.05, 0.75, box_text, transform=ax1.transAxes, bbox=props)

    plt.savefig(os.path.join(out_dir, '{}.png'.format(num_seg)))
    plt.close()

def GET_R2(num_seg):
    df = data_lv.loc[data_lv.Segm == num_seg]
    result = sm.ols(formula="Voltage ~ WT", data=df).fit()
    return result.rsquared

def GET_SLOPE(num_seg):
    df = data_lv.loc[data_lv.Segm == num_seg]
    result = sm.ols(formula="Voltage ~ WT", data=df).fit()
    return result.params.WT

if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    parser.add_argument('-f', type=str, help='.csv file with data')
    parser.add_argument('-d', type=str, help='directory with *data*.csv')

    args = parser.parse_args()

    if args.f is None and args.d is None:
        raise ValueError('No path to data')
    elif args.d is None:
        data_lv = pd.read_csv(args.f, index_col=0)
        folder = args.f.split('/')[-1].split('.')[0]
        PLOT_DIR = os.path.join(PLOT_DIR, folder)
        if not os.path.isdir(PLOT_DIR):
            os.mkdir(PLOT_DIR)

        for n in tqdm(range(1, 18)):
            CREATE_STATS_PLOTS(n, PLOT_DIR)

    elif args.f is None:
        files = list(os.walk(args.d))[0][2]
        for f in tqdm(files):
            data_lv = pd.read_csv(os.path.join(args.d, f), index_col=0)
            folder = f.split('/')[-1].split('.')[0]
            out_dir = os.path.join(PLOT_DIR, folder)
            if not os.path.isdir(out_dir):
                os.mkdir(out_dir)

            for n in range(1, 18):
                CREATE_STATS_PLOTS(n, out_dir)


"""
Functionality for working with plasma density data created by the
NURD algorithm

See Zhelavskaya et al., 2016, doi:10.1002/2015JA022132
"""

import os
import glob
import time
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from spacepy import pycdf

def read_cdf(filename, \
	pathname='/Users/mystical/Work/Science/RBSP/DataCdf/rbsp-a/nurd/'):
	"""
	read_cdf(filename, \
		pathname='/Users/mystical/Work/Science/RBSP/DataCdf/rbsp-a/nurd/')

	Read NURD cdf file as downloaded from:
		ftp://rbm.epss.ucla.edu/ftpdisk1/NURD

	Input: filename string, pathname string
	Output: nurd dataframe with columns ['UT' [datetime], 'timestamp',
					'L', 'MLT', 'MLAT', 'R', 'ne'] otherwise -1 if cdf file
					not found.
	"""

	fullpath_filename = pathname + filename
	if not os.path.isfile( fullpath_filename ):
		print("**** File Not Found: " + fullpath_filename)
		return -1

	data = pycdf.CDF(fullpath_filename)

	nurd = pd.DataFrame( data['Epoch'][:], columns=['UT'])
	nurd['timestamp'] = nurd['UT'].apply( lambda x: time.mktime(x.timetuple()) )
	nurd['L'] = data['L'][:]
	nurd['MLT'] = data['MLT'][:]
	nurd['MLAT'] = data['magLat'][:]
	nurd['R'] = np.sqrt(data['x_sm'][:]**2 + data['y_sm'][:]**2 + \
		data['z_sm'][:]**2)
	nurd['ne'] = data['density'][:]

	# Drop undefined location values
	nurd.drop(np.where( np.isnan(nurd.L) )[0], inplace=True)

	# Store metadata as attributes
	# DOESN'T ACTUALLY WORK, GETS ERASED BY OTHER PANDAS FUNCTIONS
	nurd.spacecraft = 'RBSP-a'

	data.close()

	return nurd

def find_psphere(ne_eq, L):
	"""
	find_psphere(ne_eq, L)

	Returns a boolean array of whether the density is inside
	or outside the plasmapause defined as 100 cm^-3
	at L=4 and scaled as L^-4

	Inputs: ne_eq, L arrays of equatorial density values and L-shell
	Outputs: pshere boolean array of whether point is inside plasmasphere
	"""

	ne_boundary = 100*(4/L)**4
	psphere = np.where( ne_eq >= ne_boundary, True, False )
	psphere = np.where( ne_eq < 0, True, psphere )
	psphere = np.where( np.isnan(ne_eq), True, psphere )

	return psphere

def denton_ne_eq( ne, L, R ):
	"""
	denton_ne_eq( ne, L, R ):

	Estimates the equatorial density, ne_eq, based on L and R
	using the Denton et al., 2002 formulation

	Inputs: arrays of ne, L, R
	Output: array of  ne_eq
	"""

	# Ignore warnings on log(<0)
	old = np.seterr( invalid = 'ignore' )

	alpha_ne = 6.0 - 3.0*np.log10( ne ) +0.28*(np.log10( ne ))**2.0
	alpha_L = 2.0-0.43*L

	ne_eq = ne/np.power((L/R),(alpha_ne+alpha_L))

	# Denton forumulation for ne_eq only value over certain range
	# of L, R and ne otherwise stick with ne
	ne_eq = np.where( np.logical_or(R < 2.0, L < 2.5), ne, ne_eq)
	ne_eq = np.where( np.logical_or(ne > 1500.0, ne_eq > ne ), ne, ne_eq)

	return ne_eq

def plot_density_psphere( nurd ):
	"""
	plot_density_psphere( nurd )

	Make a plot of density as a function of L, color-coded by
	inside/outside plasmasphere
	"""

	fig = plt.figure()
	ax = plt.gca()
	ax.scatter( nurd[nurd.psphere].L, nurd[nurd.psphere].ne_eq, \
		color='b', s=2, label='Plasmasphere')
	ax.scatter( nurd[nurd.psphere==False].L, nurd[nurd.psphere==False].ne_eq, \
		color='r', s=2, label='Plasmatrough')
	ax.set_yscale('log')
	ax.set_xlabel('L');
	ax.set_ylabel('Equatorial Electron Density, cm^-3')
	ax.set_title(nurd.spacecraft + ' ' + \
		nurd.UT.iloc[0].strftime('%Y-%m-%d %H:%M:%S') + ' - ' + \
		nurd.UT.iloc[-1].strftime('%Y-%m-%d %H:%M:%S') + ' UT')
	ax.legend()
	ax.set_ylim( min(nurd.ne_eq), max(nurd.ne_eq) )

	plt.show(block=False)
	plt.ion()

	return

def load_day( datestr ):
	"""
	load_day( datestr )

	Given a datestring in the form 'YYYYMMDD', read the nurd cdf file,
	and find the intervals inside the plasmasphere

	Output: nurd dataframe with columns ['UT' [datetime], 'timestamp',
					'L', 'MLT', 'MLAT', 'R', 'ne', 'ne_eq', 'psphere']
					otherwise -1 if cdf file not found
	"""

	pathname='/Users/mystical/Work/Science/RBSP/DataCdf/rbsp-a/nurd/'
	filename_start = 'rbsp-a_'
	filename_end = '.cdf'

	filename_wild = filename_start + datestr + '*' + filename_end
	filename = glob.glob( pathname + filename_wild )
	if not filename:
		print('NURD file not found: ' + pathname + filename_wild)
		return -1
	else:
		# Remove pathname and read cdf file
		filename = filename[0].replace(pathname, '')
		nurd = read_cdf(filename)
		nurd['ne_eq'] = denton_ne_eq(nurd['ne'], nurd['L'], nurd['R'] );
		nurd['psphere'] = find_psphere( nurd['ne_eq'], nurd['L'] );

	return nurd

# SOME TESTING AND DEBUGGING

#filename = 'rbsp-a_20140613_v1_3.cdf';
#nurd = read_cdf( filename )
#nurd['ne_eq'] = denton_ne_eq(nurd['ne'], nurd['L'], nurd['R'] );
#nurd['psphere'] = find_psphere( nurd['ne_eq'], nurd['L'] );
#plot_density_psphere(nurd)

#nurd = load_day('20131010')
#plot_density_psphere(nurd)
#plt.close()
#nurd.loc[nurd['ne']==min(nurd['ne']), 'ne']=-1
#plt.plot(nurd.UT, nurd['ne']/max(nurd['ne']), '.')
#plt.plot(nurd.UT, nurd.psphere, '.')
#plt.plot(nurd.UT, nurd['L']/max(nurd['L']), '.')
#
#plt.show(block=False)


from openpyxl.styles import Font, Border, Side, PatternFill, Alignment
from openpyxl import load_workbook
wb = load_workbook("/Users/gunju/Desktop/self study/python/deep/rpa_basic/1_excel/sample.xlsx")
ws = wb.active

a1 = ws["A1"]
b1 = ws["B1"]
c1 = ws["C1"]

ws.column_dimensions["A"].width = 5 # A열 너비 5로 설정
ws.row_dimensions[1].height = 50 # 1행 높이 50으로 설정

a1.font = Font(color = "FF0000", italic = True, bold = True) # 글자 색상 빨강, 이탤릭, 두껍게
b1.font = Font(color = "CC33FF", name = "Arial", strike = True) # 폰트는 Arial, 취소선
c1.font = Font(color = "0000FF", size = 20, underline = "single") # 글자크기 20, 밑줄 1줄(두 줄 하려면 double)

# 테두리 적용
thin_border = Border(left = Side(style = "thin"), right = Side(style = "thin"), top = Side(style = "thin"), bottom = Side(style = "thin"))
a1.border = thin_border
b1.border = thin_border
c1.border = thin_border

# 90점 넘는 셀에 대해서 초록색으로 적용
for row in ws.rows:
    for cell in row:
        # 셀 정렬 / left, right, top, bottom 가능
        cell.alignment = Alignment(horizontal = "center", vertical = "center")

        if cell.column == 1:
            continue

        # 셀이 정수형 데이터이고 90점보다 높으면
        if isinstance(cell.value, int) and cell.value > 90:
            cell.fill = PatternFill(fgColor = "00FF00", fill_type = "solid")
            cell.font = Font(color = "FF0000")

# 틀 고정
ws.freeze_panes = "B2"

wb.save("/Users/gunju/Desktop/self study/python/deep/rpa_basic/1_excel/sample_style.xlsx")


import numpy as np
import os
import torch
from tqdm import tqdm
from PIL import Image
import wandb

from .evaluation import eval_llh, eval_translation_error, rotation_model_evaluation, translation_model_evaluation

# Global variables
DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

def cosine_decay(step, hyper_param):
    warmup_factor = min(step, hyper_param["warmup_steps"]) / hyper_param["warmup_steps"]
    decay_step = max(step - hyper_param["warmup_steps"], 0) / (hyper_param['num_train_iter']- hyper_param["warmup_steps"])
    return hyper_param['learning_rate'] * warmup_factor * (1 + np.cos(decay_step * np.pi)) / 2

def run_single_epoch(model, data_loader, hyper_param, num_iter, mode=0, optimizer=None):
    progress_bar = tqdm(enumerate(data_loader), total=num_iter)
    epoch_losses = []
    #ipdb.set_trace()
    for (i, input_) in progress_bar:
        #load the images from the batch
        img = input_['image']
        img = img.to(DEVICE).float()
        if mode==0:
            ground_truth = input_['obj_pose_in_camera'][:,:3,:3].to(DEVICE).float()
        else:
            ground_truth = input_['obj_pose_in_camera'][:,:3,-1].to(DEVICE).float()

        loss = model.compute_loss(img, ground_truth)

        epoch_losses.append(loss.item())

        #update the learning rate using the cosine decay
        for g in optimizer.param_groups:
            lr = cosine_decay(i+1, hyper_param)
            g['lr'] = lr
        #backward pass through the network
        optimizer.zero_grad()
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), 1.)

        optimizer.step()

        if i == num_iter:
            break
    mean_epoch_loss = np.mean(epoch_losses)
    return mean_epoch_loss

def run_rotation_training(model, train_dataset, val_dataset, optimizer, hyper_param, checkpoint_dir, start_epoch=0):
    """In each epoch the model is trained for num_train_iter iterations defined in the configuration.
    The evaluation step includes calculating the loglikelihood and the spread on a num_eval_step iterations.
    The model is saved in pre-defined intervals, defined in the configuration. If additionally the full evaluation is enabled,
    also all defined evaluation metrics are calculated

    """
    train_losses = []
    loglikelihood = []
    mean_errors = []
    median_errors = []
    num_epochs = hyper_param['num_epochs']

    for epoch in range(start_epoch+1, num_epochs+1):
        # training
        model.train()
        #run a single training epoch
        train_loss = run_single_epoch(model=model,
                                      data_loader=train_dataset,
                                      hyper_param=hyper_param,
                                      num_iter=hyper_param['num_train_iter'],
                                      mode=0,
                                      optimizer=optimizer)
        # validation
        model.eval()
        with torch.no_grad():

            llh = []

            for dataset in val_dataset:
                llh.append(eval_llh(model, dataset=dataset,
                                            num_eval_iter=hyper_param['num_val_iter'],
                                            mode=0,
                                            device=DEVICE))

        if epoch==1:
            rotation_model_evaluation(model=model, dataset=val_dataset, hyper_param_rot=hyper_param)

        train_losses.append(train_loss)
        loglikelihood.append(llh)
        # log the loss values
        wandb.log({
            'TrainLoss': train_loss,
            'Loglikelihood': sum(llh)/len(llh)
        })
        print("Epoch:", epoch, "....", "TrainLoss: ", train_loss, "Loglikelihood: ", llh)
        # save a checkpoint
        if ((epoch % hyper_param['save_freq'] == 0) and epoch>=hyper_param['start_save']) or epoch == num_epochs:
            chkpt_name = f'checkpoint_{epoch}.pth' if epoch != num_epochs else f'checkpoint_final.pth'
            chkpt_path = os.path.join(checkpoint_dir, chkpt_name)
            torch.save({
                            'epoch': epoch,
                            'model_state_dict': model.state_dict(),
                            'optimizer_state_dict': optimizer.state_dict(),
                       },
                       chkpt_path
            )
            # For every saved model run a full evaluation if full_evaluation is set to true
        if hyper_param['full_eval']==True and epoch % hyper_param["eval_freq"]==0:
            rotation_model_evaluation(model=model, dataset=val_dataset, hyper_param_rot=hyper_param)
    print("Training finished.")
    print("The final IPDF model was saved to: ", os.path.join(checkpoint_dir, 'checkpoint_final.pth'))
    print("\nFinal evaluation metrics:\n")
    print("Train Loss:\n", train_losses)
    print("\nLoglikelihood:\n", loglikelihood)
    print("\nMean Error:\n", mean_errors)
    print("\nMedian Error:\n", median_errors)

def run_translation_training(model, train_dataset, val_dataset, optimizer, hyper_param, checkpoint_dir, start_epoch=0):
    """In each epoch the model is trained for num_train_iter iterations defined in the configuration.
    The evaluation step includes calculating the loglikelihood and the spread on a num_eval_step iterations.

    """
    num_epochs = hyper_param['num_epochs']
    for epoch in range(start_epoch+1, num_epochs+1):
        # training
        model.train()
        #run a single training epoch
        train_loss = run_single_epoch(model=model,
                                      data_loader=train_dataset,
                                      hyper_param=hyper_param,
                                      num_iter=hyper_param['num_train_iter'],
                                      mode=1,
                                      optimizer=optimizer)
        # validation

        llh = eval_llh(model, dataset=val_dataset,
                                            num_eval_iter=hyper_param['num_val_iter'],
                                            mode=1,
                                            device=DEVICE)

        model.eval()
        error = 0.0
        if epoch % hyper_param["eval_freq"] == 0 or epoch==1:
            error = eval_translation_error(model, dataset=val_dataset,
                                    batch_size=hyper_param['batch_size_val'],
                                    eval_iter=hyper_param['num_val_iter'],
                                    gradient_ascent=True)
        # log the loss values
            wandb.log({
                'TrainLoss': train_loss,
                'Loglikelihood': llh,
                'EstimateError': error
            })
        else:
            wandb.log({
                'TrainLoss': train_loss,
                'Loglikelihood': llh
            })

        print("Epoch:", epoch, "....", "TrainLoss: ", train_loss, " Loglikelihood: ", llh, " Estimate Error: ", error)
        # save a checkpoint
        if ((epoch % hyper_param['save_freq'] == 0) and epoch>=hyper_param['start_save']) or epoch == num_epochs:
            chkpt_name = f'checkpoint_{epoch}.pth' if epoch != num_epochs else f'checkpoint_final.pth'
            chkpt_path = os.path.join(checkpoint_dir, chkpt_name)
            torch.save({
                            'epoch': epoch,
                            'model_state_dict': model.state_dict(),
                            'optimizer_state_dict': optimizer.state_dict(),
                       },
                       chkpt_path
            )
        # For every saved model run a full evaluation if full_evaluation is set to true
        """if hyper_param['full_eval']==True and epoch % hyper_param["eval_freq"]==0:
            translation_model_evaluation(model=model, dataset=val_dataset)"""
    print("Training finished.")
    print("The final IPDF model was saved to: ", os.path.join(checkpoint_dir, 'checkpoint_final.pth'))


"""
https://leetcode.com/problems/sum-of-all-odd-length-subarrays/
Given an array of positive integers arr, calculate the sum of all possible odd-length subarrays.
A subarray is a contiguous subsequence of the array.
Return the sum of all odd-length subarrays of arr.
"""

"""
For array of size x, start with x subarrays
If array.length>2, new array.length-3 subarrays of size 3
If array.length>4, new array.length-4 subarrays of size 5

For all odd n-length subarrays, create m new subarrays
where m is equal to the array length-n.
Add the subarrays.
Add the sums.
"""

from typing import List

class Solution:
    def sumOddLengthSubarrays(self, arr: List[int]) -> int:
        table = arr.copy()

        for n in range(3,len(arr),2):
            for m in range(len(arr)-n):
                table.append(sum(arr[m:m+n]))

        return sum(table)


import sys
#sys.path.insert(0, "/home/tomohiro/workspace/web_scraping_venv/lib/python3.6/site-packages")
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit, send
from flask_pymongo import PyMongo
from bson.objectid import ObjectId
from bs4 import BeautifulSoup
import requests
import re
import sys
import time
import datetime

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
app.config["MONGO_URI"] = "mongodb://localhost:27017/scrapingdb"
async_mode = None
ping_timeout = 300
socketio = SocketIO(app, async_mode=async_mode, ping_timeout=ping_timeout)

# If the connection gets disconnected in the middle,
# A new session is gonna be created, then
# the server is gonna get not to send back the data to the same session's client
# so that set ping_timeout long not to make the socketio disconnected

mongo = PyMongo(app)
bookmarks = mongo.db.bookmarks

thread = None
bk_list = [s for s in bookmarks.find()]
# @app.route("/", methods=["POST"])
# def test_db():
# 	url = request.form["mongo"]
# 	url = {"subject": url}
# 	urls.insert_one(url)
# 	result = mongo.db.urls.find({"subject": url})
# 	return render_template(("base.html"), urls=result)

@app.route("/", methods=["GET"])
def index_return():
	#デフォルト設定でFlaskはhtmlの所在を、flaskアプリケーションと同じ階層にある
	#templatesディレクトリの中に探しに行くので下記のようなパスでよい
	return render_template("base.html")

@app.route("/itmedia/")
def itmedia():
	return render_template("itmedia/itmain.html")

@app.route("/nikkei/")
def nikkei():
	return render_template("nikkei/nkmain.html")

@app.route("/other/")
def other():
	return render_template("other/otmain.html")

@app.route("/bookmark/")
def bookmark():
	global bk_list
	return render_template("bookmark/bkmain.html", list=bk_list)

@socketio.on('bk_db')
def managebk(bk_dict):
	url = bk_dict["url"]
	head = bk_dict["head"]
	head = head.encode("raw_unicode_escape")
	head = head.decode("utf-8")

	bks = {"url": url, "head": head}
	#To prevent duplicate update from db,
	#need to add upsert=True at the end and use the mongo syntax $setOnInsert
	bookmarks.update(
		{'head': head},
		{'$setOnInsert': bks},
		upsert=True)

@socketio.on("message")
def initial_response(msg):
	send("Hello!")
	print(msg)

@socketio.on("myevent")
def handle_message(array):
	url = array["url"]
	word = array["head"]

	#if re.match(r"(^\\[A-Z].*[a-z].*)?", word):
	# 日本語だと強制的にutf-8？にエンコードされるので、それをutf-8に再度デコード
	word = word.encode("raw_unicode_escape")
	word = word.decode("utf-8")

	#Getting the global variable, thread
	#You can always default the thread value as None by doing this
	global thread

	if thread is None:
		#Starting the function, "sending_results" as background task
		#By doing this, you can devide the threads having the funcion to keep sokcket connection(main thread)
		#and one to send the url and keyword
		socketio.start_background_task(target=sending_results(url, word))

def sending_results(url, word):
	start = time.time()

	print(url)
	print(word)

	if url == "" or word == "":
		print("”URL”　と　”キーワード”　を指定してください")
		send("”URL”　と　”キーワード”　を指定")
		sys.exit()

	if not url.endswith("/"):
		url = url + "/"

	try:
		res = requests.get(url)
	except Exception as e:
		print(e)
		print("URLが正しくありません")
		send("URLが正しくありません")
		send(str(e))
		sys.exit()

	# Starting web scraping
	emit("startcomplete",("Executing scraping"))

	soup = BeautifulSoup(res.content, "html.parser")
	a_tag_list = soup.find_all("a")
	protocol = re.search(".*:", url).group(0)
	url_list = []
	### ループ内で毎回appendを呼び出していると、処理が遅くなるので
	### appendにあらかじめ、url_list.appendという関数を入れておく
	append=url_list.append

	for a in a_tag_list:
		a_link = str(a.get("href"))
		if not re.match(".*/.*", a_link):
			print("This hasn't been added to the list  " + a_link)
		elif a_link.startswith("//"):
			full_url = protocol + a_link
			append(full_url)
		elif a_link.startswith("/") or a_link.startswith("./"):
			#Removing "./" or "/" from a_link and conbining it with url
			full_url = url + re.sub(r".?/", "", a_link, count=1)
			append(full_url)
		#Checking if a_link starts with alphanumeric + "/"
		elif re.match(r"^\w+/", a_link):
			full_url = url + a_link
			append(full_url)
		else:
			append(a_link)

	#appendを初期化
	append=list.append

	perfect_url_list =sorted(set([u for u in url_list if not re.match(".*article.*|.*=.*", u )]))
	tmp_list = []
	append=tmp_list.append
	#global bk_list

	for l in perfect_url_list:
		try:
			res = requests.get(l)
		except Exception as e:
			print(e)
			#print("before for loop of a")

		soup = BeautifulSoup(res.content, "html.parser")
		a_tag_list = soup.find_all("a")
		for a in a_tag_list:
			try:
				a_link = str(a.get("href"))
				a_text = re.sub("・|\u3000|\n|\r|\t", "", a.text)
				if re.match(".*{}.*".format(word), a.text) and not a_text in tmp_list:
					if not re.match(".*/.*", a_link):
						pass
					elif a_link.startswith("//"):
						article = protocol + a_link
					elif a_link.startswith("/") or a_link.startswith("./"):
						article = l + re.sub(r".?/", "", a_link, count=1)
						res = requests.get(article)
						if res.status_code != 200:
							article = url + re.sub(r".?/", "", a_link, count=1)
					elif re.match(r"^\w+/", a_link):
						article = l + a_link
						res = requests.get(article)
						if res.status_code != 200:
							article = url + a_link
					else:
						article = a.get("href")

					append(a_text)
					a_dict_list = {
						"head": a_text,
						"url": article,
						#"sentense": get_p(article)
						}
					emit("myresponse", a_dict_list)
			except Exception as e:
				print(e)

		print("Nothing to be sent to you")

	emit("startcomplete", "DONE")
	print("DONE")
	elapsed = time.time() - start
	print(elapsed)
	append=list.append

if __name__ == "__main__":
	#socketio = SocketIO(app) により、socketio.run(app)でflaskアプリを起動できる
	socketio.run(app)

# @app.route("/next", methods=["POST"])
# def arg_check():
#     if request.method == "POST":
#         url = request.form["url"]
#         keyword = request.form["search"]
#         args = url + keyword
#     return render_template("index.html", result=args)

# @app.route("/", methods=["POST"])
# def start_scraping():
#     if request.method == "POST":
#         url = request.form["url"]
#         word = request.form["search"]

#     return render_template("index.html", result=scraping(url,word))

# @app.route("/", methods=["POST"])
# def start_socketio():

# def ack():
#     print("I got your message")

# @socketio.on("my event")
# def handle_message(json):
#     time.sleep(1)
#     emit("my response", json, callback=ack)
#     print("Replied to the clinet")


from json import load
from tkinter.font import BOLD
from openpyxl import load_workbook
from openpyxl.styles import PatternFill, Font

def edm(filename):
    wb = load_workbook(filename)
    sheet = wb['Recipients']
    sheet2 = wb['Link Clicks - Detail']

    # Deleting empty rows
    sheet.delete_rows(1,1)
    sheet.delete_rows(2,1)

    # Deleting invalid Group Names
    count = 0

    for row in sheet['C']:
        count += 1
        if row.value == "Reagan_Toh" or row.value == "Seow_Ying":
            sheet.delete_rows(count,2)

    # Insert Status column
    sheet.insert_cols(5)
    sheet['E1'].value = "Status"

    sheet['E1'].font = Font(bold=True)
    sheet['E1'].fill = PatternFill("solid", fgColor="FFFF00")

    # Clicked but unsubbed
    count = 0

    for row in sheet['K']:
        count += 1
        if row.value == "Y":
            sheet[f'E{count}'].value = "1. Clicked (Unsub)"

    # Clicked
    # Getting unique clicked emails
    clicked = []
    count = 0
    for row in sheet2['B']:
        # Exclude header and blank rows
        count += 1
        if count >= 4:
            clicked.append(row.value)

    clicked = set(clicked)

    # Check if specific emails clicked the eDM
    count = 0

    for row in sheet['B']:
        count += 1
        for item in clicked:
            if item == row.value:
                sheet[f'E{count}'].value = "1. Clicked"

    # Opened eDMs
    count = 0
    for row in sheet['H']:
        count += 1
        if row.value and sheet[f'E{count}'].value == None:
            sheet[f'E{count}'].value = "2. Opened"

    # Soft Bounced and Hard Bounced
    count = 0
    for row in sheet['D']:
        count += 1
        if (row.value == 'Remote Server Error' or row.value == 'Error (C)') and sheet[f'E{count}'].value == None:
            sheet[f'E{count}'].value = "4. Soft Bounced"
        elif row.value == 'Bad Address' and sheet[f'E{count}'].value == None:
            sheet[f'E{count}'].value = "5. Hard Bounced"
        elif row.value == 'Sent' and sheet[f'E{count}'].value == None:
            sheet[f'E{count}'].value = "3. Sent"

    # Save workbook
    wb.save(filename=filename)
    wb.close()


import scipy as sp
import numpy as np
import math
import input_checks
import ode_solver

def sparse_A(size, dm, do):
    """
    Creates a sparse matrix used by the scipy spsolve function.
    ----------
    Parameters
    size : int
        The square dimension value of the matrix.
    dm : float OR int
        Value along the main diagonal.
    do : float OR int
        Value along both the offset diagonals.
    ----------
    Returns
        A scipy sparse csr_matrix.
    """

    diagonals = [np.ones(size)*dm, np.ones(size-1)*do, np.ones(size-1)*do]
    return sp.sparse.diags(diagonals, [0, 1, -1], format='csr')

def full_A(size, dm, do):
    """
    Creates a tridiagonal matrix.
    ----------
    Parameters
    size : int
        The square dimension value of the matrix.
    dm : float OR int
        Value along the main diagonal.
    do : float OR int
        Value along both the offset diagonals.
    ----------
    Returns
        A np.ndarray tridiagonal matrix.
    """

    return np.diag(np.ones(size)*dm, 0) + np.diag(np.ones(size-1)*do, 1) + np.diag(np.ones(size-1)*do, -1)

def solve_diffusion(method, l_bound_type, r_bound_type, l_bound_func, r_bound_func, init_func, D, x_min, x_max, nx, t_min, t_max, nt, source_func=None, l_bound_args=None, r_bound_args=None, init_args=None, source_args=None, use_sparse=True):
    """
    Solves the diffusion equation using finite difference methods, based on the specified method and boundary conditions.
    ----------
    Parameters
    method : string
        Either 'lines', 'explicit_euler', 'implicit_euler', 'crank_nicolson', or 'imex'.
    l_bound_type : string
        Either 'dirichlet', 'neumann', or 'robin'.
    r_bound_type : string
        Either 'dirichlet', 'neumann', or 'robin'.
    l_bound_func : function
        Function that takes values (x, u, t) and list (args) as inputs and returns the left boundary value.
    r_bound_func : function
        Function that takes singular values (x, u, t) and list (args) as inputs and returns the right boundary value.
    init_func : function
        Function that takes (x, u, t) and list (args) as inputs and returns intitial solution array.
    D : float OR int
        Diffusion Coefficient.
    x_min : float OR int
        Minimum x value.
    x_max : float OR int
        Maximum x value.
    nx : int
        Number of x values in the grid, affects step size used for x.
    t_min : float OR int
        Minimum t value.
    t_max : float OR int
        Maximum t value.
    nt : int
        Number of t values in the grid, affects step sized used for t.
    source_func : function
        Function that takes singular values (x, t, u) and list (args) as inputs and returns source value.
    l_bound_args : list OR numpy.ndarray
        Additional arguments needed by 'l_bound_func'. If (l_bound_type) is 'robin', must contain two values [delta, gamma], such that r_bound_func returns delta - gamma*u(x).
    r_bound_args : list OR numpy.ndarray
        Additional arguments needed by 'r_bound_func'. If (r_bound_type) is 'robin', must contain two values [delta, gamma], such that r_bound_func returns delta - gamma*u(x).
    init_args : list OR numpy.ndarray
        Additional arguments needed by 'init_func'.
    source_args : list OR numpy.ndarray
        Additional arguments needed by 'source_func'.
    use_sparse : bool
        True indicates that sparse matrices are used for calculations.
    ----------
    Returns
        A numpy.array with a row of values for each solved parameter, and the final row being the x-values solved at.
    """

    # INPUT CHECKS
    input_checks.test_string(method, 'method')
    if method not in ['lines', 'explicit_euler', 'implicit_euler', 'crank_nicolson']:
        raise Exception('Argument (method) must be either \'lines\', \'explicit_euler\', \'implicit_euler\', \'crank_nicolson\'.')
    input_checks.test_string(l_bound_type, 'l_bound_type')
    if l_bound_type not in ['dirichlet', 'neumann', 'robin']:
        raise Exception('Argument (boundary_type) must be either \'dirichlet\', \'neumann\', or \'robin\'.')
    input_checks.test_string(r_bound_type, 'r_bound_type')
    if r_bound_type not in ['dirichlet', 'neumann', 'robin']:
        raise Exception('Argument (boundary_type) must be either \'dirichlet\', \'neumann\', or \'robin\'.')
    input_checks.test_function(l_bound_func, 'l_bound_func')
    input_checks.test_function(r_bound_func, 'r_bound_func')
    input_checks.test_function(init_func, 'init_func')
    input_checks.test_float_int(D, 'D')
    input_checks.test_float_int(x_min, 'x_min')
    input_checks.test_float_int(x_max, 'x_max')
    input_checks.test_int(nx, 'nx')
    input_checks.test_float_int(t_min, 't_min')
    input_checks.test_float_int(t_max, 't_max')
    input_checks.test_int(nt, 'nt')
    if source_func != None:
        input_checks.test_function(source_func, 'source_func')
    if l_bound_args != None:
        input_checks.test_list_nparray(l_bound_args, 'l_bound_args')
    if l_bound_type == 'robin':
        if len(l_bound_args) != 2:
            raise Exception('Argument (l_bound_args) must contain two values.')
    if r_bound_args != None:
        input_checks.test_list_nparray(r_bound_args, 'r_bound_args')
    if r_bound_type == 'robin':
        if len(r_bound_args) != 2:
            raise Exception('Argument (r_bound_args) must contain two values.')
    if init_args != None:
        input_checks.test_list_nparray(init_args, 'init_args')
    if source_args != None:
        input_checks.test_list_nparray(source_args, 'source_args')
    if use_sparse not in [True, False]:
        raise Exception('Argument (use_sparse) must be a boolean True or False.')

    # MEETING STABILITY CONDITION
    dx = (x_max - x_min) / nx
    dt = (t_max - t_min) / nt
    C = (dt * D) / (dx ** 2)
    if method == 'explicit_euler' or method == 'lines':
        dt = 0.49*(dx**2)/D
        nt = math.ceil((t_max - t_min) / dt)
        C = (dt * D) / (dx ** 2)

    # ADJUST SOURCE TERM
    if source_func == None:
        def source_func(x, t, u, args):
            return np.zeros(np.size(x))

    # CONSTRUCT GRIDS
    x_arr = np.linspace(x_min, x_max, nx+1)
    t_arr = np.linspace(t_min, t_max, nt+1)

    # DEFINE x_int AND INITIALISE u_t
    if l_bound_type == 'dirichlet':
        xs = 1
    else:
        xs = 0
    if r_bound_type == 'dirichlet':
        x_int = x_arr[xs:-1]
    else:
        x_int = x_arr[xs:]

    u_t = init_func(x_int, 0, 0, init_args)
    size = len(u_t)

    # CREATE MATRICES
    if use_sparse == True:
        I_mat = sp.sparse.identity(size, format='csr')
        A_mat = sparse_A(size, -2, 1)
    elif use_sparse == False:
        I_mat = np.identity(size)
        A_mat = full_A(size, -2, 1)

    # DEFINE RHS VECTOR AND MODIFY SPARSE MATRICIES ACCORDING TO BOUNDARY CONDITIONS
    if l_bound_type != 'dirichlet':
        A_mat[0, 1] *= 2
    if l_bound_type == 'robin':
        A_mat[0, 0] *= 1 + l_bound_args[1] * dx

    if r_bound_type != 'dirichlet':
        A_mat[-1, -2] *= 2
    if r_bound_type == 'robin':
        A_mat[-1, -1] *= 1 + r_bound_args[1] * dx

    def make_b(t):
        b = np.zeros(size)
        if l_bound_type == 'dirichlet':
            b[0] = l_bound_func(x_min, t, u_t, l_bound_args)
        elif l_bound_type == 'neumann':
            b[0] = l_bound_func(x_min, t, u_t, l_bound_args) * -2 * dx
        elif l_bound_type == 'robin':
            b[0] = l_bound_func(x_min, t, u_t[0], [l_bound_args[0], 0]) * -2 * dx

        if r_bound_type == 'dirichlet':
            b[-1] = r_bound_func(x_max, t, u_t, r_bound_args)
        elif r_bound_type == 'neumann':
            b[-1] = r_bound_func(x_max, t, u_t, r_bound_args) * 2 * dx
        elif r_bound_type == 'robin':
            b[-1] = r_bound_func(x_max, t, u_t[-1], [r_bound_args[0], 0]) * 2 * dx

        return b

    # SOLVE
    if use_sparse == True:
        solver = sp.sparse.linalg.spsolve
    elif use_sparse == False:
        solver = np.linalg.solve

    if method == 'lines':
        def PDE(u, t, args):
            A_mat, b = args
            return (C/dt) * (A_mat@u + b + (dx**2) * (source_func(x_int, t, u, source_args)))
        b = make_b(t_max)
        sol = ode_solver.solve_to(PDE, u_t, t_min, t_max, dt, args=[A_mat, b])
        u_t = sol[:,-1][:-1]

    if method == 'explicit_euler':
        for j in range(0, nt):
            b = make_b(t_arr[j])
            u_t = solver(I_mat, u_t + C*(A_mat@u_t + b + (dx**2) * (source_func(x_int, t_arr[j], u_t, source_args))))

    if method == 'implicit_euler':
        for j in range(0, nt):
            b = make_b(t_arr[j])
            u_t = solver(I_mat - (C*A_mat), u_t + C * (b + (dx**2) * (source_func(x_int, t_arr[j], u_t, source_args))))

    if method == 'crank_nicolson':
        for j in range(0, nt):
            b = make_b(t_arr[j])
            u_t = solver(I_mat - ((C/2)*A_mat), (I_mat + ((C/2)*A_mat))@u_t + C * (b + (dx**2) * (source_func(x_int, t_arr[j], u_t, source_args))))

    # MODIFY u_t ACCORDING TO BOUNDARY CONDITIONS
    if l_bound_type == 'dirichlet':
        u_t = np.concatenate((np.array([l_bound_func(x_min, 0, 0, l_bound_args)]), u_t))
    if r_bound_type == 'dirichlet':
        u_t = np.concatenate((u_t, np.array([r_bound_func(x_max, 0, 0, r_bound_args)])))

    return np.vstack([u_t, x_arr])


import collections
import pandas as pd
import re
import pdftotext
import requests

from static.constants import FILE_PATH

class DrugsFDA:
    def __init__(self, fda_app_num):
        self.FDA_Application_Number = None
        drugs_fda_df = pd.read_csv(FILE_PATH.DRUGSFDA_ORIGIN_DOCS, encoding="ISO-8859-1", delimiter='\t', dtype=str)
        drugs_fda_application_df = pd.read_csv(FILE_PATH.DRUGSFDA_ORIGIN_APPLICATION, encoding="ISO-8859-1",
                                               delimiter='\t',
                                               dtype=str)
        drugs_fda_df = pd.merge(drugs_fda_df, drugs_fda_application_df, how='left', on='ApplNo')
        drugs_fda_df['FullApplNo'] = drugs_fda_df.ApplType + drugs_fda_df.ApplNo
        drugs_fda_df.ApplicationDocsDate = pd.to_datetime(drugs_fda_df.ApplicationDocsDate).dt.date
        drug_df = drugs_fda_df[(drugs_fda_df.FullApplNo == fda_app_num) & (drugs_fda_df.ApplicationDocsTypeID == '2')] \
            .sort_values(['ApplicationDocsDate'], ascending=False)
        if drug_df.shape[0] > 0:
            drug = drug_df.iloc[0]
            self.FDA_Application_Number = fda_app_num
            self.Docs_Date = drug.ApplicationDocsDate
            self.Docs_Url = drug.ApplicationDocsURL
            self.Pdf = DrugsFDAPdf(get_pdf(self.Docs_Url))

def get_pdf(url):
    try:
        r = requests.get(url)
        with open('templbl.pdf', 'wb') as fd:
            fd.write(r.content)
        f = open('templbl.pdf', 'rb')
        pdf = pdftotext.PDF(f)
        return '\n\n'.join(pdf)
    except:
        return ''

def build_dict_helper(title_idx_list, content_list):
    content_dict = {}
    for i, title_idx in enumerate(title_idx_list):
        if i == len(title_idx_list) - 1:
            content_dict[title_idx[1]] = content_list[title_idx[0] + 1:]
        else:
            content_dict[title_idx[1]] = content_list[title_idx[0] + 1:title_idx_list[i + 1][0]]
    return content_dict

def group_sentence_helper(sentence_list):
    end_list = [(i, item) for i, item in enumerate(sentence_list)
                if len(item.split(' ')) <= 5 or item[-1] == '.' or item[-1] == ':']
    group_sentence = []
    if len(end_list) > 0:
        for i, end in enumerate(end_list):
            if i == 0:
                group_sentence.append(' '.join(sentence_list[:end[0] + 1]))
            else:
                group_sentence.append(' '.join(sentence_list[end_list[i - 1][0] + 1:end[0] + 1]))
        if end_list[-1] != len(sentence_list) - 1:
            group_sentence.append(' '.join(sentence_list[end_list[-1][0] + 1:]))
    return group_sentence

def print_dict(dict):
    ordered_dict = collections.OrderedDict(sorted(dict.items()))
    dict_content_list = []
    for dict_key, dict_value in ordered_dict.items():
        dict_content_list.append(dict_key)
        dict_content_list = dict_content_list + group_sentence_helper(dict_value)
    return dict_content_list

class DrugsFDAPdf:
    filter_pattern = ['^Reference ID:', '^[0-9]+$', '^Page', '^$']
    title_pattern = ['^FULL PRESCRIBING INFORMATION$',
                     '^\d+\s+INDICATIONS AND USAGE$',
                     '^\d+\s+DOSAGE AND ADMINISTRATION$',
                     '^\d+\s+DOSAGE FORMS AND STRENGTHS$',
                     '^\d+\s+CONTRAINDICATIONS$',
                     '^\d+\s+WARNINGS AND PRECAUTIONS$',
                     '^\d+\s+ADVERSE REACTIONS$',
                     '^\d+\s+DRUG INTERACTIONS$',
                     '^\d+\s+USE IN SPECIFIC POPULATIONS$',
                     '^\d+\s+DRUG ABUSE AND DEPENDENCE$',
                     '^\d+\s+OVERDOSAGE$',
                     '^\d+\s+DESCRIPTION$',
                     '^\d+\s+CLINICAL PHARMACOLOGY$',
                     '^\d+\s+NONCLINICAL TOXICOLOGY$',
                     '^\d+\s+CLINICAL STUDIES$',
                     '^\d+\s+HOW SUPPLIED/STORAGE AND HANDLING$',
                     '^\d+\s+PATIENT COUNSELING INFORMATION$',
                     '^Medication Guide$',
                     '^MEDICATION GUIDE$']
    admef_pattern = ['^absorption$',
                     '^distribution$',
                     '^metabolism$',
                     '^excretion$',
                     '^elimination$',
                     '^effect of food$',
                     '^effects of food$',
                     '^food effect$',
                     '^food effects$',
                     'table',
                     'figure',
                     'population',
                     'impairment',
                     'geriatric',
                     'pediatric',
                     'gender',
                     'race',
                     'ethnicity',
                     'interaction',
                     'interactions',
                     'racial',
                     'age',
                     'populations',
                     'weight',
                     'half-life']
    adme_pattern = ['absorption', 'distribution', 'metabolism', 'excretion', 'elimination']

    def build_pdf_content_dict(self, pdf_content):
        pdf_content_dict = {}

        pdf_content_list = pdf_content.split('\n')
        pdf_content_list = list(map(lambda s: re.sub(r'\s+', ' ', s).strip(), pdf_content_list))
        pdf_content_list = list(filter(lambda s: not re.search(r'|'.join(self.filter_pattern), s), pdf_content_list))

        title_idx_list = [(i, item) for i, item in enumerate(pdf_content_list) if
                          re.search(r'|'.join(self.title_pattern), item)]
        # level 1 title
        if pdf_content_list and len(title_idx_list) > 0:
            pdf_content_dict = build_dict_helper(title_idx_list, pdf_content_list)
            # level 2 title
            for key, value in pdf_content_dict.items():
                if re.search(r'^\d+', key):
                    section_number = re.search(r'^\d+', key).group()
                    sub_title_idx_list = [(i, item) for i, item in enumerate(value) if
                                          re.search(rf'^{section_number}\.\d+\s+.+$', item)]
                    if len(sub_title_idx_list) > 0:
                        pdf_content_dict[key] = build_dict_helper(sub_title_idx_list, value)
                        pharmacokinetics = list(
                            filter(lambda t: 'pharmacokinetics' in t[1].lower(), sub_title_idx_list))
                        # level 3 title: ADMEF
                        if len(pharmacokinetics) > 0:
                            admef_content_list = pdf_content_dict[key][pharmacokinetics[0][1]]
                            admef_idx_list = [(i, item) for i, item in enumerate(admef_content_list)
                                              if len(item.split(' ')) <= 10
                                              and ',' not in item
                                              and item[-1] != '.'
                                              and re.search(r'|'.join(self.admef_pattern), re.sub(r'[^a-z ]+', '', item.lower().strip()))]
                            if len(list(filter(lambda item: re.search(r'|'.join(self.adme_pattern), item[1].lower()),
                                               admef_idx_list))) > 0:
                                pdf_content_dict[key][pharmacokinetics[0][1]] = build_dict_helper(admef_idx_list, admef_content_list)
        return pdf_content_dict

    def dict_value_helper(self, path):
        current_dict = self.pdf_content_dict
        found = False
        value = ''
        for i, key in enumerate(path):
            if type(current_dict) is dict:
                for dict_key, dict_value in current_dict.items():
                    if re.search(rf'{key.lower()}', dict_key.lower()):
                        current_dict = dict_value
                        if i == len(path) - 1:
                            found = True
                        break
            else:
                return value
        if found:
            if type(current_dict) is list:
                value = '\n\n'.join(group_sentence_helper(current_dict))
            else:
                value = '\n\n'.join(print_dict(current_dict))
        return value

    def pharmacokinetics_non_admef(self):
        pharmacokinetics_list = self.Pharmacokinetics.split('\n\n')
        pharmacokinetics_list = [x for x in pharmacokinetics_list if x not in self.Absorption.split('\n\n')]
        pharmacokinetics_list = [x for x in pharmacokinetics_list if x not in self.Distribution.split('\n\n')]
        pharmacokinetics_list = [x for x in pharmacokinetics_list if x not in self.Metabolism.split('\n\n')]
        pharmacokinetics_list = [x for x in pharmacokinetics_list if x not in self.Excretion.split('\n\n')]
        pharmacokinetics_list = [x for x in pharmacokinetics_list if x not in self.Food_Effect.split('\n\n')]
        pharmacokinetics_list = [x for x in pharmacokinetics_list if not re.search(r'|'.join(self.adme_pattern + ['^effect of food', '^effects of food', '^food effect', '^food effects']), x.lower())]
        return '\n\n'.join(pharmacokinetics_list)

    def __init__(self, pdf_content):
        self.pdf_content_dict = self.build_pdf_content_dict(pdf_content)

        self.Boxed_Warning = self.dict_value_helper(['FULL PRESCRIBING INFORMATION'])
        self.Indication = self.dict_value_helper(['INDICATIONS AND USAGE'])
        self.Dosage_Administration = self.dict_value_helper(['DOSAGE AND ADMINISTRATION'])
        self.Pregnancy = self.dict_value_helper(['USE IN SPECIFIC POPULATIONS', 'Pregnancy'])
        self.Lactation = self.dict_value_helper(['USE IN SPECIFIC POPULATIONS', 'Lactation'])
        self.Nursing_Mother = self.dict_value_helper(['USE IN SPECIFIC POPULATIONS', 'Nursing Mother'])
        self.Females_Males_of_Reproductive_Potential = self.dict_value_helper(['USE IN SPECIFIC POPULATIONS',
                                                                               'Females and Males of Reproductive Potential|Females & Males of Reproductive Potential'])
        self.Mechanism_of_Action = self.dict_value_helper(['CLINICAL PHARMACOLOGY', 'Mechanism of Action'])
        self.Pharmacodynamics = self.dict_value_helper(['CLINICAL PHARMACOLOGY', 'Pharmacodynamics'])
        self.Pharmacokinetics = self.dict_value_helper(['CLINICAL PHARMACOLOGY', 'Pharmacokinetics'])
        self.Absorption = self.dict_value_helper(['CLINICAL PHARMACOLOGY', 'Pharmacokinetics', 'Absorption'])
        self.Distribution = self.dict_value_helper(['CLINICAL PHARMACOLOGY', 'Pharmacokinetics', 'Distribution'])
        self.Metabolism = self.dict_value_helper(['CLINICAL PHARMACOLOGY', 'Pharmacokinetics', 'Metabolism'])
        # self.Elimination = self.dict_value_helper(['CLINICAL PHARMACOLOGY', 'Pharmacokinetics', 'Elimination'])
        self.Excretion = self.dict_value_helper(['CLINICAL PHARMACOLOGY', 'Pharmacokinetics', 'Excretion'])
        self.Excretion += self.dict_value_helper(['CLINICAL PHARMACOLOGY', 'Pharmacokinetics', 'Elimination'])
        self.Food_Effect_1 = self.dict_value_helper(
            ['CLINICAL PHARMACOLOGY', 'Pharmacokinetics', 'food effect|food effects|effect of food|effects of food'])
        self.Food_Effect_2 = ''
        self.Cytotoxic = self.dict_value_helper(['NONCLINICAL TOXICOLOGY', 'Carcinogenesis'])
        self.How_Supplied = self.dict_value_helper(['HOW SUPPLIED'])
        self.Information_for_Patients = self.dict_value_helper(['PATIENT COUNSELING INFORMATION'])

        if self.Pharmacokinetics:
            paragraph_list = self.Pharmacokinetics.split('\n\n')
            for paragraph in paragraph_list:
                if re.search(r'^absorption\s*(:|—|-|–)', paragraph.lower()) and not self.Absorption:
                    title = re.split(':|—|-|–', paragraph)[0]
                    self.Absorption = paragraph[len(title)+1:].strip()
                if re.search(r'^distribution\s*(:|—|-|–)', paragraph.lower()) and not self.Distribution:
                    title = re.split(':|—|-|–', paragraph)[0]
                    self.Distribution = paragraph[len(title)+1:].strip()
                if re.search(r'^metabolism\s*(:|—|-|–)', paragraph.lower()) and not self.Metabolism:
                    title = re.split(':|—|-|–', paragraph)[0]
                    self.Metabolism = paragraph[len(title)+1:].strip()
                if re.search(r'^(excretion|elimination)\s*(:|—|-|–)', paragraph.lower()) and not self.Excretion:
                    title = re.split(':|—|-|–', paragraph)[0]
                    self.Excretion = paragraph[len(title)+1:].strip()
                if re.search(r'^(food effect|food effects|effect of food|effects of food)\s*(:|—|-|–)', paragraph.lower()) and not self.Food_Effect_1:
                    title = re.split(':|—|-|–', paragraph)[0]
                    self.Food_Effect_1 = paragraph[len(title)+1:].strip()

        if self.Absorption:
            paragraph_list = self.Absorption.split('\n\n')
            for paragraph in paragraph_list:
                if re.search(r'^(food effect|food effects|effect of food|effects of food)\s*(:|—|-|–)', paragraph.lower()):
                    paragraph_list.remove(paragraph)
                    title = re.split(':|—|-|–', paragraph)[0]
                    self.Food_Effect_2 = paragraph[len(title)+1:].strip()
                    self.Food_Effect_1 = ''
            self.Absorption = '\n\n'.join(paragraph_list)

        self.Food_Effect = self.Food_Effect_1 if self.Food_Effect_1 else self.Food_Effect_2

        self.Other = self.pharmacokinetics_non_admef()

        tube_keywords = ['ng tube', '(ng) tube', 'nasogastric tube', 'gastrostomy tube', 'jejunal tube', 'feeding tube']
        tube_list = []
        for paragraph in self.Dosage_Administration.split('\n\n'):
            if re.search(r'|'.join(tube_keywords), paragraph.lower()):
                tube_list.append(paragraph)
        self.Feeding_Tube = '\n\n'.join(tube_list)


import time
import json
import os
import requests

class pokemon:
    def __init__(self, number, energy):
        self.number = number
        self.energy = 50
        self.hp = hp_from_id(number)
        self.attack = attack_from_id(number)
        self.defense = defense_from_id(number)
        self.special_attack = special_attack_from_id(number)
        self.special_defense = special_defense_from_id(number)
        self.speed = speed_from_id(number)

class player:
    def __init__(self, name):
        self.beans = 0
        self.name = name
        self.pokemon = []

        self.last_harvest = None

    def harvest(self):
        if self.last_harvest == None:
            harvested_beans = 40
        else:
            seconds_since_last_harvest = time.time() - self.last_harvest
            harvested_beans = int(seconds_since_last_harvest / 180)

        if harvested_beans > 80:
            harvested_beans = 80

        if harvested_beans > 0:
            self.beans += harvested_beans
            self.last_harvest = time.time()

            self.save_to_file()

            print(f"You've harvested {harvested_beans} beans!")
        else:
            print("Oops, the tree hasn't produced any beans yet! Wait a little longer!")

    def print_info(self):
        print(f"You currently have {self.beans} beans!\n \n")

    def print_salutations(self):
        return(f"Hey, {self.name}! What would you like to do? Check your beans, harvest or (add more later)?\n")

    def save_to_file(self):
        # make dict of attributes
        player_data = {
            "name": self.name,
            "beans": self.beans,
            "last_harvest": self.last_harvest
        }

        file_name = f"{self.name}.json"
        file = open(file_name, "w")  # open file
        json.dump(player_data, file)  # write player data to file
        file.close()  # close file

    def load_from_file(self):
        file_name = f"{self.name}.json"
        if os.path.exists(file_name):  #check if player file exists
            player_data = json.load(open(file_name))  # open json file

            # set attributes
            self.name = player_data["name"]
            self.beans = player_data["beans"]
            self.last_harvest = player_data["last_harvest"]
        else:
            print("no player file found")

r = requests.get("https://pokeapi.co/api/v2/pokemon/6")
data = r.json()
stats = {x["stat"]["name"]: x[base_stat]for x in data["stats"]}

p1 = player(input("Hey!Whats your name?\n"))
p1.load_from_file()

x = 1
while x == 1:
    player_choice = input(p1.print_salutations())
    if player_choice == "beans":
        p1.print_info()
        input("Press Enter to go back to the main menu.")
    elif player_choice == "harvest":
        p1.harvest()
        input("Press Enter to go back to the main menu.")


"""Deleted_columns_in_city

Revision ID: 7b8f459a61b0
Revises: e280451841bb
Create Date: 2021-11-11 15:08:06.260280

"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql

# revision identifiers, used by Alembic.
revision = '7b8f459a61b0'
down_revision = 'e280451841bb'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('hotel', 'location_link')
    op.drop_column('hotel', 'breakfast_included')
    op.drop_column('hotel', 'transport_from_airport')
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('hotel', sa.Column('transport_from_airport', mysql.ENUM('not_included', 'bus', 'car'), nullable=True))
    op.add_column('hotel', sa.Column('breakfast_included', mysql.ENUM('not_included', 'included', 'paid', 'all_inclusive'), nullable=True))
    op.add_column('hotel', sa.Column('location_link', mysql.VARCHAR(length=500), nullable=True))
    # ### end Alembic commands ###


import sys,os

pathRacine=os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)),'..'))
if os.path.dirname(pathRacine) not in sys.path :
   sys.path.insert(0,pathRacine)

from .dataBase import Base

if __name__ == "__main__":
      from argparse import ArgumentParser
      p=ArgumentParser()
      p.add_argument('-p',dest='partiel',action="store_true", default=False,help='export de machine, groupe, ratio Maille et Perf uniquement')
      p.add_argument('-d',dest='database',default="../myMesh.db",help='nom de la database')
      args = p.parse_args()

      maBase=Base(args.database)
      maBase.initialise()
      maBase.exportToCSV(args.partiel)
      maBase.close()


# ! ! !
# ESSA points no working
# ! ! !

import re
import os
import sys
import Cython
import ctypes
from ctypes import cdll, CDLL

import random
import datetime
import asyncio
import json
from difflib import SequenceMatcher

import nextcord as discord

from nextcord import ActivityType, guild
from nextcord import user
from nextcord import member
from nextcord import Message
from nextcord import channel

from nextcord.ext import commands, tasks
from nextcord.ext.commands import has_permissions

from nextcord.utils import get

# mydll = cdll.LoadLibrary('c:\\Users\\jaroz\\documents\\fap box\\~Python\\~DIscord-Trader\\clear.dll')

with open('c:\\Users\\jaroz\\Documents\\fap box\\~Python\\~DIscord-Trader\\settings.json', 'r') as d:

    settings = json.loads(d.read())

    powerUser = settings['powerUser']

    pref = settings['prefix']

    d.close()

class fg:
    black = "\u001b[30m"
    red = "\u001b[31m"
    green = "\u001b[32m"
    yellow = "\u001b[33m"
    blue = "\u001b[34m"
    magenta = "\u001b[35m"
    cyan = "\u001b[36m"
    white = "\u001b[37m"

    def rgb(r, g, b):
        return f"\u001b[38;2;{r};{g};{b}m"

class bg:
    black = "\u001b[40m"
    red = "\u001b[41m"
    green = "\u001b[42m"
    yellow = "\u001b[43m"
    blue = "\u001b[44m"
    magenta = "\u001b[45m"
    cyan = "\u001b[46m"
    white = "\u001b[47m"

    def rgb(r, g, b):
        return f"\u001b[48;2;{r};{g};{b}m"

def consoleStatus(status : str):

    # mydll.clearScreen()
    os.system("cls")
    print('\n'*5)
    print(f'{fg.white}Status: ')
    print(f'{fg.white}Prefix: {fg.cyan}{pref}{fg.white}')

client = commands.Bot(command_prefix=pref)

locTime = datetime.datetime.now()

usersStatsPath = 'c:\\Users\\jaroz\\Documents\\fap box\\~Python\\~DIscord-Trader\\usersStats.json'

serversNames = []
serversIDs = []

# for guild in client.guilds:

#     serversList.append(guild)

def add(a : int, b : int):
    return a + b;

def subtrack(a : int, b : int):
    return a - b;

def percetage(a : int, b : int):
    return a % b;

def multiply(a : int, b : int):
    return a * b;

def similar(a, b):
    return SequenceMatcher(None, a, b).ratio();

def Log(logg : str):

    with open('logs.txt', 'a', 'utf8') as logfile:
        logfile.write(f"{locTime}\n{logg}\n \n")

@tasks.Loop(seconds=60)
async def loop():

    with open(usersStatsPath, 'r') as j:

        usersStats = json.loads(j.read())

        for member in discord.guilds.members(limit=None):

            if not f'user.{member.id}' in usersStats:
                    usersStats[f'user.{member.id}'] = {}
                    usersStats[f'user.{member.id}']['essa'] = 0
                    usersStats[f'user.{member.id}']['warns']

        j.close()

    with open(usersStatsPath, 'w') as j:

        j.write(json.dumps(usersStats), indent=4)
        j.close()

@client.event
async def on_ready():

    # serversList.append(discord.client.guilds)
    for guild in client.guilds:

        serversNames.append(guild.name)
        serversIDs.append(guild.id)

    await client.change_presence(activity=discord.Game(name=settings['activity']))

    consoleStatus(f'{fg.green}Online')

    await client.get_channel(settings['joinLeaveChannel']).send(f"Hello")

@client.event
async def on_command_error(ctx, error : Exception):

    embed=discord.Embed(title=error, description=f'{error}', color=0x00fbff)
    embed.set_thumbnail(url=ctx.author.avatar_url)
    embed.set_footer(text=datetime.datetime.now())

    await ctx.send(embed=embed)

    print(f"""
            \n
            \n
            exception: {fg.RED}
            {error}
            {fg.white}
            \n
            \n
            """)

@client.event
async def on_member_join(member : discord.member):

    await client.get_channel(settings['joinLeaveChannel']).send(f"Hello <@{member.id}>")

    Log(f'@<{member}> przybył na serwer')

@client.event
async def on_member_remove(ctx, member : discord.member):

    await client.get_channell(settings['joinLeaveChannel']).send(f"{member} spierdolił z serwera")

@client.event
async def on_message_delete(ctx, message : discord.message):

    if message.author.id != 921832817854398505:
        Log(f"{message.author} usunoł wiadomość: \n \n '{message.content}'\n")

@client.event
async def on_message(message : discord.message):

    if similar('Jebac Jara', message.content) > 0.5:
        await message.channel.send('Nie jara, ciebie jebać')

    if similar('Trader', message.content) > 0.7 and message.author.id != 921832817854398505:
        await message.channel.send('Trader to bug')

    if similar('mati', message.content) > 0.7 and message.author.id:
        await message.channel.send("https://media.discordapp.net/attachments/791411982380695592/927649861321519104/Screenshots_2022-01-01-23-31-08.png")

    if similar('zelson', message.content) > 0.7 and message.author.id != 921832817854398505:
        await message.channel.send('E')
        await asyncio.sleep(1)
        await message.channel.send('XD')
        await asyncio.sleep(1)
        await message.channel.send('Kto pytał')

    await client.process_commands(message)

@has_permissions(ban_members=True)
@client.command()
async def ban(ctx, member : discord.member, reason=None):
    await member.ban()

    if reason is None:
        reason = 'No provided'

    Log(f"user {member.id} has banned from {ctx.author.id} ")

    embed=discord.Embed(title="Banned", description="user", color=0x00fbff)
    embed.add_field(name="reason", value=f"{reason}", inline=False)
    embed.set_thumbnail(url=member.avatar_url)

    await ctx.send(embed=embed)

@has_permissions(kick_members=True)
@client.command()
async def kick(ctx, member : discord.Member, reason=None):
    await member.kick()

    if reason is None:
        reason = 'No provided'

    Log(f"user {member.id} has kicked from {ctx.author.id} ")

    embed=discord.Embed(title="Kicked", description="user", color=0x00fbff)
    embed.add_field(name="reason", value=f"{reason}", inline=False)
    await ctx.send(embed=embed)

@has_permissions(mute_members=True)
@client.command()
async def mute(ctx, member : discord.Member, reason=None):

    muteRole = discord.utils.get(ctx.message.guild.roles, id=settings['muteRole'])

    if reason is None:
        reason = 'No provided'

    await client.add_roles(member, muteRole)

@client.command()
async def log(ctx, loggg : str):

    if len(loggg) > 69:
        await ctx.channel.send()

    try:
        Log(loggg)

        embed = discord.Embed(title="Udana akcja", description="Log pomyślnie wprowadzony do dziennika", color=0x00fbff)
        await ctx.channel.send(embed)
    except:
        embed = discord.Embed(title="błąd", description="Maksymalna długość zmiennej string loggg to 69 znaków", color=0x00fbff)
        await ctx.channel.send(embed)

@client.command()
async def end(ctx):

    if ctx.author.id == powerUser:

        consoleStatus(f'{fg.red}Offline')
        await ctx.channel.send("baj")

        sys.exit(0)

    else:

        await ctx.channel.send('Missing permisions !')

@client.command()
async def rename(ctx, name : str):

    if ctx.author.id == powerUser:

        await client.user.edit(username=name)

    else:

        ctx.channel.send('Missing permisions !')

@client.command()
async def retag(ctx):

    botTag = client.get_user(921832817854398505).discriminator

    await client.user.edit(username=botTag)
    await client.user.edit(username='Trader')

@client.command()
async def restatus(ctx, typeActicity : discord.activity, status : str):

    if ctx.author.id == powerUser:

        await client.change_presence(activity=ActivityType(name=status))

    else:

        await ctx.channel.send('Missing permisions !')

@client.command()
async def prefix(ctx):

    await ctx.channel.send(f"My prefix is {pref}")

@client.command()
async def av(ctx, *, member : discord.Member = None):

    if member is None:

        member == ctx.author

    avatar = member.avatar

    await ctx.channel.send(avatar)

@client.command()
async def serversL(ctx):

    for i in serversNames:

        await ctx.send(i)

@client.command()
async def source(ctx):

    src = open('c:\\Users\\jaroz\\Documents\\fap box\\~Python\\~DIscord-Trader\\bot.py', 'r').read()

    await ctx.channel.send(f"```py\n{src}\n```")

@client.command()
async def plus(ctx, member : discord.Member, amount : int):

    j = open('c:\\Users\\jaroz\\Documents\\fap box\\~Python\\~DIscord-Trader\\essaPoints.json', 'a')

    essa = json.loads(j.read())

    for member in ctx.guild.members(limit=None):

        if not member.id in essa:
            essa.append({f'{member.id}': 0})

        if essa[f"{ctx.author.id}"] < amount and ctx.author.id != powerUser:

            embed=discord.Embed(title=f'error - {datetime.datetime.now()}', description='Twoja essa jest za mała', color=0x00fbff)
            embed.set_thumbnail(url=ctx.author.avatar_url)
            await ctx.send(embed=embed)

        else:

            fromUser = int(essa[f'"{ctx.author.id}"'])
            toUser = int(essa[f'"{member.id}"'])

            fromUser -= amount
            toUser += amount

    j.close()

@client.command()
async def send(ctx, msg : str):

    await ctx.message.delete()
    await ctx.channel.send(f"{msg}")

    Log(f"\n <@{ctx.author.id}> używając {settings['prefix']} wyslal wiadomość {msg}")

@client.command()
async def similarity(ctx, a : str, b : str):

    await ctx.channel.send(f'{float(similarity(a, b))}%')

@client.command()
async def anal(ctx, member : discord.Member):
    await ctx.channel.send(f"*<@{ctx.author.id}> zaczol zapinać <@{member.id}> prosto w jego dupalce :hot_face:*")
    await asyncio.sleep(5)
    await ctx.channel.send(f"*<@{ctx.author.id}> spuscił się <@{member.id}> prosto w głąb jego mokrego odbytu :weary:*")

@client.command()
async def roll(ctx, min : int, max : int):
    if max > 6900 or max < -6900:
        max = 6900

    if min < -6900 or min > 6900:
        min = -6900

    await ctx.channel.send(random.randint(min, max))

@client.command()
async def choice(ctx, choice0 : str, choice1 : str, choice2=None, choice3=None, choice4=None):
    choices = []

    if choice0 != None:
        choices.append(choice0)

    if choice1 != None:
        choices.append(choice1)

    if choice2 != None:
        choices.append(choice2)

    if choice3 != None:
        choices.append(choice3)

    if choice4 != None:
        choices.append(choice4)

    await ctx.channel.send(random.choice(choices))

@client.command()
async def meme(ctx):

    memes = [

        'https://cdn.discordapp.com/attachments/753683283182747759/939476919739621407/FB_IMG_1643793609067.jpg', # 0
        'https://cdn.discordapp.com/attachments/753683283182747759/939476920108728330/FB_IMG_1643793642945.jpg', # 1
        'https://cdn.discordapp.com/attachments/753683283182747759/939476920360370206/FB_IMG_1643793770997.jpg', # 2
        'https://cdn.discordapp.com/attachments/753683283182747759/939478970410356766/Untitled18_20220204165110.png', # 3
        'x', # 4
        'x', # 5
        'x', # 6
        'x', # 7
        'x', # 8
        'x', # 9
        'x', # 10
        'x', # 11
        'x', # 12
        'x', # 13
        'x', # 14
        'x', # 15
        'x', # 16
        'x', # 17
        'x', # 18
        'x' # 19
    ]

    memeChoice = random.choice(memes)

    while memeChoice == 'x':

        memeChoice = random.choice(memes)

    await ctx.channel.send(random.choice(memes))
    # await client.say(ctx.message.channel, f"{random.choice(memes)}")

@client.command()
async def film(ctx):

    films = [

        'https://cdn.discordapp.com/attachments/296056831514509312/773404299609767946/video0-58.mp4',
        'https://cdn.discordapp.com/attachments/296056831514509312/773404299609767946/video0-58.mp4',
        'https://cdn.discordapp.com/attachments/773151424724729886/806854489742245888/video0_2.mp4',
        'https://cdn.discordapp.com/attachments/773151424724729886/808636325934792744/7b2c1bf81d9016431aa46ea9a5c3a130.mp4',
        'https://cdn.discordapp.com/attachments/796881224718221323/808303823974039602/redditsave.com-weird_flex_but_ok-edzlanht70e61.mp4',
        'https://cdn.discordapp.com/attachments/796881224718221323/808318518835413003/Teams.mp4',
        'https://cdn.discordapp.com/attachments/792792798377279509/808089281435926548/video0.mp4',
        'https://cdn.discordapp.com/attachments/621057340963160115/809166647108960257/video-1612904500.mp4',
        'https://cdn.discordapp.com/attachments/755771736561287209/809730636801376256/video0-67.mp4',
        'https://cdn.discordapp.com/attachments/755771736561287209/809731909801476116/video0_1_2.mp4',
        'https://cdn.discordapp.com/attachments/773151424724729886/809765492671774760/video-1613040259.mp4',
        'https://cdn.discordapp.com/attachments/773151424724729886/810244464396206090/Top_10_niebezpieczne_gangi-2.mp4',
        'https://cdn.discordapp.com/attachments/773151424724729886/811210304452165672/xD.mp4',
        'https://cdn.discordapp.com/attachments/773151424724729886/812319349166833664/video0_6.mp4',
        'https://cdn.discordapp.com/attachments/773151424724729886/812628207530278922/video0-15_1.mp4',
        'https://cdn.discordapp.com/attachments/773151424724729886/812825229746765844/2e0bb6c0-a98d-4f22-83c5-abba8a93000c.mp4',
        'https://cdn.discordapp.com/attachments/773151424724729886/814106087665172540/true.mp4',
        'https://cdn.discordapp.com/attachments/676232229189451787/816653608975204382/video0.mp4',
        'https://cdn.discordapp.com/attachments/676232229189451787/816725693017751634/video0a.mp4'
    ]

    await ctx.channel.send(random.choice(films))

client.run(settings["token"])