import torch
import torch.backends.cudnn as cudnn
import numpy as np
import random
import scanpy as sc
import umap.umap_ as umap

from sklearn.preprocessing import LabelEncoder
from multiprocessing import Pool, cpu_count
from sklearn import metrics
from sklearn.cluster import DBSCAN
from sklearn.cluster import KMeans

def preprocessing(adata, batch_key = None):
    sc.pp.filter_genes(adata, min_counts=3)
    adata.layers["counts"] = adata.X.copy() # preserve counts
    sc.pp.normalize_total(adata, target_sum=1e4)
    sc.pp.log1p(adata)
    adata.raw = adata # freeze the state in `.raw`

    sc.pp.highly_variable_genes(
    adata,
    n_top_genes=1200,
    subset=True,
    layer="counts",
    flavor="seurat_v3",
    batch_key=batch_key
    )

def data_load(data_name, cov):

    covariate = torch.empty(0)
    batch = None

    if data_name == 'Two_moons':
        from sklearn.datasets import make_moons
        df, labels = make_moons(n_samples=4000, noise = 0.05, shuffle = False, random_state = 42)

    elif data_name == 'MNIST':
        from torchvision import datasets, transforms
        MNIST = datasets.MNIST('./mnist', train=True, download=True, transform=transforms.ToTensor())
        df = MNIST.data.view(60000,28*28).to(torch.float) / 255
        df = df.apply_(lambda x: 1 if x>= 0.5 else 0)
        labels = MNIST.targets

    elif data_name == 'cortex':
        import scvi
        adata = scvi.data.cortex()
        preprocessing(adata)
        df = adata.layers['counts']
        labels = adata.obs['cell_type']

    elif data_name == 'pbmc':
        import scvi
        adata = scvi.data.pbmc_dataset()
        preprocessing(adata)
        df = adata.layers['counts'].toarray()
        labels = adata.obs['str_labels']

    elif data_name == 'retina':
        import scvi
        from torch.nn import functional as F
        adata = scvi.data.retina()
        preprocessing(adata)
        df = adata.layers['counts']
        labels = adata.obs['labels']
        batch = LabelEncoder().fit_transform(adata.obs['batch'])
        if cov:
            covariate = F.one_hot(torch.Tensor(batch).to(torch.int64))

    elif data_name == 'heart_cell_atlas':
        import scvi
        from torch.nn import functional as F
        adata = scvi.data.heart_cell_atlas_subsampled()
        preprocessing(adata)
        df = adata.layers['counts'].toarray()
        labels = adata.obs['cell_type']
        if cov:
            donor = LabelEncoder().fit_transform(adata.obs['donor'])
            donor_one = F.one_hot(torch.Tensor(donor).to(torch.int64))
            cell_source = LabelEncoder().fit_transform(adata.obs['cell_source'])
            cell_source_one = F.one_hot(torch.Tensor(cell_source).to(torch.int64))
            batch = cell_source_one
            mito = torch.Tensor(adata.obs['percent_mito']).view(-1,1)
            ribo = torch.Tensor(adata.obs['percent_ribo']).view(-1,1)
            covariate = torch.concat((donor_one, cell_source_one, mito, ribo), dim=1)

    X = torch.Tensor(df)
    X_ = torch.concat((X,covariate), dim=1)
    labels_ = LabelEncoder().fit_transform(labels)

    return X, X_, labels, labels_, batch

def set_args(data_name):
    if data_name == 'Two_moons':
        dist = 'G'
        corr = cov = eval_ = False
        log_transform = False
        z_dim = 2

    elif data_name == 'MNIST':
        dist = 'B'
        corr = cov = eval_ = False
        log_transform = False
        z_dim = 10

    elif data_name == 'cortex':
        dist = 'NB'
        corr = cov = False
        log_transform = eval_ = True
        z_dim = 10

    elif data_name == 'pbmc':
        dist = 'NB'
        corr = cov = False
        log_transform = eval_ = True
        z_dim = 10

    elif data_name == 'retina':
        dist = 'NB'
        corr = cov = True
        log_transform = eval_ = True
        z_dim = 10

    elif data_name == 'heart_cell_atlas':
        dist = 'NB'
        corr = cov = True
        log_transform = eval_ = True
        z_dim = 10

    return dist, corr, cov, eval_, log_transform, z_dim

def seed(num):
    torch.manual_seed(num)
    torch.cuda.manual_seed(num)
    torch.cuda.manual_seed_all(num)
    np.random.seed(num)
    cudnn.benchmark = False
    cudnn.deterministic = True
    random.seed(num)

def make_simplices(graph, y=None):
    if y is not None:
      graph_ = umap.discrete_metric_simplicial_set_intersection(graph, y, far_dist = 5.0)
    else:
      graph_ = graph
    graph_ = graph.tocoo()
    graph_.sum_duplicates()

    # For smaller datasets we can use more epochs
    if graph_.shape[0] <= 10000:
        n_epochs = 500
    else:
        n_epochs = 200

    graph_.data[graph_.data < (graph_.data.max() / float(n_epochs))] = 0.0
    graph_.eliminate_zeros()

    head = graph_.row
    tail = graph_.col
    weight = graph_.data
    epochs_per_sample = umap.make_epochs_per_sample(graph_.data, n_epochs)

    return head, tail, weight, epochs_per_sample

def mp(function, object, num_cpu = cpu_count()):
    pool = Pool(num_cpu)
    return pool.map(function, object)

def make_tuple(tuple):
    i, head, tail, epochs_per_sample = np.array(tuple).T
    index = (head == i)
    return [tail[index],
    epochs_per_sample[index], epochs_per_sample[index]]

def cal_graph(data, random_state = 42):
    graph = umap.UMAP(random_state = random_state,
    min_dist = 0.0,
    n_neighbors = 30,
    transform_mode='graph',
    verbose=True).fit_transform(data)

    head, tail, weight, epochs_per_sample = make_simplices(graph, y=None)

    lst = [(i, head, tail, epochs_per_sample) for i in range(data.shape[0])]
    arr = np.array(mp(make_tuple, lst, num_cpu = int(cpu_count() / 2)), dtype=object)
    return arr

def check(tuple):
    data, labels_, i, j = np.array(tuple).T
    clustering = DBSCAN(min_samples = i+3, eps = 0.01 + j * 0.01).fit(data)
    ARI = metrics.adjusted_rand_score(labels_, clustering.labels_)
    NMI = metrics.normalized_mutual_info_score(labels_, clustering.labels_)
    return (ARI+NMI)/2

def DBSCAN_(data, labels_):
    lst = []
    for i in range(5):
        for j in range(200):
            lst.append((data, labels_, i,j))

    output = np.array(mp(check, lst, int(cpu_count()/4)))
    temp = lst[np.where(output==output.max())[0][-1]]
    i, j = temp[2], temp[3]
    clustering = DBSCAN(min_samples = i+3, eps = 0.01 + j * 0.01).fit(data)
    return clustering

def evaluation(data, labels_):
    dict_ = {}
    clustering = KMeans(random_state=42, n_clusters=len(set(labels_))).fit(data)
    ARI = metrics.adjusted_rand_score(labels_, clustering.labels_)
    NMI = metrics.normalized_mutual_info_score(labels_, clustering.labels_)
    dict_['KMeans'] = {'ARI': ARI, 'NMI': NMI, 'labels_': clustering.labels_}

    clustering = DBSCAN_(data, labels_)
    ARI = metrics.adjusted_rand_score(labels_, clustering.labels_)
    NMI = metrics.normalized_mutual_info_score(labels_, clustering.labels_)
    dict_['DBSCAN'] = {'ARI': ARI, 'NMI': NMI, 'labels_': clustering.labels_}
    return dict_


from typing import List

class Solution:
    def maxIncreaseKeepingSkyline(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        max1 = [max(x[i] for x in grid) for i in range(n)]
        max2 = [max(x) for x in grid]
        res = 0
        for i in range(m):
            for j in range(n):
                res += min(max2[i], max1[j]) - grid[i][j]
        return res


import os
from datetime import datetime, timedelta

import django
import pytz
import requests
from google.transit import gtfs_realtime_pb2

django.setup()

from busshaming.models import Feed, TripDate, RealtimeEntry, Stop

GTFS_API_KEY = os.environ.get('TRANSPORT_NSW_API_KEY')

def process_trip_update(trip_dates, stops, feed_tz, trip_update, threshold):
    trip = trip_update.trip
    key = (trip.trip_id, trip.start_date)
    # trip_date = trip_dates[key]
    if key not in trip_dates:
        print(trip)
        print("CAN'T FIND IN SCHEDULE: {}".format(key))
        return
    trip_date = trip_dates[key]
    for stop_update in trip_update.stop_time_update:
        if stop_update.arrival.time < threshold:
            stop = stops[stop_update.stop_id]
            arrival_time = datetime.fromtimestamp(stop_update.arrival.time, feed_tz)
            departure_time = datetime.fromtimestamp(stop_update.departure.time, feed_tz)
            # Upsert RealtimeEntry
            RealtimeEntry.objects.upsert(trip_date.trip_id, stop.id, stop_update.stop_sequence, arrival_time, stop_update.arrival.delay, departure_time, stop_update.departure.delay)

def fetch():
    feed = Feed.objects.get(slug='nsw-buses')
    feed_tz = pytz.timezone(feed.timezone)
    stops = {}
    for stop in Stop.objects.filter(feed=feed):
        stops[stop.gtfs_stop_id] = stop
    trip_dates = {}
    today = datetime.now(tz=feed_tz).date()
    yesterday = today - timedelta(days=1)
    for trip_date in TripDate.objects.filter(date__gte=yesterday, date__lte=today).prefetch_related('trip'):
        datestr = trip_date.date.strftime('%Y%m%d')
        trip_dates[(trip_date.trip.gtfs_trip_id, datestr)] = trip_date

    feed_message = gtfs_realtime_pb2.FeedMessage()
    headers = {'Authorization': 'apikey ' + GTFS_API_KEY}
    print('fetching...')
    response = requests.get(feed.realtime_feed_url, headers=headers)
    print('fetching complete.')
    if response.status_code == 200:
        feed_message.ParseFromString(response.content)
        now = datetime.now(tz=feed_tz)
        threshold = int((now + timedelta(minutes=3)).timestamp())
        for entity in feed_message.entity:
            if entity.HasField('trip_update'):
                process_trip_update(trip_dates, stops, feed_tz, entity.trip_update, threshold)
    else:
        print(response.status_code)
        print(response.content)

if __name__ == '__main__':
    fetch()


# https://adventofcode.com/2022/day/1
from pathlib import Path

ref = 0
part = "_2"

ext = "_ref" + str(ref) + ".txt" if ref else ".txt"
path = Path(__file__).parent.absolute()
input = open(path/("input" + ext), "r").read().rstrip().split("\n")
# ---

def run():
    arr = [0]
    for i in input:
        if i == '':
            arr.append(0)
        else:
            arr[len(arr)-1] += int(i)
    result = sum(sorted(arr)[-3:])
    return result

# ---
result = run()
print(result)
open(path/("result" + part + ext), "w").write(str(result).rstrip() + "\n")


from django.test import TestCase
from factories import GroupUpFactory, HierarchyFactory
from quizblock.tests.test_models import FakeReq
from gate_block.models import GateBlock
from uelc.main.models import CaseQuiz, Cohort, Case
from uelc.main.helper_functions import (
    admin_ajax_page_submit, admin_ajax_reset_page,
    page_submit, reset_page)

class TestSubmissionResetFunctions(TestCase):

    def setUp(self):
        self.hierarchy = HierarchyFactory(name="main", base_url="/pages/main/")
        self.root = self.hierarchy.get_root()
        self.r = FakeReq()
        self.cohort = Cohort.objects.create(name="main_cohort")
        self.case = Case.objects.create(
            name="main_case",
            hierarchy=self.hierarchy)
        self.r.POST = {'description': 'description', 'rhetorical': False,
                       'allow_redo': True, 'show_submit_state': False}
        self.casequiz = CaseQuiz.create(self.r)
        self.root.add_pageblock_from_dict(self.casequiz.as_dict())
        self.section = self.hierarchy.get_root().get_first_leaf()
        self.section.add_pageblock_from_dict(self.casequiz.as_dict())
        self.section.append_pageblock('', '', self.casequiz)
        self.gate_block = GateBlock.objects.create()
        self.section.append_pageblock('', '', self.gate_block)
        self.grp_usr_profile = GroupUpFactory()

    def test_admin_ajax_page_submit(self):
        '''There are two unlock methods - one in
        CaseQuiz and another in GateBlock'''
        self.assertFalse(
            self.gate_block.unlocked(self.grp_usr_profile.user, self.section))
        self.assertFalse(
            self.casequiz.unlocked(self.grp_usr_profile.user, self.section))
        admin_ajax_page_submit(self.section, self.grp_usr_profile.user)
        '''To test CaseQuiz unlocked we need to create a user visit...'''
        self.assertTrue(
            self.gate_block.unlocked(self.grp_usr_profile.user, self.section))

    def test_admin_ajax_reset_page(self):
        admin_ajax_page_submit(self.section, self.grp_usr_profile.user)
        self.assertTrue(
            self.gate_block.unlocked(self.grp_usr_profile.user, self.section))
        admin_ajax_reset_page(self.section, self.grp_usr_profile.user)
        self.assertFalse(self.gate_block.unlocked(
            self.grp_usr_profile.user, self.section))

    def test_reset_page(self):
        self.otr = FakeReq()
        self.otr.user = self.grp_usr_profile.user
        admin_ajax_page_submit(self.section, self.grp_usr_profile.user)
        reset_page(self.section, self.otr)
        self.assertFalse(
            self.gate_block.unlocked(self.grp_usr_profile.user, self.section))

class TestPageSubmitFunction(TestCase):

    def setUp(self):
        self.hierarchy = HierarchyFactory(name="main", base_url="/pages/main/")
        self.root = self.hierarchy.get_root()
        self.root.add_child_section_from_dict(
            {'label': "One", 'slug': "one",
             'children': [{'label': "Three", 'slug': "introduction"}]})
        self.root.add_child_section_from_dict({'label': "Two", 'slug': "two"})
        blocks = [{'label': 'Welcome UELC',
                   'css_extra': '',
                   'block_type': 'Test Block',
                   'body': 'You should now use the edit link to add content'}]
        self.root.add_child_section_from_dict({'label': 'Four', 'slug': 'four',
                                               'pageblocks': blocks})
        self.section = self.root.get_first_child()
        self.last_section = self.root.get_last_child()
        self.otr = FakeReq()
        self.otr.POST = {'description': 'description', 'rhetorical': False,
                         'allow_redo': True, 'show_submit_state': False}
        self.grp_usr_profile = GroupUpFactory()
        self.otr.user = self.grp_usr_profile.user

    def test_page_submit(self):
        self.first_request = page_submit(self.section, self.otr)
        self.assertEqual(
            self.first_request['Location'],
            self.section.get_next().get_absolute_url())
        self.last_request = page_submit(self.last_section, self.otr)
        self.assertEqual(
            self.last_request['Location'],
            self.last_section.get_absolute_url())

    def test_get_root_context(self):
        pass

    def test_has_responses(self):
        pass


"""The main API for the ``wombat``."""
from __future__ import annotations

import datetime
from typing import TYPE_CHECKING
from pathlib import Path

import yaml
import numpy as np
import pandas as pd
from attrs import Attribute, field, define
from simpy.events import Event

from wombat.core import (
    Metrics,
    FromDictMixin,
    RepairManager,
    ServiceEquipment,
    WombatEnvironment,
)
from wombat.windfarm import Windfarm
from wombat.core.port import Port
from wombat.core.library import load_yaml, library_map
from wombat.core.data_classes import convert_to_list

def _library_mapper(file_path: str | Path) -> Path:
    """Attempts to extract a default library path if one of "DINWOODIE" or "IEA_26"
    are passed, other returns ``file_path``.

    Parameters
    ----------
    file_path : str | Path
        Should be a valid file path, or one of "DINWOODIE" or "IEA_26" to indicate a
        provided library is being used.

    Returns
    -------
    str | Path
        The library path.
    """
    return Path(library_map.get(file_path, file_path)).resolve()  # type: ignore

@define(frozen=True, auto_attribs=True)
class Configuration(FromDictMixin):
    """The ``Simulation`` configuration data class that provides all the necessary
    definitions.

    Parameters
    ----------
    name: str
        Name of the simulation. Used for logging files.
    layout : str
        The windfarm layout file. See ``wombat.Windfarm`` for more details.
    service_equipment : str | list[str]
        The equpiment that will be used in the simulation. See
        ``wombat.core.ServiceEquipment`` for more details.
    weather : str
        The weather profile to be used. See ``wombat.simulation.WombatEnvironment``
        for more details.
    workday_start : int
        Starting hour for a typical work shift. Can be overridden by
        equipment-specific settings.
    workday_end : int
        Ending hour for a typical work shift. Can be overridden by
        equipment-specific settings.
    inflation_rate : float
        The annual inflation rate to be used for post-processing.
    fixed_costs : str
        The file name for the fixed costs assumptions.
    project_capacity : int | float
        The total capacity of the wind plant, in MW.
    port : dict | str | Path
        The port configuration file or dictionary that will be used to setup a
        tow-to-port repair strategy, default None.
    port_distance : int | float
        The simulation-wide daily travel distance for servicing equipment. This should
        be used as a base setting when multiple or all servicing equipment will be
        operating out of the same base location, but can be individually modified.
    start_year : int
        Start year of the simulation. The exact date will be determined by
        the first valid date of this year in ``weather``.
    end_year : int
        Final year of the simulation. The exact date will be determined by
        the last valid date of this year in ``weather``.
    non_operational_start : str | datetime.datetime | None
        The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized
        period of prohibited operations. When defined at the environment level, an
        undefined or later starting date will be overridden for all servicing equipment
        and any modeled port, by default None.
    non_operational_end : str | datetime.datetime | None
        The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized
        period of prohibited operations. When defined at the environment level, an
        undefined or earlier ending date will be overridden for all servicing equipment
        and any modeled port, by default None.
    reduced_speed_start : str | datetime.datetime | None
        The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized
        period of reduced speed operations. When defined at the environment level, an
        undefined or later starting date will be overridden for all servicing equipment
        and any modeled port, by default None.
    reduced_speed_end : str | datetime.datetime | None
        The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized
        period of reduced speed operations. When defined at the environment level, an
        undefined or earlier ending date will be overridden for all servicing equipment
        and any modeled port, by default None.
    reduced_speed : float
        The maximum operating speed during the annualized reduced speed operations.
        When defined at the environment level, an undefined or faster value will be
        overridden for all servicing equipment and any modeled port, by default 0.0.
    random_seed : int | None
        The random seed to be passed to a universal NumPy ``default_rng`` object to
        generate Weibull random generators, by default None.
    random_generator: np.random._generator.Generator | None
        An optional numpy random generator that can be provided to seed a simulation
        with the same generator each time, in place of the random seed. If a
        :py:attr:`random_seed` is also provided, this will override the random seed,
        by default None.
    """

    name: str
    layout: str
    service_equipment: str | list[str] = field(converter=convert_to_list)
    weather: str | pd.DataFrame
    workday_start: int = field(converter=int)
    workday_end: int = field(converter=int)
    inflation_rate: float = field(converter=float)
    project_capacity: int | float = field(converter=float)
    fixed_costs: dict | str | Path = field(default=None)
    port: dict | str | Path = field(default=None)
    start_year: int = field(default=None)
    end_year: int = field(default=None)
    port_distance: int | float = field(default=None)
    non_operational_start: str | datetime.datetime | None = field(default=None)
    non_operational_end: str | datetime.datetime | None = field(default=None)
    reduced_speed_start: str | datetime.datetime | None = field(default=None)
    reduced_speed_end: str | datetime.datetime | None = field(default=None)
    reduced_speed: float = field(default=0.0)
    random_seed: int | None = field(default=None)
    random_generator: np.random._generator.Generator | None = field(default=None)

@define(auto_attribs=True)
class Simulation(FromDictMixin):
    """The primary API to interact with the simulation methodologies.

    Parameters
    ----------
    library_path : str
        The path to the main data library.
    config : Configuration | dict | str
        One of the following:
         - A pre-loaded ``Configuration`` object
         - A dictionary ready to be converted to a ``Configuration`` object
         - The name of the configuration file to be loaded, that will be located at:
           ``library_path`` / config / ``config``
    random_seed : int | None
        The random seed to be passed to a universal NumPy ``default_rng`` object to
        generate Weibull random generators, by default None.
    random_generator: np.random._generator.Generator | None
        An optional numpy random generator that can be provided to seed a simulation
        with the same generator each time, in place of the random seed. If a
        :py:attr:`random_seed` is also provided, this will override the random seed,
        by default None.
    """

    library_path: Path = field(converter=_library_mapper)
    config: Configuration = field()
    random_seed: int | None = field(default=None)
    random_generator: np.random._generator.Generator | None = field(default=None)

    metrics: Metrics = field(init=False)
    windfarm: Windfarm = field(init=False)
    env: WombatEnvironment = field(init=False)
    repair_manager: RepairManager = field(init=False)
    service_equipment: list[ServiceEquipment] = field(init=False)
    port: Port = field(init=False)

    def __attrs_post_init__(self) -> None:
        """Post-initialization hook."""
        self._setup_simulation()

    @config.validator  # type: ignore
    def _create_configuration(
        self, attribute: Attribute, value: str | Path | dict | Configuration
    ) -> None:
        """Validates the configuration object and creates the ``Configuration`` object
        for the simulation.

        Raises
        ------
        TypeError
            Raised if the value provided is not able to create a valid ``Configuration``
            object
        ValueError
            Raised if ``name`` and ``config.name`` or ``library_path`` and
            ``config.library`` are not aligned.

        Returns
        -------
        Configuration
            The validated simulation configuration
        """
        if isinstance(value, (str, Path)):
            value = load_yaml(self.library_path / "project/config", value)
        if isinstance(value, dict):
            value = Configuration.from_dict(value)
        if isinstance(value, Configuration):
            object.__setattr__(self, attribute.name, value)
        else:
            raise TypeError(
                "``config`` must be a dictionary, valid file path to a yaml-enocoded",
                "dictionary, or ``Configuration`` object!",
            )

    @classmethod
    def from_config(
        cls, library_path: str | Path, config: str | Path | dict | Configuration
    ):
        """Creates the ``Simulation`` object only the configuration contents as either a
        full file path to the configuration file, a dictionary of the configuration
        contents, or pre-loaded ``Configuration`` object.

        Parameters
        ----------
        library_path : str | Path
            The simulation's data library. If a filename is provided for
            :py:attr:`config`, this is the data library from where it will be imported.
            This will also be used to feed into the returned `Simulation.library_path`.
        config : str | Path | dict | Configuration
            The simulation configuration, see ``Configuration`` for more details on the
            contents. The following is a description of the acceptable contents:

            - ``str`` : the full file path of the configuration yaml file.
            - ``dict`` : a dictionary with the requried configuration settings.
            - ``Configuration`` : a pre-created ``Configuration`` object.

        Raises
        ------
        TypeError
            Raised if ``config`` is not one of the three acceptable input types.

        Returns
        -------
        Simulation
            A ready-to-run ``Simulation`` object.
        """
        library_path = _library_mapper(library_path)
        if isinstance(config, (str, Path)):
            config = library_path / "project" / "config" / config
            config = load_yaml(config.parent, config.name)
        if isinstance(config, dict):
            config = Configuration.from_dict(config)
        if not isinstance(config, Configuration):
            raise TypeError(
                "``config`` must be a dictionary or ``Configuration`` object!"
            )
        if TYPE_CHECKING:
            assert isinstance(config, Configuration)  # mypy helper
        return cls(  # type: ignore
            library_path=library_path,
            config=config,
            random_seed=config.random_seed,
            random_generator=config.random_generator,
        )

    def _setup_simulation(self):
        """Initializes the simulation objects."""
        self.env = WombatEnvironment(
            self.library_path,
            self.config.weather,
            simulation_name=self.config.name,
            workday_start=self.config.workday_start,
            workday_end=self.config.workday_end,
            start_year=self.config.start_year,
            end_year=self.config.end_year,
            port_distance=self.config.port_distance,
            non_operational_start=self.config.non_operational_start,
            non_operational_end=self.config.non_operational_end,
            reduced_speed_start=self.config.reduced_speed_start,
            reduced_speed_end=self.config.reduced_speed_end,
            reduced_speed=self.config.reduced_speed,
            random_seed=self.random_seed,
            random_generator=self.random_generator,
        )
        self.repair_manager = RepairManager(self.env)
        self.windfarm = Windfarm(self.env, self.config.layout, self.repair_manager)

        # Create the servicing equipment and set the necessary environment variables
        self.service_equipment: dict[str, ServiceEquipment] = {}  # type: ignore
        for service_equipment in self.config.service_equipment:
            equipment = ServiceEquipment(
                self.env, self.windfarm, self.repair_manager, service_equipment
            )
            equipment.finish_setup_with_environment_variables()
            name = equipment.settings.name
            if name in self.service_equipment:
                raise ValueError(
                    f"Servicing equipment `{name}` already exists, please use unique"
                    " names for all servicing equipment."
                )
            self.service_equipment[name] = equipment  # type: ignore

        # Create the port and add any tugboats to the available servicing equipment list
        if self.config.port is not None:
            self.port = Port(
                self.env, self.windfarm, self.repair_manager, self.config.port
            )
            for service_equipment in self.port.service_equipment_manager.items:
                name = service_equipment.settings.name  # type: ignore
                if name in self.service_equipment:
                    raise ValueError(
                        f"Servicing equipment `{name}` already exists, please use"
                        " unique names for all servicing equipment."
                    )
                self.service_equipment[name] = service_equipment  # type: ignore

        if self.config.project_capacity * 1000 != round(self.windfarm.capacity, 6):
            raise ValueError(
                f"Input `project_capacity`: {self.config.project_capacity:,.6f} MW is"
                f" not equal to the sum of turbine capacities:"
                f" {self.windfarm.capacity / 1000:,.6f} MW"
            )

    def run(
        self,
        until: int | float | Event | None = None,
        create_metrics: bool = True,
        save_metrics_inputs: bool = True,
    ):
        """Calls ``WombatEnvironment.run()`` and gathers the results for
        post-processing. See ``wombat.simulation.WombatEnvironment.run`` or
        ``simpy.Environment.run`` for more details.

        Parameters
        ----------
        until : Optional[int | float | Event], optional
            When to stop the simulation, by default None. See documentation on
            ``simpy.Environment.run`` for more details.
        create_metrics : bool, optional
            If True, the metrics object will be created, and not, if False, by default
            True.
        save_metrics_inputs : bool, optional
            If True, the metrics inputs data will be saved to a yaml file, with file
            references to any larger data structures that can be reloaded later. If
            False, the data will not be saved, by default True.
        """
        self.env.run(until=until)
        if save_metrics_inputs:
            self.save_metrics_inputs()
        if create_metrics:
            self.initialize_metrics()

    def initialize_metrics(self) -> None:
        """Instantiates the ``metrics`` attribute after the simulation is run."""
        events = self.env.load_events_log_dataframe()
        operations = self.env.load_operations_log_dataframe()
        power_potential, power_production = self.env.power_production_potential_to_csv(
            windfarm=self.windfarm, operations=operations, return_df=True
        )
        substation_turbine_map = {
            s_id: {k: v.tolist() for k, v in dict.items()}
            for s_id, dict in self.windfarm.substation_turbine_map.items()
        }
        capacities = [
            self.windfarm.system(t).capacity for t in self.windfarm.turbine_id
        ]
        self.metrics = Metrics(
            data_dir=self.library_path,
            events=events,
            operations=operations,
            potential=power_potential,
            production=power_production,
            inflation_rate=self.config.inflation_rate,
            project_capacity=self.config.project_capacity,
            turbine_capacities=capacities,
            fixed_costs=self.config.fixed_costs,  # type: ignore
            substation_id=self.windfarm.substation_id.tolist(),
            turbine_id=self.windfarm.turbine_id.tolist(),
            substation_turbine_map=substation_turbine_map,
            service_equipment_names=[*self.service_equipment],  # type: ignore
        )

    def save_metrics_inputs(self) -> None:
        """Saves the inputs for the `Metrics` initialization with either a direct
        copy of the data or a file reference that can be loaded later.
        """
        substation_turbine_map = {
            s_id: {k: v.tolist() for k, v in dict.items()}
            for s_id, dict in self.windfarm.substation_turbine_map.items()
        }
        data = {
            "data_dir": str(self.library_path),
            "events": str(self.env.events_log_fname),
            "operations": str(self.env.operations_log_fname),
            "potential": str(self.env.power_potential_fname),
            "production": str(self.env.power_production_fname),
            "inflation_rate": self.config.inflation_rate,
            "project_capacity": self.config.project_capacity,
            "turbine_capacities": [
                self.windfarm.system(t_id).capacity for t_id in self.windfarm.turbine_id
            ],
            "fixed_costs": self.config.fixed_costs,
            "substation_id": self.windfarm.substation_id.tolist(),
            "turbine_id": self.windfarm.turbine_id.tolist(),
            "substation_turbine_map": substation_turbine_map,
            "service_equipment_names": [*self.service_equipment],
        }

        with open(self.env.metrics_input_fname, "w") as f:
            yaml.dump(data, f, default_flow_style=False, sort_keys=False)


import time
from selenium import webdriver  # импортируем webdriver
from selenium.webdriver.support.select import Select  # импортируем класс Select или библиотеки webdriver element =
from selenium.webdriver.chrome.options import Options

driver = webdriver.Chrome(executable_path='C:/chromedriver')
driver.maximize_window()

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium import webdriver

####################################################################################################################
# Shop1 отображение страницы товара
# 1 open url
driver.get("https://practice.automationtesting.in/")  # метод get сообщает браузеру, что нужно открыть сайт по указанной ссылке
# wait
driver.implicitly_wait(6)

# 2 login
driver.find_element_by_css_selector('#menu-item-50 > a').click()
driver.find_element_by_id("username").send_keys("warargout@gmail.com")
driver.find_element_by_id("password").send_keys("ghjnfkbyf")
time.sleep(2)  # ставим ожидание

# 3 click shop
driver.find_element_by_css_selector('#menu-item-40 > a').click()
time.sleep(2)  # ставим ожидание

# 4 click html5
driver.find_element_by_css_selector('a[data-product_id="181"]').click()
time.sleep(2)  # ставим ожидание

# 5 print header
header = driver.find_element_by_css_selector('#content > div > div:nth-child(2) > h1').text
print("\n", "Заголовок книги: ", header)

######################################################################################################################
# Shop2 количество товаров кв категории
# 1 open url
driver.get("https://practice.automationtesting.in/")  # метод get сообщает браузеру, что нужно открыть сайт по указанной ссылке
# wait
driver.implicitly_wait(6)

# 2 login
driver.find_element_by_css_selector('#menu-item-50 > a').click()
driver.find_element_by_id("username").send_keys("warargout@gmail.com")
driver.find_element_by_id("password").send_keys("ghjnfkbyf")
time.sleep(2)  # ставим ожидание

# 3 click shop
driver.find_element_by_css_selector('#menu-item-40 > a').click()
time.sleep(2)  # ставим ожидание

# 4 select html
driver.find_element_by_css_selector('.cat-item-19 > a').click()
time.sleep(2)  # ставим ожидание

# 5 print count
bks = len(driver.find_elements_by_class_name('status-publish'))
print('\n', "Книг найдено: ", bks)

#######################################################################################################################
# Shop3 сортировка товаров
# 1 open url
driver.get("https://practice.automationtesting.in/")  # метод get сообщает браузеру, что нужно открыть сайт по указанной ссылке
# wait
driver.implicitly_wait(6)

# 2 login
driver.find_element_by_css_selector('#menu-item-50 > a').click()
driver.find_element_by_id("username").send_keys("warargout@gmail.com")
driver.find_element_by_id("password").send_keys("ghjnfkbyf")
time.sleep(2)  # ставим ожидание

# 3 click shop
driver.find_element_by_css_selector('#menu-item-40 > a').click()
time.sleep(2)  # ставим ожидание

# 4 def order ?
SortType = driver.find_element_by_css_selector('option[selected="selected"]').get_attribute("value")

print("\n", "text ", SortType)
# print("\n", "text ", str(SortType))
assert EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.orderby > option[selected = "selected"]'), 'Default sorting')

# assert driver.find_element_by_css_selector('.orderby').value_of_css_property('value') == "menu_order"

# 5 sort by price down
SortOrd = driver.find_element_by_class_name('orderby')
Select(SortOrd).select_by_value("price-desc")
time.sleep(2)  # ставим ожидание

# 6 new old SortOrder
SortOrder = driver.find_element_by_css_selector('option[selected="selected"]')

# 7 order by price down?
SortType = SortOrder.get_attribute("value")
print("\n", "новая сортировка ", SortType)

#######################################################################################################################
# Shop4 отображение, скидка товара
driver.get("https://practice.automationtesting.in/")  # метод get сообщает браузеру, что нужно открыть сайт по указанной ссылке
# wait
driver.implicitly_wait(6)

# 2 login
driver.find_element_by_css_selector('#menu-item-50 > a').click()
driver.find_element_by_id("username").send_keys("warargout@gmail.com")
driver.find_element_by_id("password").send_keys("ghjnfkbyf")
time.sleep(2)  # ставим ожидание

# 3 click shop
driver.find_element_by_css_selector('#menu-item-40 > a').click()
time.sleep(2)  # ставим ожидание

# 4 select Android
driver.find_element_by_css_selector('img[title="Android Quick Start Guide"]').click()
time.sleep(2)  # ставим ожидание

# 5 old price 600
OldPr = driver.find_element_by_css_selector('.price > del > span').text
print("\n", "старая цена ", OldPr)
assert OldPr == "₹600.00"

# 6 new price 450
NewPr = driver.find_element_by_css_selector('.price > ins > span').text
print(" новая цена   ", OldPr)
assert NewPr == "₹450.00"

# 7 bookcase
WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'img[title="Android Quick Start Guide"]'))).click()
time.sleep(2)  # ставим ожидание, что б посмотреть

# 8 close bookcase
WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.pp_close'))).click()
time.sleep(2)  # ставим ожидание, что б посмотреть

######################################################################################################################
# Shop5 проверка цены в магазине
driver.get("https://practice.automationtesting.in/")  # метод get сообщает браузеру, что нужно открыть сайт по указанной ссылке
# wait
driver.implicitly_wait(6)

# 2 click shop
driver.find_element_by_css_selector('#menu-item-40 > a').click()
time.sleep(2)  # ставим ожидание

# 3 add html5 WA D to basket
driver.find_element_by_css_selector('a[data-product_id="182"]').click()
time.sleep(2)  # ставим ожидание

# 4 book 1? price 180?
BaskIt = driver.find_element_by_class_name('cartcontents').text
print("\n", "книг в корзине ", BaskIt)
assert BaskIt == "1 Item"

BaskPr = driver.find_element_by_css_selector('#wpmenucartli > a > span[class="amount"]').text
print(" стоимость кв корзине ", BaskPr)
assert BaskPr == "₹180.00"

# 5 to basket
driver.find_element_by_id('wpmenucartli').click()
time.sleep(2)  # ставим ожидание, что б посмотреть

# 6 subtotal
WebDriverWait(driver, 15).until(EC.visibility_of_element_located((By.CSS_SELECTOR, 'td[data-title="Subtotal"] > span'))  )
time.sleep(2)  # ставим ожидание, что б посмотреть

# 7 total
WebDriverWait(driver, 15).until(EC.visibility_of_element_located((By.CSS_SELECTOR, 'td[data-title="Total"] > strong > span'))  )
time.sleep(2)  # ставим ожидание, что б посмотреть

######################################################################################################################
# Shop6 работа в корзине
driver.get("https://practice.automationtesting.in/")  # метод get сообщает браузеру, что нужно открыть сайт по указанной ссылке
# wait
driver.implicitly_wait(6)

# 2 click shop
driver.find_element_by_css_selector('#menu-item-40 > a').click()
time.sleep(2)  # ставим ожидание, что б посмотреть

# 3 add html5 WA D and JS D S a A to basket
driver.find_element_by_css_selector('a[data-product_id="182"]').click() #html5 WA D to basket
time.sleep(2)  # ставим ожидание
driver.find_element_by_css_selector('a[data-product_id="180"]').click() #JS D S a A to basket
time.sleep(2)  # ставим ожидание

# 4 to basket
driver.find_element_by_id('wpmenucartli').click()
time.sleep(2)  # ставим ожидание, что б посмотреть

# 5 del html5
driver.find_element_by_css_selector('a[data-product_id="182"]').click()
time.sleep(2)  # ставим ожидание, что б посмотреть

# 6 undo del
driver.find_element_by_link_text('Undo?').click()
time.sleep(2)  # ставим ожидание, что б посмотреть

# 7 JS count -> 3
CountJS = driver.find_element_by_css_selector('.cart > tbody > tr:nth-child(2) > td:nth-child(5) > div > input')
CountJS.clear()
CountJS.send_keys(3)
time.sleep(2)  # ставим ожидание, что б посмотреть

# 8 update basket
driver.find_element_by_css_selector('[value="Update Basket"]').click()
time.sleep(2)  # ставим ожидание, что б посмотреть

# 9 JS 3?
# NumberJS = CountJS.value_of_css_property("value")
CountJS = driver.find_element_by_css_selector('.cart > tbody > tr:nth-child(2) > td:nth-child(5) > div > input')
NumberJS = CountJS.get_attribute('value')
print("\n", "количество книг JS ", NumberJS)
assert  NumberJS == "3"

# 10 Applay coupon
time.sleep(2)  # ставим ожидание, на этот раз по заданию
driver.find_element_by_css_selector('[name="apply_coupon"]').click()

# 11 Please enter ?
ErrTx = driver.find_element_by_css_selector('.woocommerce-error > li').text
print("\n", "Текст ошибки ", "\n", ErrTx)
assert ErrTx == "Please enter a coupon code."

################################################################################################################
# Shop7 покупка товара
driver.get("https://practice.automationtesting.in/")  # метод get сообщает браузеру, что нужно открыть сайт по указанной ссылке
# wait
driver.implicitly_wait(6)

# 2 click shop
driver.find_element_by_css_selector('#menu-item-40 > a').click()
driver.execute_script("window.scrollBy(0, 300);") # проскроллитm страницу на 300 пикселей вниз

# 3 add html5 WA D to basket
driver.find_element_by_css_selector('a[data-product_id="182"]').click() #html5 WA D to basket
time.sleep(1)  # ставим ожидание, тут надо

# 4 to basket
driver.find_element_by_id('wpmenucartli').click()

# 5 proceed
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.checkout-button'))).click()

# 6 fill *
WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'billing_first_name'))).send_keys("Yurec")
driver.find_element_by_id('billing_last_name').send_keys('Tkachev')
driver.find_element_by_id('billing_email').send_keys('warargout@gmail.com')
driver.find_element_by_id('billing_phone').send_keys('5500 8800')

driver.find_element_by_id('s2id_billing_country').click() #нажать селектор страны
driver.find_element_by_id('s2id_autogen1_search').send_keys('rus') #поиск по имени
driver.find_element_by_css_selector('#select2-results-1 > li:nth-last-child(1)').click() #выбрали результат

driver.find_element_by_id('billing_address_1').send_keys('1st square, 1')
driver.find_element_by_id('billing_city').send_keys('Krasnodar')
driver.find_element_by_id('billing_state').send_keys('Krasnodarskiy kray')
driver.find_element_by_id('billing_postcode').send_keys('350000')

# 7 payment
driver.execute_script("window.scrollBy(0, 600);") # проскроллитm страницу на 300 пикселей вниз
time.sleep(2)  # ставим ожидание, по заданию
driver.find_element_by_id('payment_method_cheque').click()

# 8 place order
driver.find_element_by_id('place_order').click()

# 9 thanks ?
ThTx = driver.find_element_by_css_selector('#page-35 > div > div > p').text
print(ThTx)
assert ThTx == "Thank you. Your order has been received."

# 10 Check Payments ?
PMTx = driver.find_element_by_css_selector('.order_details > tfoot > tr:nth-child(3) > td').text
print(PMTx)
assert PMTx == "Check Payments"
time.sleep(3)  # ставим ожидание, что б посмотреть

driver.quit()


import os
import argparse
from convert_model import convert_model
from convert_mean import convert_mean
import mxnet as mx

apache_repo_url = 'https://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/'
repo_url = os.environ.get('MXNET_GLUON_REPO', apache_repo_url)
_mx_caffe_model_root = '{repo}caffe/models/'.format(repo=repo_url)

"""Dictionary for model meta information

For each model, it requires three attributes:

  - prototxt: URL for the deploy prototxt file
  - caffemodel: URL for the binary caffemodel
  - mean : URL for the data mean or a tuple of float

Optionly it takes

  - top-1-acc : top 1 accuracy for testing
  - top-5-acc : top 5 accuracy for testing
"""
model_meta_info = {
    # pylint: disable=line-too-long
    'bvlc_alexnet' : {
        'prototxt' : (_mx_caffe_model_root + 'bvlc_alexnet/deploy.prototxt',
                      'cb77655eb4db32c9c47699c6050926f9e0fc476a'),
        'caffemodel' : (_mx_caffe_model_root + 'bvlc_alexnet/bvlc_alexnet.caffemodel',
                        '9116a64c0fbe4459d18f4bb6b56d647b63920377'),
        'mean' : (_mx_caffe_model_root + 'bvlc_alexnet/imagenet_mean.binaryproto',
                  '63e4652e656abc1e87b7a8339a7e02fca63a2c0c'),
        'top-1-acc' : 0.571,
        'top-5-acc' : 0.802
    },
    'bvlc_googlenet' : {
        'prototxt' : (_mx_caffe_model_root + 'bvlc_googlenet/deploy.prototxt',
                      '7060345c8012294baa60eeb5901d2d3fd89d75fc'),
        'caffemodel' : (_mx_caffe_model_root + 'bvlc_googlenet/bvlc_googlenet.caffemodel',
                        '405fc5acd08a3bb12de8ee5e23a96bec22f08204'),
        'mean' : (123, 117, 104),
        'top-1-acc' : 0.687,
        'top-5-acc' : 0.889
    },
    'vgg-16' : {
        'prototxt' : (_mx_caffe_model_root + 'vgg/VGG_ILSVRC_16_layers_deploy.prototxt',
                      '2734e5500f1445bd7c9fee540c99f522485247bd'),
        'caffemodel' : (_mx_caffe_model_root + 'vgg/VGG_ILSVRC_16_layers.caffemodel',
                        '9363e1f6d65f7dba68c4f27a1e62105cdf6c4e24'),
        'mean': (123.68, 116.779, 103.939),
        'top-1-acc' : 0.734,
        'top-5-acc' : 0.914
    },
    'vgg-19' : {
        'prototxt' : (_mx_caffe_model_root + 'vgg/VGG_ILSVRC_19_layers_deploy.prototxt',
                      '132d2f60b3d3b1c2bb9d3fdb0c8931a44f89e2ae'),
        'caffemodel' : (_mx_caffe_model_root + 'vgg/VGG_ILSVRC_19_layers.caffemodel',
                        '239785e7862442717d831f682bb824055e51e9ba'),
        'mean' : (123.68, 116.779, 103.939),
        'top-1-acc' : 0.731,
        'top-5-acc' : 0.913
    },
    'resnet-50' : {
        'prototxt' : (_mx_caffe_model_root + 'resnet/ResNet-50-deploy.prototxt',
                      '5d6fd5aeadd8d4684843c5028b4e5672b9e51638'),
        'caffemodel' : (_mx_caffe_model_root + 'resnet/ResNet-50-model.caffemodel',
                        'b7c79ccc21ad0479cddc0dd78b1d20c4d722908d'),
        'mean' : (_mx_caffe_model_root + 'resnet/ResNet_mean.binaryproto',
                  '0b056fd4444f0ae1537af646ba736edf0d4cefaf'),
        'top-1-acc' : 0.753,
        'top-5-acc' : 0.922
    },
    'resnet-101' : {
        'prototxt' : (_mx_caffe_model_root + 'resnet/ResNet-101-deploy.prototxt',
                      'c165d6b6ccef7cc39ee16a66f00f927f93de198b'),
        'caffemodel' : (_mx_caffe_model_root + 'resnet/ResNet-101-model.caffemodel',
                        '1dbf5f493926bb9b6b3363b12d5133c0f8b78904'),
        'mean' : (_mx_caffe_model_root + 'resnet/ResNet_mean.binaryproto',
                  '0b056fd4444f0ae1537af646ba736edf0d4cefaf'),
        'top-1-acc' : 0.764,
        'top-5-acc' : 0.929
    },
    'resnet-152' : {
        'prototxt' : (_mx_caffe_model_root + 'resnet/ResNet-152-deploy.prototxt',
                      'ae15aade2304af8a774c5bfb1d32457f119214ef'),
        'caffemodel' : (_mx_caffe_model_root + 'resnet/ResNet-152-model.caffemodel',
                        '251edb93604ac8268c7fd2227a0f15144310e1aa'),
        'mean' : (_mx_caffe_model_root + 'resnet/ResNet_mean.binaryproto',
                  '0b056fd4444f0ae1537af646ba736edf0d4cefaf'),
        'top-1-acc' : 0.77,
        'top-5-acc' : 0.933
    },
}

def get_model_meta_info(model_name):
    """returns a dict with model information"""
    return model_meta_info[model_name].copy()

def download_caffe_model(model_name, meta_info, dst_dir='./model'):
    """Download caffe model into disk by the given meta info """
    if not os.path.isdir(dst_dir):
        os.mkdir(dst_dir)
    model_name = os.path.join(dst_dir, model_name)

    assert 'prototxt' in meta_info, "missing prototxt url"
    proto_url, proto_sha1 = meta_info['prototxt']
    prototxt = mx.gluon.utils.download(proto_url,
                                       model_name+'_deploy.prototxt',
                                       sha1_hash=proto_sha1)

    assert 'caffemodel' in meta_info, "mssing caffemodel url"
    caffemodel_url, caffemodel_sha1 = meta_info['caffemodel']
    caffemodel = mx.gluon.utils.download(caffemodel_url,
                                         model_name+'.caffemodel',
                                         sha1_hash=caffemodel_sha1)
    assert 'mean' in meta_info, 'no mean info'
    mean = meta_info['mean']
    if isinstance(mean[0], str):
        mean_url, mean_sha1 = mean
        mean = mx.gluon.utils.download(mean_url,
                                       model_name+'_mean.binaryproto',
                                       sha1_hash=mean_sha1)
    return (prototxt, caffemodel, mean)

def convert_caffe_model(model_name, meta_info, dst_dir='./model'):
    """Download, convert and save a caffe model"""

    (prototxt, caffemodel, mean) = download_caffe_model(model_name, meta_info, dst_dir)
    model_name = os.path.join(dst_dir, model_name)
    convert_model(prototxt, caffemodel, model_name)
    if isinstance(mean, str):
        mx_mean = model_name + '-mean.nd'
        convert_mean(mean, mx_mean)
        mean = mx_mean
    return (model_name, mean)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Convert Caffe model zoo')
    parser.add_argument('model_name', help='can be '+', '.join(model_meta_info.keys()))
    args = parser.parse_args()
    assert args.model_name in model_meta_info, 'Unknown model ' + args.model_name
    fname, _ = convert_caffe_model(args.model_name, model_meta_info[args.model_name])
    print('Model is saved into ' + fname)


# coding=utf-8
"""
@Description: gunicorn启动配置
@FilePath: /blog/gunicorn_conf.py
@Author: liu.junlei@foxmail.com
@Date: 2020-12-27 19:04:33
@LastEditTime: 2020-12-27 20:36:06
"""

import multiprocessing

bind = "0.0.0.0:8888"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'gevent'
timeout = 500
accesslog = '/root/blog/logs/gunicorn.log'
errorlog = '/root/blog/logs/gunicorn.error.log'


#-------------------------------------------
#VARIOUS UNIVARIABLE PLOTTING EXAMPLES
	#2021-08-03  JFH
#-------------------------------------------

##-------------------------------------------
##SIMPLE STATIC 1D PLOT
##-------------------------------------------

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

#DEFINE DATA
x = np.linspace(0, 10,200)
y  = np.sin(x)
y1 = y + np.random.normal(0, 0.5, len(x)) #ADD NOISE

#PLOT
fig, ax = plt.subplots()
ax.plot(x,y,'-',x,y1,'o')
plt.show()
#exit()

##-------------------------------------------
##SIMPLE PLOT MOVIE: PLOTING FRAMES FROM LOOP
##-------------------------------------------

import numpy as np
import matplotlib.pyplot as plt

#PARAMETERS
dt=0.01		#time to pause between frames in seconds
x0=0.0; dx=0.1  #mesh parameters
Nframe=100

plt.figure() #INITIALIZE FIGURE
FS=18
plt.xlabel('Time (s)', fontsize=FS)
plt.ylabel('Amplitude (cm)', fontsize=FS)
for i in range(0,Nframe):
	x=x0+i*dx; y=np.sin(x)
	plt.plot(x,y,'bo')
	plt.pause(dt)
plt.show()
#exit()

##-------------------------------------------
##SUBPLOT PLOT MOVIE: PLOTING FRAMES FROM LOOP
##-------------------------------------------

import numpy as np
import matplotlib.pyplot as plt
import time

#PARAMETERS
dt=0.005		#time to pause between frames in seconds
NP=100

x = np.linspace(0,10,NP)
y = np.exp(-0.1*x)*np.sin(5*x)

plt.ion()  #Enable interactive mode.
fig,ax = plt.subplots(3,1,figsize=(15,15))
ax[0].plot(x,y,'-')
ax[1].plot(x,y,'-',)

plt.show()

for i in range(0,len(x)):

	ax[0].clear()

	ax[0].plot(x,y,'-'); ax[0].plot(x[i],y[i],'bo')
	ax[1].plot(x[i],y[i],'ro')
	ax[2].plot(x[i],y[i],'ro')

	plt.draw()
	plt.pause(dt)
#exit()

##-------------------------------------------
## SMOOTHING+SAVING A PLOT TO PNG AND PDF FILES
##-------------------------------------------

#NOTE: PDF IS A "VECTOR IMAGES"
#i.e will scale well and look good in either large or small windows without becoming grainy

import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp1d #,spline
import scipy.signal
import warnings
from matplotlib.ticker import FormatStrFormatter

warnings.filterwarnings("ignore") #supress all warnings

#GENERATE DATA
N=1002	#number of data points
x=np.linspace(-45,45.0, N);
#ye=np.sin(x) #exact
ye=x*x*np.sin(x) #exact
y=ye+np.random.normal(0, 100, N)	#add noise

#DEFINE
f = plt.figure()
f, ax = plt.subplots()

#AXES
FS=18	#FONT SIZE
plt.xlabel('Time (ps)', fontsize=FS)
plt.ylabel('Amplitude (cm)', fontsize=FS)
plt.xticks([-40,-20,0,20,40],fontsize=FS)
plt.yticks([-2000,-1000,0,1000,2000],fontsize=FS)

#GENERATE SMOOTH LINE THROUHG NOISY DATA

#SMOTHING METHOD-1: savgol_filter
window=61 #window must me odd and requires adjustment depending on plot

#SMOOTHED DATA (SAME NUMBER OF POINTS AS y)
#https://riptutorial.com/scipy/example/15878/using-a-savitzky-golay-filter
ys = scipy.signal.savgol_filter(y, window, 4)  # window size , polynomial order

# #QUADRATICALLY INTERPOLATE THE savgol_filter DATA ONTO LESS DENSE MESH
xs1=np.linspace(min(x), max(x), int(0.25*len(x)))
F=interp1d(x, ys, kind='quadratic');
ys1=F(xs1);

#PLOT
plt.plot(x, y,'.', markersize=16,color='black',markerfacecolor='white',label="raw data") # ,color='black', markersize=8)
plt.plot(x,ye,'r-',linewidth=3,label="ground truth")
# plt.plot(x,ys,'*',color='blue',linewidth=1.0,label="savgol smoothing")
plt.plot(xs1,ys1,'*',color='blue',linewidth=1.0,label="savgol smoothing")
ax.legend()

#PLOT RANGES
plt.xlim(min(x),0)

#CONTROL AXIS TICK FORMAT
ax.yaxis.set_major_formatter(FormatStrFormatter('%4.1f'))
ax.xaxis.set_major_formatter(FormatStrFormatter('%.0f'))

#SAVE IMAGES
f.savefig('plot-1.png', bbox_inches='tight')
f.savefig("plot-1.pdf", bbox_inches='tight')
exit()

# ###-------------------------------------------
# ###FUNC-ANIMATION MULTIPLE ANIMATIONS (TAKEN FROM ONLINE)
# ###-------------------------------------------

# import numpy as np
# import matplotlib.pyplot as plt
# from matplotlib.animation import FuncAnimation

# # https://brushingupscience.com/2016/06/21/matplotlib-animations-the-easy-way/

# # Example animations using matplotlib's FuncAnimation
# # Ken Hughes. 18 June 2016.

# # For more detail, see
# # https://brushingupscience.wordpress.com/2016/06/21/matplotlib-animations-the-easy-way/

# # Examples include
# #    - line plot
# #    - pcolor plot
# #    - scatter plot
# #    - contour plot
# #    - quiver plot
# #    - plot with changing labels

# import numpy as np
# import matplotlib.pyplot as plt
# from matplotlib.animation import FuncAnimation

# # Use matplotlib ggplot stylesheet if available
# try:
#    plt.style.use('ggplot')
# except:
#    pass

# # Set which type of animation will be plotted. One of:
# # line, pcolor, scatter, contour, quiver, labels
# animation_type = 'line'

# # ----------------------------------------------------------------------------
# # Create data to plot. F is 2D array. G is 3D array

# # Create a two-dimensional array of data: F(x, t)
# x = np.linspace(-3, 3, 91)
# t = np.linspace(0, 25, 30)
# X2, T2 = np.meshgrid(x, t)
# sinT2 = np.sin(2*np.pi*T2/T2.max())
# F = 0.9*sinT2*np.sinc(X2*(1 + sinT2))

# # Create three-dimensional array of data G(x, z, t)
# x = np.linspace(-3, 3, 91)
# t = np.linspace(0, 25, 30)
# y = np.linspace(-3, 3, 91)
# X3, Y3, T3 = np.meshgrid(x, y, t)
# sinT3 = np.sin(2*np.pi*T3 /
#               T3.max(axis=2)[..., np.newaxis])
# G = (X3**2 + Y3**2)*sinT3

# # ----------------------------------------------------------------------------
# # Set up the figure and axis
# fig, ax = plt.subplots(figsize=(4, 3))

# if animation_type not in ['line', 'scatter']:
#    ax.set_aspect('equal')

# # ----------------------------------------------------------------------------
# if animation_type == 'line':
#    ax.set(xlim=(-3, 3), ylim=(-1, 1))

#    line = ax.plot(x, F[0, :], color='k', lw=2)[0]

#    def animate(i):
#        line.set_ydata(F[i, :])

# # ----------------------------------------------------------------------------
# if animation_type == 'pcolor':
#    cax = ax.pcolormesh(x, y, G[:-1, :-1, 0], vmin=-1, vmax=1, cmap='Blues')
#    fig.colorbar(cax)

#    def animate(i):
#        cax.set_array(G[:-1, :-1, i].flatten())

# # ----------------------------------------------------------------------------
# if animation_type == 'scatter':
#    ax.set(xlim=(-3, 3), ylim=(-1, 1))
#    scat = ax.scatter(x[::3], F[0, ::3])

#    def animate(i):
#        # Must pass scat.set_offsets an N x 2 array
#        y_i = F[i, ::3]
#        scat.set_offsets(np.c_[x[::3], y_i])

# # ----------------------------------------------------------------------------
# if animation_type == 'contour':
#    # Keyword options used in every call to contour
#    contour_opts = {'levels': np.linspace(-9, 9, 10), 'cmap':'RdBu', 'lw': 2}
#    cax = ax.contour(x, y, G[..., 0], **contour_opts)

#    def animate(i):
#        ax.collections = []
#        ax.contour(x, y, G[..., i], **contour_opts)

# # ----------------------------------------------------------------------------
# if animation_type == 'quiver':
#    ax.set(xlim=(-4, 4), ylim=(-4, 4))

#    # Plot every 20th arrow
#    step = 15
#    x_q, y_q = x[::step], y[::step]

#    # Create U and V vectors to plot
#    U = G[::step, ::step, :-1].copy()
#    V = np.roll(U, shift=4, axis=2)

#    qax = ax.quiver(x_q, y_q, U[..., 0], V[..., 0], scale=100)

#    def animate(i):
#        qax.set_UVC(U[..., i], V[..., i])

# # ----------------------------------------------------------------------------
# if animation_type == 'labels':
#    ax.set(xlim=(-1, 1), ylim=(-1, 1))
#    string_to_type = 'abcdefghijklmnopqrstuvwxyz0123'
#    label = ax.text(0, 0, string_to_type[0],
#                    ha='center', va='center',
#                    fontsize=12)

#    def animate(i):
#        label.set_text(string_to_type[:i+1])
#        ax.set_ylabel('Time (s): ' + str(i/10))
#        ax.set_title('Frame ' + str(i))

# # ----------------------------------------------------------------------------
# # Save the animation
# anim = FuncAnimation(fig, animate, interval=100, frames=len(t)-1, repeat=True)
# fig.show()
# plt.show()
# # anim.save(animation_type + '.gif', writer='imagemagick')
# exit()
# I HAVE WORKED THROUGH THIS EXAMPLE AND UNDERSTAND IT COMPLETELY


import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.core.audio import SoundLoader
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from kivy.clock import Clock
from functools import partial
from random import shuffle
from backend.managerController import *

kivy.require('1.0.8')

controller = ManagerControllerSingleton()

class AudioBackground(BoxLayout):
    pass

class MetadataEditor(BoxLayout):
    pass

class MusicViewer(GridLayout):
    pass

class AlbumViewer(GridLayout):
    pass

class ArtistViewer(GridLayout):
    pass

class MusicLibraryApp(App):
    filename = StringProperty(None)
    sound = ObjectProperty(None, allownone=True)
    volume = NumericProperty(1.0)

    currentSongDuration = NumericProperty(None)
    currentSongPosition = NumericProperty(None)
    progressBarPosition = NumericProperty(0)

    localSong = None
    localSongPos = 0
    currentSongTitle = StringProperty('-')

    kv_directory = 'gui'
    currentState = "allSongs"
    buttonStatus = StringProperty("Play")
    repeatButtonStatus = StringProperty("Repeat")

    onBuild = True

    playList = []
    auxPlaylist = []

    def copy_playlist(self, root, num, nothing=None):
        self.localSong = self.auxPlaylist[num]
        self.localSongPos = num
        self.playList = self.auxPlaylist
        self.load_song(root, self.localSong, num)

    def __load_playlist(self, root, all=True, artist="", band=""):
        self.auxPlaylist = controller.allSongs
        if all == False:  # load all songs!
            self.auxPlaylist = []
            if artist != "":
                ids = artist.getSongs()
            else:
                ids = band.getSongs()
            for songid in ids:
                self.auxPlaylist.append(controller.allSongs[controller.dictSongs[str(songid)]])

    def __append_playlist(self, song, nothing=None):
        self.playList.append(song)

    def __remove_playlist(self, root, index, nothing=None):
        del self.playList[index]
        if self.playList == []:
            self.localSong = None
            self.sound.stop()
            Clock.unschedule(self.update_progress)
            self.sound.unload()
            self.buttonStatus = "Play"
        else:
            if self.localSongPos == index:
                self.localSong = self.playList[index]
                self.sound.stop()
                Clock.unschedule(self.update_progress)
                self.load_song(root,self.localSong,index)
                self.buttonStatus = "Play"
        self.view_playlist(root)

    def clear_playlist(self, root):
        self.playList = []
        self.localSong = None
        try:
            self.sound.stop()
            Clock.unschedule(self.update_progress)
            self.sound.unload()
            self.buttonStatus = "Play"
            self.view_playlist(root)
        except:
            return

    def next_playlist(self, root):
        if self.localSongPos >= (len(self.playList) - 1):
            self.localSongPos = 0
        else:
            self.localSongPos += 1
        self.load_song(root, self.playList[self.localSongPos], self.localSongPos)

    def previous_playlist(self, root):
        if self.localSongPos == 0:
            self.localSongPos = len(self.playList) - 1
        else:
            self.localSongPos -= 1
        self.load_song(root, self.playList[self.localSongPos], self.localSongPos)

    def shuffle_playlist(self, root):
        self.playList[self.localSongPos], self.playList[0] = self.playList[0], self.playList[self.localSongPos]
        copy = []
        for i in range(1, len(self.playList)):
            copy.append(self.playList[i])
        shuffle(copy)
        copy.insert(0, self.playList[0])
        self.playList = copy
        self.localSongPos = 0
        self.view_playlist(root)

    def view_playlist(self, root):
        if root.ids["musicPanel"].children:
            root.ids["musicPanel"].clear_widgets()
        music_view = MusicViewer()
        root.ids["musicPanel"].add_widget(music_view)

        songButtonList = []
        playlistButtonList = []
        for song in self.playList:
            songButtonList.append(Button(text=song.title, size=(50, 30), size_hint=(0.8, None)))
            music_view.add_widget(songButtonList[len(songButtonList) - 1])
            playlistButtonList.append(Button(text="-", size=(50, 30), size_hint=(0.2, None)))
            music_view.add_widget(playlistButtonList[len(playlistButtonList) - 1])

        # If there is a song, load it and binding the buttons
        if len(songButtonList) > 0:
            for num in range(0, len(songButtonList)):
                button_import = partial(self.load_song, root, self.playList[num], num)
                songButtonList[num].bind(on_press=button_import)
                playlist_button_import = partial(self.__remove_playlist, root, num)
                playlistButtonList[num].bind(on_press=playlist_button_import)

        return root

    def load_song(self,  root, song, num, nothing='nothing'):
        wasPlaying = False
        # TODO: Separate selected song from playing song
        if self.sound:
            if self.sound.state != 'stop':
                wasPlaying = True
                self.sound.stop()
                Clock.unschedule(self.update_progress)
        self.localSong = song
        self.localSongPos = num
        self.currentSongTitle = self.localSong.title + "  --  " + self.localSong.album + "  --  " + self.localSong.band
        self.filename = song.path
        self.sound = SoundLoader.load(self.filename)
        self.currentSongPosition = 0
        if wasPlaying:
            self.__play_music()
        if self.currentState == "metadata":
            self.view_metadata(root)

    def __load_all_songs(self, root, all=True, artist="", band=""):
        if root.ids["musicPanel"].children:
            root.ids["musicPanel"].clear_widgets()
        music_view = MusicViewer()
        root.ids["musicPanel"].add_widget(music_view)

        self.__load_playlist(root, all, artist, band)

        songButtonList = []
        playlistButtonList = []
        for song in self.auxPlaylist:
            songButtonList.append(Button(text=song.title, size=(50, 30), size_hint=(0.8, None)))
            music_view.add_widget(songButtonList[len(songButtonList) - 1])
            playlistButtonList.append(Button(text="+", size=(50, 30), size_hint=(0.2, None)))
            music_view.add_widget(playlistButtonList[len(playlistButtonList) - 1])
            self.localSongPos = 0

        # If there is a song, load it and binding the buttons
        if len(songButtonList) > 0:
            print(self.auxPlaylist)
            if self.onBuild == True:
                self.playList = self.auxPlaylist
                self.load_song(root, self.playList[0], 0, None)
                self.onBuild = False

            for num in range(0, len(songButtonList)):
                button_import = partial(self.copy_playlist, root, num)
                songButtonList[num].bind(on_press=button_import)
                playlist_button_import = partial(self.__append_playlist, self.auxPlaylist[num])
                playlistButtonList[num].bind(on_press=playlist_button_import)

        return root

    def build(self):
        root = AudioBackground(spacing=5)

        # Initializing components
        controller.initialize_components()

        # Listing all musics on db
        self.__load_all_songs(root)

        return root

    def view_all_songs(self,root):
        self.__load_all_songs(root)

    def import_songs(self, root):
        controller.import_song()
        if (self.currentState == "allSongs"):
            self.view_all_songs(root)
            return
        if (self.currentState == "albums"):
            self.view_albums(root)
            return
        if (self.currentState == "artists"):
            self.view_albums(root)
            return
        if (self.currentState == "metadata"):
            self.view_metadata(root)
            return

    def view_albums(self, root):
        self.currentState = "albums"
        if root.ids["miscPanel"].children:
            root.ids["miscPanel"].clear_widgets()
        album_view = AlbumViewer()
        root.ids["miscPanel"].add_widget(album_view)

        buttonList = []
        for album in controller.allAlbums:
            nameToPrint = album.getName()
            tam = len(album.getName())
            #formatting album name
            while tam > 20:
                nameToPrint = nameToPrint[0:20]
                nameToPrint += '\n'
                nameToPrint += album.getName()[20:]
                tam -= 20
            buttonList.append(Button(text=nameToPrint, size_hint=(.3, None)))
            album_view.add_widget(buttonList[len(buttonList)-1])
            album_press = partial(self.album_press, controller.allAlbums[len(buttonList) - 1], root)
            buttonList[len(buttonList) - 1].bind(on_press=album_press)

    def view_artists(self, root):
        self.currentState="artists"
        if root.ids["miscPanel"].children:
            root.ids["miscPanel"].clear_widgets()
        artist_view = ArtistViewer()
        root.ids["miscPanel"].add_widget(artist_view)

        buttonList = []
        for artist in controller.allArtists:
            nameToPrint = artist.getName()
            tam = len(artist.getName())
            # formatting album name
            while tam > 20:
                nameToPrint = nameToPrint[0:20]
                nameToPrint += '\n'
                nameToPrint += artist.getName()[20:]
                tam -= 20
            buttonList.append(Button(text=nameToPrint, size_hint=(.3, None)))
            artist_view.add_widget(buttonList[len(buttonList) - 1])
            artist_press = partial(self.artist_press, controller.allArtists[len(buttonList) - 1], root)
            buttonList[len(buttonList) - 1].bind(on_press=artist_press)

    def artist_press(self, artistObj, root, nothing):
        # Listing all musics on db
        self.__load_all_songs(root, artist=artistObj, all=False)

    def album_press(self, albumObj, root, nothing):
        self.__load_all_songs(root, all=False, band=albumObj)

    def view_metadata(self, root):
        self.currentState = "metadata"
        if root.ids["miscPanel"].children:
            root.ids["miscPanel"].clear_widgets()
        if self.localSong is not None:
            metadata_view = MetadataEditor()
            root.ids["miscPanel"].add_widget(metadata_view)

    def __play_music(self):

        self.currentSongDuration = self.sound.length

        # stop the sound if it's currently playing
        if self.sound.status != 'stop':
            self.sound.stop()
            Clock.unschedule(self.update_progress)
        else:
            self.sound.volume = self.volume
            self.sound.play()
            self.sound.seek(self.currentSongPosition)
            Clock.schedule_interval(self.update_progress, 0.5)

    def play_music(self, widget):
        try:
            self.__play_music()
        except:
            return
        if self.sound.status != 'stop':
            self.buttonStatus = 'Stop'
        else:
            self.buttonStatus = 'Play'

    def update_progress(self, dt):
        if self.sound.status == "stop":
            Clock.unschedule(self.update_progress)
            self.localSongPos += 1
            if self.localSongPos == len(self.playList):
                if self.repeatButtonStatus == "Stop rep.":
                    self.localSongPos = 0
                    self.load_song(None, self.playList[self.localSongPos], self.localSongPos)
                    self.__play_music()
                else:
                    self.localSongPos = 0
                    self.sound.stop()
                    self.buttonStatus = "Play"
                    self.load_song(None, self.playList[self.localSongPos], self.localSongPos)
            else:
                self.load_song(None, self.playList[self.localSongPos], self.localSongPos)
                self.__play_music()
        self.currentSongPosition = self.sound.get_pos()
        self.progressBarPosition = (self.currentSongPosition * 100.0)/self.currentSongDuration

    def set_volume(self, value):
        self.volume = value
        if self.sound:
            self.sound.volume = value

    def change_track(self, widget):
        self.localSong.track = widget.text

    def change_title(self, widget):
        self.localSong.title = widget.text

    def change_album(self, widget):
        self.localSong.album = widget.text

    def change_artist(self, widget):
        self.localSong.band = widget.text

    def save_metadata(self):
        changeFields(self.localSong.path,
                     self.localSong.track,
                     self.localSong.title,
                     self.localSong.album,
                     self.localSong.band)

    def change_repeat_status(self):
        if self.repeatButtonStatus == "Repeat":
            self.repeatButtonStatus = "Stop rep."
        else:
            self.repeatButtonStatus = "Repeat"

if __name__ == '__main__':
    MusicLibraryApp().run()


from Files import Files
import pytesseract as ps
import cv2 as cv2
import numpy as np

class Reciept:
    def __init__(self, path):
        original = cv2.imread(path)
        print(path.lstrip("/").split("-"))
        data = path.rstrip("/").split("-")

        self.store = str(data[0].split("/")[-1]).lower()
        self.date = data[1]
        self.time = data[2]
        self.payment = data[3].split(".")[0]
        print(self.store,self.date,self.time,self.payment)
        Image = self.showReciept(original)

        recieptString = ps.image_to_string(Image, lang="eng", config='--psm 1')

        self.itemList = self.getItems(recieptString, self.store)
        self.totalCost = self.getTotalCost(recieptString)

        file = open(f"/home/ryan/PycharmProjects/Reciept-Calculator/Reciepts/TextFiles/{self.store}-{self.date}-{self.time}-{self.payment}.txt", "w+")
        for item in self.itemList.keys():
            for i in range(0,int(self.itemList[item][0])):
                file.write(f"{item},{self.itemList[item][1]}\n")
        file.write(f"Total={self.totalCost}")

        # print(self.error(self.getCalculatedCost(), self.totalCost))

    def nothing(self):
        pass

    def showReciept(self, image):
        def nothing():
            pass
        cv2.namedWindow('Colorbars')
        cv2.moveWindow('Colorbars',700,100)
        Image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        cv2.imshow('Colorbars', Image)

        cv2.createTrackbar("hl", 'Colorbars', 100 , 255,lambda x:x)

        scale_percent = 40  # percent of original size
        width = int(Image.shape[1] * scale_percent / 100)
        height = int(Image.shape[0] * scale_percent / 100)
        dim = (width, height)
        # resize image

        while(1):
            low = cv2.getTrackbarPos("hl", 'Colorbars')
            (thresh, blackAndWhiteImage) = cv2.threshold(Image, low, 255, cv2.THRESH_BINARY)

            resized = cv2.resize(blackAndWhiteImage, dim, interpolation=cv2.INTER_AREA)

            cv2.imshow('Colorbars',np.array(resized, dtype = np.uint8 ))
            k = cv2.waitKey(1)
            # print(k)
            if k == 27:
                # print("true")
                break

        cv2.destroyAllWindows()

        return blackAndWhiteImage

    def getCalculatedCost(self):
        tc = 0
        for i in self.itemList.keys():
            tc += (float(self.itemList[i][0]) * float(self.itemList[i][1].strip("£")))
        print(f"£{tc}")
        return tc

    def error(self, calculated, true):
        return true - calculated

    def getDate(self, recieptString):
        pass

    def getTime(self, recieptString):
        pass

    def stripStrayCharacters(self,String):
        newString = String
        blacklist = [" x "," * "," « "," «x "]
        for i in blacklist:
            if i in newString:
                newString = newString.strip(i)
        return newString

    def checkSimilarities(self, itemName, dictKeys):
        # Things to consider:
        #   - World Length
        #   - Letter Frequency
        #   - Letter Distance
        similarites = {}
        for item in dictKeys:
            letterFrequency = {}
            for char in itemName: # add letters to dict
                letterFrequency[char] = 0
            for char in item:
                if char in letterFrequency.keys():
                    letterFrequency[char] += 1

            similarites[item] = sum(letterFrequency.values())/len(itemName)*100
            print(item + " " + str(letterFrequency))

        print(sum(letterFrequency.values()))
        # exit(0)
        vals = list(similarites.values())
        maxSimilarity = max(vals)
        closestItem = max(similarites, key=lambda key: similarites[key])
        print(similarites)
        # exit(0)
        return maxSimilarity, closestItem

    def checkDictionary(self, Store, ScannedItem, ScannedPrice):
        dict = Files().getItems(Store)
        ScannedItem = ''.join(ScannedItem.rstrip().lstrip())
        ScannedPrice = ''.join(ScannedPrice.rstrip().lstrip())

        # print("hi")
        TrueItem = ""
        TruePrice = ""

        # for item in dict:
        #     if item in ScannedItem or ScannedItem in item:
        #         ScannedItem = item

        if ScannedItem in dict.keys():
            return ''.join(ScannedItem.rstrip().lstrip()), dict[''.join(ScannedItem.rstrip().lstrip())]
        else:
            loop = True
            similar = self.checkSimilarities(ScannedItem,dict.keys()) #[0] is similarity score and [1] is closest item name
            if similar[0] > 80:
                print(f"I suspect that this item name {ScannedItem} is supposed to be {similar[1]}. is this correct?")
                response = input()
                if response == "yes":
                    TrueItem = similar[1]
                    TruePrice = dict[TrueItem]
                    loop = False
            while loop:
                print(
                    f"{''.join(ScannedItem.rstrip().lstrip())} does not appear to be stored in the dictionary, Would you like to add it? (yes/rename/skip)")
                response = input()
                TrueItem = ScannedItem
                TruePrice = ScannedPrice
                if response == "skip":
                    TrueItem = -1
                    TruePrice = -1
                    break
                if response == "rename":
                        print("Please enter the name of the item.")
                        TrueItem = input()
                        if TrueItem in dict.keys():
                            print("Item is already in dictionary")
                            TruePrice = dict[TrueItem]
                            break
                        response = "yes"

                if response == "yes":
                    print(f"Adding {TrueItem} to the dictionary, is {ScannedPrice} the correct price?")
                    response = input()
                    if response == "yes":
                        print(f"Successfully added {TrueItem} to the dictionary with price {ScannedPrice}")
                        loop = False
                    elif response == "no":
                        print("Please enter the correct price")
                        TruePrice = f"£{input()}"
                        print(f"Successfully added {TrueItem} to the dictionary with price {TruePrice}")
                        loop = False
                else:
                    print("invalid response, please try again.")
            if TrueItem != -1 and TrueItem != -1:
                Files().addItem(Store, TrueItem, TruePrice)

        # print(TrueItem,TruePrice)
        return TrueItem, TruePrice

    def getItems(self, recieptString, Store):
        nameBuffer = ""
        priceBuffer = ""
        Items = {}
        stage = "Name"
        atException = False
        for i in recieptString:
            # print(priceBuffer)
            if stage == "Name":
                if i == "@":
                    atException = True
                if "SUB-TOTAL" in nameBuffer:
                    return Items

                if "\n" in nameBuffer and nameBuffer != "" and len(priceBuffer) > 0:
                    # print(nameBuffer)
                    priceBuffer = ""
                    nameBuffer = "".join(i)
                    stage = "Name"
                elif i == "£" or i == "€" or i == "$":
                    if not atException:
                        stage = "Price"
                    else:
                        atException = False
                else:
                    nameBuffer += i
            if stage == "Price" and "\n" not in priceBuffer:
                priceBuffer += i
            if "\n" in priceBuffer:
                # print(r' '.join(nameBuffer.strip("\n").split()))
                nameBuffer = self.stripStrayCharacters(nameBuffer)
                nameBuffer, priceBuffer = self.checkDictionary(Store, ' '.join(nameBuffer.strip("\n").split()),
                                                               priceBuffer.strip("\n"))
                if nameBuffer != -1 and priceBuffer != -1:
                    if nameBuffer not in Items.keys():
                        Items[nameBuffer] = [1, priceBuffer.strip("\n")]
                    else:
                        # print(Items[nameBuffer][0])
                        Items[nameBuffer] = [str(int(Items[nameBuffer][0]) + 1), Items[nameBuffer][1]]

                # Items[nameBuffer] = priceBuffer.strip("\n")
                priceBuffer = ""
                nameBuffer = "".join(i)
                stage = "Name"

        # print(Items)
        return Items

    def getCardNumber(self, recieptString):
        buffer = ""
        Number = ""
        for i in recieptString:
            buffer += i
            if "NUMBER" in buffer and len("".join(x for x in Number if x.isdigit())) < 4:
                # print(i)
                Number += i
        # print(buffer)
        # print(Number)
        if not Number.isdigit():
            print("Cannot Read Card Number, Please Enter Correct Number:")
            Number = input()
        # print(Number)
        return Number

    def getTotalCost(self, recieptString):
        buffer = ""
        totalCost = ""
        for i in recieptString:
            buffer += i
            if "VISA DEBIT SALE" in buffer and len(totalCost) < 6 and ("£" in totalCost or i == "£"):
                totalCost += i
        # print(buffer)
        print(totalCost)
        return totalCost.strip("£")


import telebot

from config import TOKEN, keys
from extension import Converter, ConvertionException

bot = telebot.TeleBot(TOKEN)

@bot.message_handler(commands=['start', 'help'])
def send_welcome(message: telebot.types.Message):
    bot.send_message(message.chat.id, f'{message.chat.username}, write the currency You want to exchange in \
    "currency1 currency2 quantity" type \n To watch the list of currencies write "/values"')

@bot.message_handler(commands=['values'])
def values(message: telebot.types.Message):
    text = 'List of currencies:'
    for key in keys.keys():
        text = '\n'.join((text, key))
    bot.send_message(message.chat.id, text)

@bot.message_handler(content_types=['text'])
def convert(message: telebot.types.Message):

    values = message.text.split(' ')
    values = list(map(str.lower, values))

    try:
        result = Converter.get_price(values)
    except ConvertionException as e:
        bot.reply_to(message, f"User's error \n{e}")
    except Exception as e:
        bot.reply_to(message, f"Can't handle command \n{e}")
    else:
        bot.send_message(message.chat.id, result)

bot.polling(none_stop=True)


from ape_safe import ApeSafe
from brownie import Contract, accounts, network

from helpers import (
    CHAIN_IDS,
    ENG_EOA_ADDRESS,
    MULTISIG_ADDRESSES,
    SDL_ADDRESSES,
    SDL_DAO_COMMUNITY_VESTING_PROXY_ADDRESS,
    SDL_MINTER_ADDRESS
)
from scripts.utils import confirm_posting_transaction

TARGET_NETWORK = "MAINNET"

def main():
    """
    Send SDL to fund minichef rewards from results of snapshot vote
    Vote: https://snapshot.org/#/saddlefinance.eth/proposal/0x508226028d3a401df05f84aa4f6292f5dde8db55d5aa10804f73794a02dbf5f0
    """

    print(f"You are using the '{network.show_active()}' network")
    assert network.chain.id == CHAIN_IDS[TARGET_NETWORK], f"Not on {TARGET_NETWORK}"

multisig = ApeSafe(MULTISIG_ADDRESSES[CHAIN_IDS[TARGET_NETWORK]])

sdl = multisig.get_contract(SDL_ADDRESSES[CHAIN_IDS[TARGET_NETWORK]])

sdl_vesting_contract_proxy = multisig.get_contract(
    SDL_DAO_COMMUNITY_VESTING_PROXY_ADDRESS[CHAIN_IDS[TARGET_NETWORK]]
)

# Claim SDL from vesting contract
sdl_vesting_contract_proxy.release()

# Send needed SDL to EOA for bridging to minichefs
arbitrum_minichef_debt = 50077104116351310467768  # SDL owed to arbitrum
optimism_minichef_debt = 75042837369470445854120  # SDL owed to optimism
sidechain_minichef_debt = (
    arbitrum_minichef_debt + optimism_minichef_debt
)

sdl_balance = sdl.balanceOf(MULTISIG_ADDRESSES[CHAIN_IDS["MAINNET"]])
sdl.transfer(
    ENG_EOA_ADDRESS,
    sidechain_minichef_debt,
    {"from": multisig.address}
)

# Send 1.3m SDL to minter
sdl_to_minter = 1_300_000 * 1e18

sdl.transfer(
    SDL_MINTER_ADDRESS[CHAIN_IDS[TARGET_NETWORK]],
    sdl_to_minter,
    {"from": multisig.address}
)
assert sdl_balance > sdl.balanceOf(
    MULTISIG_ADDRESSES[CHAIN_IDS["MAINNET"]]
), "SDL not sent to EOA"

# combine history into multisend txn
safe_tx = multisig.multisend_from_receipts()
safe_tx.safe_nonce = 84

# sign with private key
safe_tx.sign(accounts.load("deployer").private_key)
multisig.preview(safe_tx)

confirm_posting_transaction(multisig, safe_tx)


from yunobuiltin import if_let, get, assoc, is_iterable
from .utils import merge, escape_query_string_characters

# going to shadow some builtins
builtin_range = range
builtin_bool = bool
builtin_filter = filter

# query and filter take single expressions. to make them take multiple
# expressions use and/or or bool
# query(term('foo','bar')) => {'query': {'term': {'foo': 'bar'}}}
query = lambda q: {"query": q}
# filter(term('foo','bar')) => {'filter': {'term': {'foo': 'bar'}}}
filter = lambda q: {"filter": q}

size = lambda s: {"size": s}
limit = size
from_ = lambda f: {"from": f}
offset = from_
sort = lambda *fields: {"sort": fields}

def term(key, values, **options):
    op = 'term'
    if is_iterable(values) and not isinstance(values, basestring):
        op = 'terms'

    if op == 'terms':
        return {op: merge({key: values}, options)}
    else:
        return {op: {key: merge({"value": values}, options)}}

range = lambda k, **opts: {"range": {k: opts}}
match = lambda f, q, **opts: {"match": {f: merge({"query": q}, opts)}}
bool = lambda **opts: {"bool": opts}
boosting = lambda **opts: {"boosting": opts}
ids = lambda type, ids: {"ids": {"type": type, "values": ids}}
constant_score = lambda **opts: {"constant_score": opts}
dis_max = lambda **opts: {"dis_max": opts}
prefix = lambda **opts: {"prefix": opts}
filtered = lambda **opts: {"filtered": opts}
flt = fuzzy_like_this = lambda **opts: {"fuzzy_like_this": opts}
flt_field = fuzzy_like_this_field = lambda **opts: {"fuzzy_like_this_field": opts}
fuzzy = lambda **opts: {"fuzzy": opts}
match_all = lambda **opts: {"match_all": opts if opts else {}}
multi_match = lambda **opts: {"multi_match": opts}
mlt = more_like_this = lambda **opts: {"more_like_this": opts}
mlt_field = more_like_this_field = lambda **opts: {"more_like_this_field": opts}

def query_string(escape_fn=None, **options):
    if escape_fn is None:
        escape_fn = escape_query_string_characters

    options = if_let(get(options, "query"),
                     lambda q: assoc(options, "query", escape_fn(q)),
                     options)

    return {"query_string": options}

query_string_unescaped = lambda **opts: {'query_string': opts}
simple_query_string = lambda **opts: {'simple_query_string': opts}

span_first = lambda **opts: {"span_first": opts}
span_near = lambda **opts: {"span_near": opts}
span_not = lambda **opts: {"span_not": opts}
span_or = lambda **opts: {"span_or": opts}
span_term = lambda **opts: {"span_term": opts}
wildcard = lambda **opts: {"wildcard": opts}
indices = lambda **opts: {"indices": opts}
has_child = lambda **opts: {"has_child": opts}
custom_filters_score = lambda **opts: {"custom_filters_score": opts}
function_score = lambda **opts: {"function_score": opts}
top_children = lambda **opts: {"top_children": opts}
nested = lambda **opts: {"nested": opts}
common = lambda **opts: {"common": opts}
exists = lambda f: {"exists": {"field": f}}