import sys # argv
import math # inf
import itertools # permutations

from typing import Tuple, List, Iterable

OP_NUM_PARAMS = {
    99: 0,
    1: 3,
    2: 3,
    3: 1,
    4: 1,
    5: 2,
    6: 2,
    7: 3,
    8: 3,
}

def parse_op_value(op_value: int) -> Tuple[int, List[int]]:
    op = op_value % 100
    if op not in OP_NUM_PARAMS:
        raise Exception(f'Invalid op: {op}')
    op_value //= 100
    param_modes = []
    for _ in range(OP_NUM_PARAMS[op]):
        mode = op_value % 10
        assert mode in [0, 1]
        param_modes.append(mode)
        op_value //= 10
    return op, param_modes

def run_program(memory: List[int], input_values: Iterable[int]) -> List[int]:
    memory = memory.copy()
    input_values = iter(input_values)

    output_values = []
    ip = 0
    while True:
        op, param_modes = parse_op_value(memory[ip])
        if op == 99:
            break

        def get_param(n: int) -> int:
            param_value = memory[ip+n]
            if param_modes[n-1] == 0:
                return memory[param_value]
            else:
                return param_value

        def set_param(n: int, value: int):
            param_value = memory[ip+n]
            if param_modes[n-1] == 0:
                memory[param_value] = value
            else:
                raise Exception('Attempted to write to immediate parameter')

        if op == 1:
            result = get_param(1) + get_param(2)
            set_param(3, result)
        elif op == 2:
            result = get_param(1) * get_param(2)
            set_param(3, result)
        elif op == 3:
            set_param(1, next(input_values))
        elif op == 4:
            output_values.append(get_param(1))
        elif op == 5:
            if get_param(1) != 0:
                ip = get_param(2)
                continue
        elif op == 6:
            if get_param(1) == 0:
                ip = get_param(2)
                continue
        elif op == 7:
            result = get_param(1) < get_param(2)
            set_param(3, int(result))
        elif op == 8:
            result = get_param(1) == get_param(2)
            set_param(3, int(result))
        else:
            raise Exception(f'Invalid op: {op}')

        ip += 1 + len(param_modes)

    try:
        next(input_values)
    except StopIteration:
        return output_values
    else:
        raise Exception("Program didn't use all input values")

with open(sys.argv[1]) as f:
    memory = [int(value) for value in f.read().split(',')]

largest_output = -math.inf
largest_output_config = None
for phase_config in itertools.permutations(range(0, 5)):
    output_value = 0
    for phase in phase_config:
        output_value, = run_program(memory, [phase, output_value])
    if output_value > largest_output:
        largest_output = output_value
        largest_output_config = phase_config
#print(largest_output_config)
print(largest_output)


"""
PBF File Downloader.

This module contains a downloader capable of downloading a PBF files from multiple sources.
"""
import json
import time
import warnings
from pathlib import Path
from time import sleep
from typing import Any, Callable, Dict, Hashable, List, Literal, Sequence, Union

import geopandas as gpd
import requests
import topojson as tp
from requests import HTTPError
from shapely.geometry import Polygon, mapping
from shapely.geometry.base import BaseGeometry, BaseMultipartGeometry
from tqdm import tqdm

from srai.constants import WGS84_CRS
from srai.geometry import (
    buffer_geometry,
    flatten_geometry,
    flatten_geometry_series,
    get_geometry_hash,
    remove_interiors,
)
from srai.loaders import download_file
from srai.loaders.osm_loaders.openstreetmap_extracts import (
    OpenStreetMapExtract,
    find_smallest_containing_geofabrik_extracts,
    find_smallest_containing_openstreetmap_fr_extracts,
)
from srai.loaders.osm_loaders.pbf_file_clipper import PbfFileClipper

PbfSourceLiteral = Literal["geofabrik", "openstreetmap_fr", "protomaps"]
PbfSourceExtractsFunctions: Dict[
    PbfSourceLiteral,
    Callable[[Union[BaseGeometry, BaseMultipartGeometry]], List[OpenStreetMapExtract]],
] = {
    "geofabrik": find_smallest_containing_geofabrik_extracts,
    "openstreetmap_fr": find_smallest_containing_openstreetmap_fr_extracts,
}

class PbfFileDownloader:
    """
    PbfFileDownloader.

    PBF(Protocolbuffer Binary Format)[1] file downloader is a downloader
    capable of downloading `*.osm.pbf` files with OSM data for a given area.

    This downloader can use multiple sources to extract a PBF file for a given region:
     - Geofabrik - free hosting service with PBF files http://download.geofabrik.de/.
     - OpenStreetMap.fr - free hosting service with PBF files https://download.openstreetmap.fr/.
     - Protomaps - (will be deprecated!) free download service for downloading an extract for
       an area of interest.

    References:
        1. https://wiki.openstreetmap.org/wiki/PBF_Format
    """

    PROTOMAPS_API_START_URL = "https://app.protomaps.com/downloads/osm"
    PROTOMAPS_API_DOWNLOAD_URL = "https://app.protomaps.com/downloads/{}/download"
    PROTOMAPS_MAX_WAIT_TIME_S = 300  # max 5 minutes for protomaps to generate an extract

    _PBAR_FORMAT = "[{}] Downloading pbf file #{} ({})"

    SIMPLIFICATION_TOLERANCE_VALUES = [
        1e-07,
        2e-07,
        5e-07,
        1e-06,
        2e-06,
        5e-06,
        1e-05,
        2e-05,
        5e-05,
        0.0001,
        0.0002,
        0.0005,
        0.001,
        0.002,
        0.005,
        0.01,
        0.02,
        0.05,
    ]

    def __init__(
        self,
        download_source: PbfSourceLiteral = "protomaps",
        download_directory: Union[str, Path] = "files",
        switch_to_geofabrik_on_error: bool = True,
    ) -> None:
        """
        Initialize PbfFileDownloader.

        Args:
            download_source (PbfSourceLiteral, optional): Source to use when downloading PBF files.
                Can be one of: `geofabrik`, `openstreetmap_fr`, `protomaps`.
                Defaults to "protomaps".
            download_directory (Union[str, Path], optional): Directory where to save
                the downloaded `*.osm.pbf` files. Defaults to "files".
            switch_to_geofabrik_on_error (bool, optional): Flag whether to automatically
                switch `download_source` to 'geofabrik' if error occures. Defaults to `True`.
        """
        self.download_source = download_source
        self.download_directory = download_directory
        self.clipper = PbfFileClipper(working_directory=self.download_directory)
        self.switch_to_geofabrik_on_error = switch_to_geofabrik_on_error

    def download_pbf_files_for_regions_gdf(
        self, regions_gdf: gpd.GeoDataFrame
    ) -> Dict[Hashable, Sequence[Path]]:
        """
        Download PBF files for regions GeoDataFrame.

        Function will split each multipolygon into single polygons and download PBF files
        for each of them.

        Args:
            regions_gdf (gpd.GeoDataFrame): Region indexes and geometries.

        Raises:
            ValueError: If provided geometries aren't shapely.geometry.Polygons.

        Returns:
            Dict[Hashable, Sequence[Path]]: List of Paths to downloaded PBF files per
                each region_id.
        """
        regions_mapping: Dict[Hashable, Sequence[Path]] = {}

        non_polygon_types = set(
            type(geometry)
            for geometry in flatten_geometry_series(regions_gdf.geometry)
            if not isinstance(geometry, Polygon)
        )
        if non_polygon_types:
            raise ValueError(f"Provided geometries aren't Polygons (found: {non_polygon_types})")

        try:
            if self.download_source == "protomaps":
                regions_mapping = self._download_pbf_files_for_polygons_from_protomaps(regions_gdf)
            elif self.download_source in PbfSourceExtractsFunctions:
                regions_mapping = self._download_pbf_files_for_polygons_from_existing_extracts(
                    regions_gdf
                )
        except Exception as err:
            if self.download_source != "geofabrik" and self.switch_to_geofabrik_on_error:
                warnings.warn(
                    f"Error occured ({err}). Auto-switching to 'geofabrik' download source.",
                    stacklevel=1,
                )
                regions_mapping = self._download_pbf_files_for_polygons_from_existing_extracts(
                    regions_gdf, override_to_geofabrik=True
                )
            else:
                error_message = str(err)
                if self.download_source != "geofabrik":
                    error_message += (
                        "\nPlease change the 'download_source' to"
                        " 'geofabrik' or other availablesource:\n"
                        " PbfDownloader(download_source='geofabrik', ...) or"
                        " OsmPbfLoader(download_source='geofabrik', ...)."
                    )
                raise RuntimeError(error_message) from err

        return regions_mapping

    def _download_pbf_files_for_polygons_from_existing_extracts(
        self, regions_gdf: gpd.GeoDataFrame, override_to_geofabrik: bool = False
    ) -> Dict[Hashable, Sequence[Path]]:
        regions_mapping: Dict[Hashable, Sequence[Path]] = {}

        unary_union_geometry = regions_gdf.geometry.unary_union

        if override_to_geofabrik:
            extract_function = PbfSourceExtractsFunctions["geofabrik"]
        else:
            extract_function = PbfSourceExtractsFunctions[self.download_source]
        extracts = extract_function(unary_union_geometry)

        downloaded_pbf_files = []

        for extract in extracts:
            pbf_file_path = Path(self.download_directory).resolve() / f"{extract.id}.osm.pbf"

            download_file(url=extract.url, fname=pbf_file_path.as_posix(), force_download=False)

            downloaded_pbf_files.append(pbf_file_path)

        polygons = flatten_geometry(unary_union_geometry)

        for region_id, row in regions_gdf.iterrows():
            polygons = flatten_geometry(row.geometry)
            regions_mapping[region_id] = [
                self.clipper.clip_pbf_file(polygon, downloaded_pbf_files) for polygon in polygons
            ]

        return regions_mapping

    def _download_pbf_files_for_polygons_from_protomaps(
        self, regions_gdf: gpd.GeoDataFrame
    ) -> Dict[Hashable, Sequence[Path]]:
        regions_mapping: Dict[Hashable, Sequence[Path]] = {}

        for region_id, row in regions_gdf.iterrows():
            polygons = flatten_geometry(row.geometry)
            regions_mapping[region_id] = [
                self._download_pbf_file_for_polygon_from_protomaps(
                    polygon, region_id, polygon_id + 1
                )
                for polygon_id, polygon in enumerate(polygons)
            ]

        return regions_mapping

    def _download_pbf_file_for_polygon_from_protomaps(
        self, polygon: Polygon, region_id: str = "OSM", polygon_id: int = 1
    ) -> Path:
        """
        Download PBF file for a single Polygon.

        Function will buffer polygon by 50 meters, simplify exterior boundary to be
        below 1000 points (which is a limit of Protomaps API) and close all holes within it.

        Boundary of the polygon will be sent to Protomaps service and an `*.osm.pbf` file
        will be downloaded with a hash based on WKT representation of the parsed polygon.
        If file exists, it won't be downloaded again.

        Args:
            polygon (Polygon): Polygon boundary of an area to be extracted.
            region_id (str, optional): Region name to be set in progress bar.
                Defaults to "OSM".
            polygon_id (int, optional): Polygon number to be set in progress bar.
                Defaults to 1.

        Returns:
            Path: Path to a downloaded `*.osm.pbf` file.
        """
        geometry_hash = get_geometry_hash(polygon)
        pbf_file_path = Path(self.download_directory).resolve() / f"{geometry_hash}.osm.pbf"

        if not pbf_file_path.exists():  # pragma: no cover
            boundary_polygon = self._prepare_polygon_for_download(polygon)
            geometry_geojson = mapping(boundary_polygon)

            s = requests.Session()

            req = s.get(url=self.PROTOMAPS_API_START_URL)

            csrf_token = req.cookies["csrftoken"]
            headers = {
                "Referer": self.PROTOMAPS_API_START_URL,
                "Cookie": f"csrftoken={csrf_token}",
                "X-CSRFToken": csrf_token,
                "Content-Type": "application/json; charset=utf-8",
                "User-Agent": "SRAI Python package (https://github.com/kraina-ai/srai)",
            }
            request_payload = {
                "region": {"type": "geojson", "data": geometry_geojson},
                "name": geometry_hash,
            }

            start_extract_request = s.post(
                url=self.PROTOMAPS_API_START_URL,
                json=request_payload,
                headers=headers,
                cookies=dict(csrftoken=csrf_token),
            )
            start_extract_request.raise_for_status()

            start_extract_result = start_extract_request.json()
            try:
                extraction_uuid = start_extract_result["uuid"]
                status_check_url = start_extract_result["url"]
            except KeyError as err:
                error_message = (
                    f"Error from the 'Protomaps' service: {json.dumps(start_extract_result)}."
                )
                raise RuntimeError(error_message) from err

            with tqdm() as pbar:
                status_response: Dict[str, Any] = {}
                cells_total = 0
                nodes_total = 0
                elems_total = 0
                start_time = time.time()
                while not status_response.get("Complete", False):
                    sleep(0.5)
                    status_response = s.get(url=status_check_url).json()
                    cells_total = max(cells_total, status_response.get("CellsTotal", 0))
                    nodes_total = max(nodes_total, status_response.get("NodesTotal", 0))
                    elems_total = max(elems_total, status_response.get("ElemsTotal", 0))

                    cells_prog = status_response.get("CellsProg", None)
                    nodes_prog = status_response.get("NodesProg", None)
                    elems_prog = status_response.get("ElemsProg", None)

                    if cells_total > 0 and cells_prog is not None and cells_prog < cells_total:
                        pbar.set_description(
                            self._PBAR_FORMAT.format(region_id, polygon_id, "Cells")
                        )
                        pbar.total = cells_total + nodes_total + elems_total
                        pbar.n = cells_prog
                    elif nodes_total > 0 and nodes_prog is not None and nodes_prog < nodes_total:
                        pbar.set_description(
                            self._PBAR_FORMAT.format(region_id, polygon_id, "Nodes")
                        )
                        pbar.total = cells_total + nodes_total + elems_total
                        pbar.n = cells_total + nodes_prog
                    elif elems_total > 0 and elems_prog is not None and elems_prog < elems_total:
                        pbar.set_description(
                            self._PBAR_FORMAT.format(region_id, polygon_id, "Elements")
                        )
                        pbar.total = cells_total + nodes_total + elems_total
                        pbar.n = cells_total + nodes_total + elems_prog
                    else:
                        pbar.total = cells_total + nodes_total + elems_total
                        pbar.n = cells_total + nodes_total + elems_total

                    pbar.refresh()

                    if (time.time() - start_time) > self.PROTOMAPS_MAX_WAIT_TIME_S:
                        error_message = (
                            "'Protomaps' service took too long to generate an extract"
                            f" ({self.PROTOMAPS_MAX_WAIT_TIME_S}s)."
                        )
                        raise RuntimeError(error_message)

            try:
                download_file(
                    url=self.PROTOMAPS_API_DOWNLOAD_URL.format(extraction_uuid),
                    fname=pbf_file_path.as_posix(),
                )
            except HTTPError as err:
                error_message = f"Error from the 'Protomaps' service: {err.response}."
                raise RuntimeError(error_message) from err

        return pbf_file_path

    def _prepare_polygon_for_download(self, polygon: Polygon) -> Polygon:
        """
        Prepare polygon for download.

        Function buffers the polygon, closes internal holes and simplifies its boundary to 1000
        points.

        Makes sure that the generated polygon with fully cover the original one by increasing the
        buffer size incrementally. Buffering is applied to the last simplified geometry to speed up
        the process.
        """
        is_fully_covered = False
        buffer_size_meters = 50

        polygon_to_buffer = polygon

        while not is_fully_covered:
            buffered_polygon = buffer_geometry(polygon_to_buffer, meters=buffer_size_meters)
            simplified_polygon = self._simplify_polygon(buffered_polygon, 1000)
            closed_polygon = remove_interiors(simplified_polygon)
            is_fully_covered = polygon.covered_by(closed_polygon)
            buffer_size_meters += 50

            polygon_to_buffer = closed_polygon

        return closed_polygon

    def _simplify_polygon(self, polygon: Polygon, exterior_max_points: int = 1000) -> Polygon:
        """Simplify a polygon boundary to up to provided number of points."""
        simplified_polygon = polygon

        for simplify_tolerance in self.SIMPLIFICATION_TOLERANCE_VALUES:
            simplified_polygon = (
                tp.Topology(
                    polygon,
                    toposimplify=simplify_tolerance,
                    prevent_oversimplify=True,
                )
                .to_gdf(winding_order="CW_CCW", crs=WGS84_CRS, validate=True)
                .geometry[0]
            )

            if len(simplified_polygon.exterior.coords) < exterior_max_points:
                break

        if len(simplified_polygon.exterior.coords) > exterior_max_points:
            simplified_polygon = polygon.convex_hull

        if len(simplified_polygon.exterior.coords) > exterior_max_points:
            simplified_polygon = polygon.minimum_rotated_rectangle

        return simplified_polygon


from __future__ import annotations

from typing import Optional

from sqlalchemy import ForeignKey
from sqlalchemy.inspection import inspect
from sqlalchemy.orm import relationship
from sqlmodel import Field, Relationship, SQLModel, create_engine

class Base(SQLModel):
    __repr_exclude__ = None
    __args_memo__ = None

    def __repr_args__(self) -> list[Tuple[str, Any]]:
        cls = type(self)
        if cls.__args_memo__ is None:
            if cls.__repr_exclude__ is None:
                rels = inspect(cls).relationships.keys()
                fks = [fk.parent.name.rstrip("_id") for fk in cls.__table__.foreign_keys]
                cls.__repr_exclude__ = [k for k in rels if k in fks]
            cls.__args_memo__ = [(k, v) for k, v in super().__repr_args__() if k not in cls.__repr_exclude__]
        return cls.__args_memo__

class Caca(Base, table=True):
    __tablename__ = "cacas"
    id: Optional[int] = Field(default=None, primary_key=True, index=True)
    description: str = Field(index=True)
    shengbing_id: int = Field(default=None, foreign_key="shengbings.id")
    shengbing: ShengBing = Relationship(
        back_populates="cacas", sa_relationship_kwargs={"uselist": False}
    )

class ShengBing(Base, table=True):
    __tablename__ = "shengbings"
    id: Optional[int] = Field(default=None, primary_key=True, index=True)
    severity: str = Field(index=True)
    cacas: list[Caca] = Relationship(back_populates="shengbing")
    erzi: ErZi = Relationship(
        back_populates="shengbing", sa_relationship_kwargs={"uselist": False}
    )

class ErZi(Base, table=True):
    __tablename__ = "erzis"
    id: Optional[int] = Field(default=None, primary_key=True, index=True)
    name: str = Field(index=True)
    shengbing_id: int = Field(default=None, foreign_key="shengbings.id")
    shengbing: ShengBing = Relationship(
        back_populates="erzi", sa_relationship_kwargs={"uselist": False}
    )

engine = create_engine("sqlite:///example.db")

if __name__ == "__main__":
    parser = ArgumentParser()
    parser.add_argument("-c", "--create", action="store_true")
    args = parser.parse_args()

    if args.create:
        Base.metadata.create_all(engine)


'''
Created on May 24, 2019

@author: hzhang0418
'''

import os
import sys
import random

import pandas as pd

import v6.data_io
import v6.label_debugger
import v6.feature_selection

import utils.myconfig

def run(detector='both', confusion=False, top_k=20):
    config_file=r'/scratch/hzhang0418/projects/datasets/mono/beer.config'
    #config_file=r'/scratch/hzhang0418/projects/datasets/mono/bike.config'
    #config_file=r'/scratch/hzhang0418/projects/datasets/mono/books1.config'
    #config_file=r'/scratch/hzhang0418/projects/datasets/mono/citations.config'
    #config_file=r'/scratch/hzhang0418/projects/datasets/mono/movies1.config'
    #config_file=r'/scratch/hzhang0418/projects/datasets/mono/restaurants4.config'

    #config_file = r'/scratch/hzhang0418/projects/datasets/labeldebugger/fodors_zagats.config'

    #config_file=r'/scratch/hzhang0418/projects/datasets/mono/cora_large.config'
    #config_file = r'/scratch/hzhang0418/projects/datasets/labeldebugger/cora_new.config'

    #mayuresh
    config_file=r'/export/da/mkunjir/LabelDebugger/config/cora.config'

    params = utils.myconfig.read_config(config_file)

    basedir = params['basedir']
    hpath = os.path.join(basedir, params['hpath'])
    gpath = os.path.join(basedir, 'golden.csv')

    exclude_attrs = ['_id', 'ltable.id', 'rtable.id']

    features, labels, pair2index, index2pair = v6.data_io.read_feature_file(hpath, exclude_attrs)
    pair2golden = v6.data_io.read_golden_label_file(gpath) if os.path.exists(gpath) else {p:labels[i] for p, i in pair2index.items()} ## mayuresh: if no golden labels, then only new errors will be injected

    params['fs_alg'] = 'xgboost'
    params['max_list_len'] = 40
    params['detectors'] = detector
    params['confusion'] = confusion

    params['num_cores'] = 4
    params['num_folds'] = 5

    params['min_con_dim'] = 1
    params['counting_only'] = False
    #debugger = v6.label_debugger.LabelDebugger(features, labels, params)

    # label errors
    all_errors = []
    false_nonmatch_errors = 0
    false_nonmatch_indices = []
    for index, p in index2pair.items():
        if labels[index]!=pair2golden[p]:
            #print('Error found at: ', index, ' the pair is: ', p)
            all_errors.append(index)
            if labels[index]==0:
               false_nonmatch_errors += 1
               false_nonmatch_indices.append(index)

    '''
    # TEMP: mayuresh: eliminate errors from labeled data
    #mpath = os.path.join(basedir, 'labeled_data.csv')
    new_hpath = os.path.join(basedir, 'feature_vector_newerrors.csv')
    new_gpath = os.path.join(basedir, 'golden_newerrors.csv')
    fv_df = pd.read_csv(hpath)
    g_df = pd.read_csv(gpath)
    #fv_df.drop(fv_df.index[all_errors], inplace=True)
    #fv_df.to_csv(new_hpath, index=False)
    #exit()
    '''

    # randomly insert errors
    seed = 0 #random.randrange(sys.maxsize)
    rng = random.Random(seed)
    print("Seed was:", seed)

    perc = 0.1 #rng.randint(5, 15)/100.0
    print("Error rate:", perc)

    num_err = int(len(labels)*perc - len(all_errors)) # accounting for existing errors
    #num_err = min(800, num_err) # 800 is the maximum errors we can check in the given budget

    while num_err < 0: ## correct some existing errors to make them equal to the required number
      i = 0#rng.randint(0, len(all_errors)-1)
      index = all_errors[i]
      labels[index] = 1 - labels[index]
      if labels[index]==1:
         false_nonmatch_errors -= 1
         false_nonmatch_indices.remove(index)
      all_errors.remove(index)
      num_err += 1

    error_indices = set(all_errors)
    new_error_indices = set()
    for _ in range(num_err*10):
        index = rng.randint(0, len(labels)-1)
        if index in error_indices or index in new_error_indices:
            continue
        new_error_indices.add(index)
        if labels[index]==1:
            false_nonmatch_errors += 1
            false_nonmatch_indices.append(index)
        labels[index] = 0 if labels[index]==1 else 1
        if len(new_error_indices)>=num_err:
            break

    '''
    ## mayuresh: update feature vector with new errors and new golden labels
    print('New errors inserted: ', len(new_error_indices))
    #g_df = g_df.iloc[0:0] # dropping current golden labels
    for cnt, index in enumerate(new_error_indices):
      pair = index2pair[index]
      fv_df.at[index, 'label'] = labels[index]
      #print('Changed fv location: ', index)
      g_df.at[index, 'golden'] = 1-labels[index] # = g_df.append({'_id':cnt, 'ltable.id':pair[0], 'rtable.id':pair[1], 'golden':1-labels[index]}, ignore_index=True)
    g_df.drop(fv_df.index[all_errors], inplace=True) #delete old errors
    g_df.to_csv(new_gpath, index=False)
    fv_df.drop(fv_df.index[all_errors], inplace=True) #delete old errors
    fv_df.to_csv(new_hpath, index=False)
    exit()
    '''

    print("Total number of errors: ", len(error_indices) + len(new_error_indices))
    print("Of which total false nonmatches: ", false_nonmatch_errors)
    all_errors = list(error_indices)
    all_errors.extend(list(new_error_indices))
    false_match_errors = len(all_errors) - false_nonmatch_errors

    print(params['dataset_name'])

    #index = 324
    #print( labels[index], pair2golden[index2pair[index]] )

    ## mayuresh: read selected features if available in file or else store them
    #del params['spath']
    if 'spath' in params:
       selected_features_path = os.path.join(basedir, params['spath'])
       selected_features = pd.read_csv(selected_features_path).to_numpy()
    else:
       selected_features = v6.feature_selection.select_features(features, labels, params['fs_alg'])
       selected_features_path = os.path.join(basedir, 'selected_features.csv')
       pd.DataFrame(selected_features).to_csv(selected_features_path, index=False)
    print('Selected features of dim: ', selected_features.shape, ' from the original features: ', features.shape)

    params['fs_alg'] = 'none' # disabling feature selection now that we are done selecting
    debugger = v6.label_debugger.LabelDebugger(selected_features, labels, params)

    all_detected_errors, detected_false_nonmatches = debug_labels(debugger, index2pair, pair2golden, top_k)

    print("Total number of label errors: ", len(all_errors))
    print("Number of iterations: ", debugger.iter_count)
    print("Number of checked pairs: ", len(debugger.verified_indices))
    print("Number of detected errors: ", len(all_detected_errors))
    print("Of which the number of false nonmatches: ", len(detected_false_nonmatches))
    print("False match detection recall: ", 100.0 * (len(all_detected_errors) - len(detected_false_nonmatches)) / false_match_errors)
    print("False non-match detection recall: ", 100.0 * len(detected_false_nonmatches) / false_nonmatch_errors)
    print("False match detection prec: ", 100.0 * (len(all_detected_errors) - len(detected_false_nonmatches)) / (len(debugger.verified_indices) * false_match_errors / len(all_errors)))
    #print("False nonmatch detection prec: ", 100.0 * (len(detected_false_nonmatches)) / (len(debugger.verified_indices) * false_nonmatch_errors / len(all_errors)))

    apath = os.path.join(basedir, params['apath'])
    bpath = os.path.join(basedir, params['bpath'])

    table_A = pd.read_csv(apath)
    table_B = pd.read_csv(bpath)

    '''
    #show all errors
    table_A['id'] = table_A['id'].astype(str)
    table_B['id'] = table_B['id'].astype(str)
    all_error_pairs = []
    for index in all_errors:
        p = index2pair[index]
        label = labels[index]
        left = table_A.loc[ table_A['id'] == str(p[0])]
        right = table_B.loc[ table_B['id'] == str(p[1])]
        tmp = {}
        for col in left:
            tmp['ltable.'+col] = left.iloc[0][col]
        for col in right:
            tmp['rtable.'+col] = right.iloc[0][col]
            tmp['label'] = label
        all_error_pairs.append(tmp)

    if len(all_error_pairs)>0:
        df = pd.DataFrame(all_error_pairs)
        output_file = params['dataset_name']+'_all_errors.csv'
        df.to_csv(output_file, index=False)

    # show missed errors
    missed_error_pairs = []
    for index in all_errors:
        if index not in all_detected_errors:
            p = index2pair[index]
            label = labels[index]
            left = table_A.loc[ table_A['id'] == int(p[0])]
            right = table_B.loc[ table_B['id'] == int(p[1])]
            tmp = {}
            for col in left:
                tmp['ltable.'+col] = left.iloc[0][col]
            for col in right:
                tmp['rtable.'+col] = right.iloc[0][col]
                tmp['label'] = label
            missed_error_pairs.append(tmp)

    if len(missed_error_pairs)>0:
        df = pd.DataFrame(missed_error_pairs)
        output_file = params['dataset_name']+'missed_errors.csv'
        df.to_csv(output_file, index=False)
    '''

def debug_labels(debugger, index2pair, pair2golden, top_k=20):

    num_iter_without_errors = 0
    all_detected_errors = set()
    all_detected_match_errors = set() # labels mislabeled as matches
    total_num_iters = 0
    while True:
        top_suspicious_indices = debugger.find_suspicious_labels(top_k)

        # find their correct labels
        index2correct_label = { index:pair2golden[ index2pair[index] ]  for index in top_suspicious_indices}
        iter_count, num_errors, error_indices, error_indices_matches, det_error_poses  = debugger.analyze(index2correct_label)
        #print('Iteration: ', iter_count)
        #print("Number of errors found: ", num_errors)
        #print("Error indices: ", error_indices)
        #print("Detector performance: ")
        #for n, (count, pos) in enumerate(det_error_poses):
        #    print("Detector ", n, "found ", count, " errors")
        #    print("Positions: ", pos)

        all_detected_errors.update(error_indices)
        all_detected_match_errors.update(error_indices_matches)

        if num_errors==0:
            num_iter_without_errors += 1
        else:
            num_iter_without_errors = 0

        if num_iter_without_errors>=3:
            break

        debugger.correct_labels(index2correct_label)

        total_num_iters += 1

        if total_num_iters>=5000:
            break

    return all_detected_errors, all_detected_match_errors

run('fpfn', False, 20)
#run('mono', False, 20)
#run('both', False, 20)
#run('both', False, 10)
#run('both', False, 15)
#run('cleanlab', False)
run('cleanlab', True)


import requests
import time

url = "https://api.covid19tracker.ca/summary/split"
response = requests.get(url)
js = response.json()

file = open('provincecodes.txt')
codes = file.read()
file.close()
print(codes)

province = input("enter a province code: ").upper()

while True:
    if province in codes:
        for i in js['data']:
            if i['province'] == province:
                province_data = i
        t = time.localtime()
        current_time = time.strftime("%H:%M:%S", t)
        result_date = province_data['date']
        cases = province_data['total_cases']
        deaths = province_data['total_fatalities']
        tests = province_data['total_tests']
        vaccinations = province_data['total_vaccinations']
        vaccinated = province_data['total_vaccinated']
        result = f'{province} covid data:\ndate: {result_date}\ntime: {current_time}\ntotal cases: {cases}\ntotal deaths: {deaths}\ntotal tests : {tests} \ntotal vaccines administered: {vaccinations}\nnumber of fully vaccinated: {vaccinated}\n'
        print(result)
        with open(f"./province covid data/{province}coviddata.txt", "a") as f:
            f.write(result)
        break

    else:
        print('enter a valid province code')
        province = input("enter the province code: ").upper()


import asyncio

import pygame
import socketio
from aiohttp import web

from game_socket.socketevent import SocketEvent
from util.globals import COLOR_WHITE

class Pygame:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((500, 500))

        self.server = GameServer()
        self.running = True

    async def run(self):
        cnt = 0
        while self.running:
            cnt += 1
            self.screen.fill(COLOR_WHITE)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.running = False

            if cnt == 5:
                self.server.enabled = True

            pygame.display.update()
            self.update_socket()
        pygame.quit()

    def update_socket(self):
        if self.server.enabled:
            if not self.server.is_running:
                self.server.start()
        else:
            if self.server.is_running:
                self.server.stop()

class GameServer:
    def __init__(self):
        self.sio = socketio.AsyncServer()
        self.site = None
        self.runner = None
        self.server_task = None

        self.enabled = False
        self.is_running = False

    def start(self, ip='localhost', port=8003, listener=None):
        print('[Server] start')
        self.is_running = True
        self.server_task = asyncio.create_task(self.run(ip, port, listener))

    def stop(self):
        print('[Server] stop')
        self.is_running = False
        if self.site is not None:
            asyncio.create_task(self.site.stop())

    def disconnect(self, sid):
        asyncio.create_task(self.sio.disconnect(sid))

    async def run(self, ip, port, listener):
        self.set_on_message_listener(listener)

        app = web.Application()
        self.sio.attach(app)

        self.runner = web.AppRunner(app)
        await self.runner.setup()
        self.site = web.TCPSite(self.runner, host=ip, port=port)
        await self.site.start()

    def set_on_message_listener(self, listener):
        @self.sio.on('*')
        async def catch_all(event, sid, data):
            print(event, sid, data)
            await listener(event, sid, data)

    def set_on_connect_listener(self, listener):

        @self.sio.event
        def connect(sid, environ, auth):
            print(f'[Event] {sid} connected!')

    def set_on_disconnect_listener(self, listener):
        @self.sio.event
        def disconnect(sid):
            print(f'[Event] {sid} disconnected!')
            listener(sid)

    async def post(self, event, sid, data):
        await self.sio.emit(event, data, sid)

    def emit(self, event, sid=None, data= None):
        print('[Emit]', event, sid, data)
        asyncio.create_task(self.post(event, sid, data))

async def main():
    game = Pygame()
    pygame_task = asyncio.create_task(game.run())

    await asyncio.gather(pygame_task)

if __name__ == '__main__':
    asyncio.run(main())


import flask

from app import app, db
from models import Image
from plot import plot_data

@app.route("/images")
def list_images():
    return {
        "result": "success",
        "images": Image.query.all(),
    }

@app.route("/images/<image_id>/meta")
def get_image_metadata(image_id):
    image = Image.query.filter_by(id=image_id).first()
    if not image:
        return {
            "result": "error",
            "message": f"can't find image with ID {image_id}"
        }, 404
    else:
        return {
            "result": "success",
            "image": image,
        }

@app.route("/images/<image_id>/meta", methods=["POST"])
def update_image_metadata(image_id):
    cell_width = flask.request.json.get("cell_width")
    if not cell_width:
        return {
            "result": "error",
            "message": "cell_width is a required parameter",
        }, 400

    num_updated_rows = (
        Image.query
            .filter_by(id=image_id)
            .update({Image.cell_width: cell_width}))
    db.session.commit()
    if num_updated_rows == 0:
        return {
            "result": "error",
            "message": f"could not find image with ID {image_id}"
        }, 404

    return {"result": "success"}

@app.route("/images/<image_id>", methods=["GET"])
def get_image(image_id):
    return flask.send_from_directory(app.config["IMAGE_DIR"], image_id)

@app.route("/plot", methods=["POST"])
def launch_plot():
    plot_data(flask.request.json)
    return {"result": "success"}


#!/usr/bin/env python
# -*- coding: utf-8 -*-

# This script is a simple wrapper which prefixes each i3status line with custom
# information. It is a python reimplementation of:
# http://code.stapelberg.de/git/i3status/tree/contrib/wrapper.pl
#
# To use it, ensure your ~/.i3status.conf contains this line:
#     output_format = "i3bar"
# in the 'general' section.
# Then, in your ~/.i3/config, use:
#     status_command i3status | ~/.i3/contrib/mplayer-wrapper.py
# In the 'bar' section.
#
# In its previous version it would display the cpu frequency governor, but you
# are free to change it to display whatever you like, see the comment in the
# source code below.
#
# © 2012 Valentin Haenel <valentin.haenel@gmx.de>
#
# In its current version, it will display what mplayer is playing by extracting
# song title from ICY Info lines written to a specific file stored whose path
# is stored in METADATA_FILE variable.
# To use it, ensure to output mplayer's ICY Info line to METADATA_FILE (see below).
# I used the following script to launch mplayer:
#     METADATA_FILE=/tmp/mplayer.data
#     [ -f $METADATA_FILE ] && rm -f $METADATA_FILE
#     mplayer -playlist foo.m3u | stdbuf -o L grep ICY > $METADATA_FILE
#     [ -f $METADATA_FILE ] && rm -f $METADATA_FILE
#
# © 2015 Mathieu Soula aka rid  <msoula@gmx.com>
#
# This program is free software. It comes without any warranty, to the extent
# permitted by applicable law. You can redistribute it and/or modify it under
# the terms of the Do What The Fuck You Want To Public License (WTFPL), Version
# 2, as published by Sam Hocevar. See http://sam.zoy.org/wtfpl/COPYING for more
# details.

import re
import os.path
import sys
import json

METADATA_FILE='/tmp/mplayer.data'
SONG_COLOR='#6780fb'
NOSONG_COLOR='#ffffff'
NOSONG_MSG='No Song Played'

def get_song(f):
    """ Read last line of given file which contains Mplayer's ICY Info data
        This line contains dictionary entry StreamTitle that is returned if found """
    try:
        with open(METADATA_FILE) as fp:
            line = fp.readlines()[-1].strip()
            info = line.split(':', 1)[1].strip()
            attrs = dict(re.findall("(\w+)='([^']*)'", info))
            return attrs.get('StreamTitle', None)
    except IndexError:
        return None
    except IOError:
        return None

def print_line(message):
    """ Non-buffered printing to stdout. """
    sys.stdout.write(message + '\n')
    sys.stdout.flush()

def read_line():
    """ Interrupted respecting reader for stdin. """
    # try reading a line, removing any extra whitespace
    try:
        line = sys.stdin.readline().strip()
        # i3status sends EOF, or an empty line
        if not line:
            sys.exit(3)
        return line
    # exit on ctrl-c
    except KeyboardInterrupt:
        sys.exit()

if __name__ == '__main__':
    # Skip the first line which contains the version header.
    print_line(read_line())

    # The second line contains the start of the infinite array.
    print_line(read_line())

    while True:
        line, prefix = read_line(), ''
        # ignore comma at start of lines
        if line.startswith(','):
            line, prefix = line[1:], ','

        j = json.loads(line)

        # insert information into the start of the json, but could be anywhere
        if os.path.exists(METADATA_FILE):
            song = get_song(METADATA_FILE)
            if song is not None:
                j.insert(0, {'full_text' : '%s' % song, 'name' : 'song', 'color' : SONG_COLOR})
            else:
                j.insert(0, {'full_text' : NOSONG_MSG, 'name' : 'song', 'color' : NOSONG_COLOR})
        else:
            j.insert(0, {'full_text' : NOSONG_MSG, 'name' : 'song', 'color' : NOSONG_COLOR})

        # and echo back new encoded json
        print_line(prefix+json.dumps(j))


from django.core.exceptions import ValidationError

BLOCK_LIST = ['FUCK', 'SHIT', 'Omar']

# 1- Clear input and Block list
# 2- Sum
# 3- if has error
# 4- iterate invalid items

def block(value):
    # 1- Clear input and Block list
    init_value = f"{value}".lower()
    init_items = set(init_value.split())
    block_list = set(x.lower() for x in BLOCK_LIST)

    # 2- Sum
    valid_words = list(init_items & block_list)
    has_error = len(valid_words) > 0

    # 3- if has error
    if has_error:
        invalid_items = []
        # 4- iterate invalid items
        for i, word in enumerate(valid_words):
            invalid_items.append(ValidationError("%(value)s is word blocked", params={
                                 'value': word}, code=f"blocked-word-{i}"))
        raise ValidationError(invalid_items)

    return value

2- Models ( updated - expanded )/src/products/validators.py


from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('auth/', include('authentication.urls', namespace='auth')),
    path('', include('expenses.urls', namespace='expenses')),
    path('', include('income.urls', namespace='income')),
    path('preferences/', include('userpreferences.urls', namespace='preferences')),
]

if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


from selenium import webdriver
from selenium.webdriver.common.by import By ## 추가
import re
driver = webdriver.Chrome('chromedriver.exe')

baseurl = 'https://www.kkc.or.kr/megazine/megazine_02.html?page='
lasturl = '&key='

namelist = [] # 크롤링 해온 이름을 넣을 list
# linelist =[] # 크롤링 해온 Line01 클래스를 넣을 list
# line2list =[] # 크롤링 해온 Line02 클래스를 넣을 list
namedict ={} # json 저장을 위해 dict 하나 생성

for i in range(14):
    if i == 0:
        continue
    driver.get(baseurl+str(i)+lasturl) #url을 분석한 결과 baseurl과 lasturl 사이에 숫자만 바뀌면 페이지가 넘어감
    name=driver.find_elements(By.CLASS_NAME,"kind") # 견종 이름 가져오기

    for j in name:
        namelist.append(j.text) # 가져온 견종 이름 namelist에 넣기 # 몇번? -> name만큼

    '''
    line =driver.find_elements(By.CLASS_NAME,'line01')
    for i in line:
        linelist.append(i.text)
    ''' # line01이라는 class에 있는 데이터(text) 가져오기 # 무슨 데이터야? -> 강아지의 원산지

    '''
    line2 = driver.find_elements(By.CLASS_NAME,'line02')
    for i in line2:
        linelist2.append(i.text)
    ''' # line02라는 class에 있는 데이터(text) 가져오기 # 무슨 데이터야? -> 강아지의 체고, 체중, 운동량, 그룹 모두 담겨 있음 ;;;

    # print(namelist) # 중간중간 namelist가 점점 늘어나는 걸 확인하는 용

#print(namelist) # 위 for 문 안에 넣으면 for문이 끝날때마다 출력되나, 여기에다가 print를 쓰면 결과만 만옴
#print(linelist)
#print(linelist2)

## 리스트에서 특정 문자열 제거
namelist2 =[]

for i in namelist :
    text = re.sub('[A-Z(/s]','',i).strip()
    namelist2.append(text)
print(namelist2)

for i in range(len(namelist2)): # list 만큼 dict에
    namedict[i] = namelist2[i] # 넣어줌
print(namedict) # 그 dict가 제대로 됐는지 확인

import json
with open("dognames.json",'w', encoding='utf-8') as file :
    json.dump(namedict,file, indent='\t', ensure_ascii=False) # dict를 json으로 저장


from typing import Any

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from dependency_injector.wiring import Provide, inject
from mmpy_bot import ActionEvent, Plugin, listen_webhook

from src.bot.services.matching import MatchingService
from src.bot.services.notify_service import NotifyService
from src.core.db.models import MatchReviewAnswerEnum
from src.depends import Container

MONDAY_TIME_SENDING_MESSAGE = 10
DAY_OF_WEEK_MONDAY = "mon"
FRIDAY_TIME_SENDING_MESSAGE = 11
DAY_OF_WEEK_FRIDAY = "fri"
SUNDAY_TIME_SENDING_MESSAGE = 11
DAY_OF_WEEK_SUNDAY = "sun"
WEDNESDAY_TIME_SENDING_MESSAGE = 11
DAY_OF_WEEK_WEDNESDAY = "wed"

class WeekRoutine(Plugin):
    @inject
    def on_start(
        self,
        notify_service: NotifyService = Provide[Container.week_routine_service,],
        matching_service: MatchingService = Provide[Container.matching_service],
        scheduler: AsyncIOScheduler = Provide[Container.scheduler],
    ) -> None:
        scheduler.add_job(
            notify_service.notify_all_users,
            "cron",
            day_of_week=DAY_OF_WEEK_FRIDAY,
            hour=FRIDAY_TIME_SENDING_MESSAGE,
            kwargs=dict(plugin=self, title="Еженедельный пятничный опрос"),
        )
        scheduler.add_job(
            matching_service.run_matching,
            "cron",
            day_of_week=DAY_OF_WEEK_SUNDAY,
            hour=SUNDAY_TIME_SENDING_MESSAGE,
        )
        scheduler.add_job(
            notify_service.meeting_notifications,
            "cron",
            day_of_week=DAY_OF_WEEK_MONDAY,
            hour=MONDAY_TIME_SENDING_MESSAGE,
            kwargs=dict(plugin=self),
        )
        scheduler.add_job(
            self.wednesday_notification_and_closing_meetings,
            "cron",
            day_of_week=DAY_OF_WEEK_WEDNESDAY,
            hour=WEDNESDAY_TIME_SENDING_MESSAGE,
            kwargs=dict(notify_service=notify_service, matching_service=matching_service),
        )
        scheduler.start()

    @inject
    async def _change_user_status(
        self, user_id: str, notify_service: NotifyService = Provide[Container.week_routine_service,]
    ) -> None:
        await notify_service.set_waiting_meeting_status(user_id)

    @listen_webhook("set_waiting_meeting_status")
    async def add_to_meeting(
        self,
        event: ActionEvent,
    ) -> None:
        await self._change_user_status(event.user_id)
        self.driver.respond_to_web(
            event,
            {
                "update": {"message": "До встречи!", "props": {}},
            },
        )

    @listen_webhook("not_meeting")
    async def no(self, event: ActionEvent) -> None:
        self.driver.respond_to_web(
            event,
            {
                "update": {"message": "На следующей неделе отправлю новое предложение.", "props": {}},
            },
        )

    @listen_webhook("match_review_is_complete")
    async def answer_yes(
        self,
        event: ActionEvent,
    ) -> None:
        await self._save_user_answer(event.user_id, MatchReviewAnswerEnum.IS_COMPLETE)
        self.driver.respond_to_web(
            event,
            {
                "update": {
                    "message": "Поделитесь итогами вашей встречи в канале "
                    '"Coffee на этой неделе", отправьте фото и '
                    "краткие эмоции, чтобы мотивировать других "
                    "поучаствовать в Random Coffee!",
                    "props": {},
                },
            },
        )

    @listen_webhook("match_review_is_not_complete")
    async def answer_no(
        self,
        event: ActionEvent,
    ) -> None:
        await self._save_user_answer(event.user_id, MatchReviewAnswerEnum.IS_NOT_COMPLETE)
        user_nickname = await self._get_pair_nickname(event.user_id)
        self.driver.respond_to_web(
            event,
            {
                "update": {
                    "message": f"Неделя скоро закончится, не забудь "
                    f"познакомиться с новым человеком и провести "
                    f"время за классным разговором, напиши "
                    f"{user_nickname} точно ждёт вашей встречи",
                    "props": {},
                },
            },
        )

    @inject
    async def _save_user_answer(
        self, user_id: str, answer: str, notify_service: NotifyService = Provide[Container.week_routine_service,]
    ) -> None:
        await notify_service.set_match_review_answer(user_id, answer)

    @inject
    async def _get_pair_nickname(
        self, user_id: str, matching_service: MatchingService = Provide[Container.matching_service,]
    ) -> Any:
        return await matching_service.get_match_pair_nickname(user_id)

    async def wednesday_notification_and_closing_meetings(
        self,
        notify_service: NotifyService,
        matching_service: MatchingService,
    ) -> None:
        await notify_service.match_review_notifications(plugin=self)
        await matching_service.run_closing_meetings()


import re
import string
from aiogram import Dispatcher, types
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters import Text
from aiogram.dispatcher.filters.state import StatesGroup, State
from config import ADMIN
from keyboard.clent_kb import direct_markup, submit_markup, cancel_markup
from database import command_insert_sql

class FSMAdmin(StatesGroup):
    id_mentor = State()
    name_mentor = State()
    direct_mentor = State()
    age_mentor = State()
    group_mentor = State()
    submit = State()

async def start_fsm(message: types.Message):
    if message.from_user.id != ADMIN:
        await message.answer('Функция доступна только для админа!')
    else:
        await FSMAdmin.id_mentor.set()
        await message.answer('id ментора?', reply_markup=cancel_markup)

async def load_id_mentor(message: types.Message, state: FSMContext):
    if not message.text.isdigit():
        await message.answer('id должен состоять только из цифр')
    elif message.text.isdigit():
        if len(message.text) != 10:
            await message.answer('id должен содержать не менее 10 цифр')
        else:
            async with state.proxy() as data:
                data['id_mentor'] = message.text
            await FSMAdmin.next()
            await message.answer('Имя ментора?', reply_markup=cancel_markup)

async def load_name_mentor(message: types.Message, state: FSMContext):
    if not message.text.isalpha():
        if [i for i in message.text if i in string.digits]:
            await message.answer('Имя не должно содержать цифр')
        else:
            await message.answer('В имени присутствуют недопустимые символы.')
    else:
        async with state.proxy() as data:
            data['name_mentor'] = message.text
        await FSMAdmin.next()
        await message.answer('Направление ментора', reply_markup=direct_markup)

async def load_direct_mentor(message: types.Message, state: FSMContext):
    direct = ['backend', 'frontend', 'fullstack']
    if message.text not in direct:
        await message.answer('Такого направления нет')
    else:
        async with state.proxy() as data:
            data['direct_mentor'] = message.text
        await FSMAdmin.next()
        await message.answer('Какой возраст?', reply_markup=cancel_markup)

async def load_age_mentor(message: types.Message, state: FSMContext):
    normal_age = [str(i) for i in range(17, 60)]

    if not message.text.isdigit():
        await message.answer('Пиши только число')

    elif message.text.isdigit() and message.text not in normal_age:
        await message.answer('Возрастное ограничение')
    else:
        async with state.proxy() as data:
            data['age_mentor'] = message.text
        await FSMAdmin.next()
        await message.answer('какая группа?', reply_markup=cancel_markup)

async def load_group_mentor(message: types.Message, state: FSMContext):
    example = re.findall(r'[a-zA-Z-]+[1-9]+', message.text)
    print(len(example))
    if len(example) <= 0:
        await message.answer(f'Неправильное название группы. Нужно писать так, например\nPy-21 или UX-UI-12')
    else:
        async with state.proxy() as data:
            data['group_mentor'] = message.text
        await FSMAdmin.next()
        await message.answer(f"id_ментора: {data['id_mentor']}\nИмя ментора: {data['name_mentor']}"
                                          f"\nНаправление: {data['direct_mentor']}"
                                          f"\nВозраст: {data['age_mentor'] }\nГруппа: {data['group_mentor']}",)

        await message.answer('Все верно?', reply_markup=submit_markup)

async def submit_fsm(message: types.Message, state: FSMContext):
    if message.text not in ['да', 'нет']:
        await message.answer('Выбери ответ из списка!')
    else:
        if message.text.lower() == 'да':
            await command_insert_sql(state)
            await state.finish()
            await message.answer('Ты зареган')
        elif message.text.lower() == 'нет':
            await state.finish()
            await message.answer('Не хочешь, не надо!')

async def cancel_fsm(message:types.Message, state: FSMContext):
    current_state = await state.get_state()
    if current_state is not None:
        await state.finish()
        await message.answer('Регистрация отменена')

def register_fsm_handlers(dp: Dispatcher):
    dp.register_message_handler(cancel_fsm,  state='*', commands=['cancel'])
    dp.register_message_handler(cancel_fsm, Text(equals='cancel', ignore_case=True), state='*',)
    dp.register_message_handler(start_fsm, commands=['reg'])
    dp.register_message_handler(load_id_mentor, state=FSMAdmin.id_mentor)
    dp.register_message_handler(load_name_mentor, state=FSMAdmin.name_mentor)
    dp.register_message_handler(load_direct_mentor, state=FSMAdmin.direct_mentor)
    dp.register_message_handler(load_age_mentor, state=FSMAdmin.age_mentor)
    dp.register_message_handler(load_group_mentor, state=FSMAdmin.group_mentor)
    dp.register_message_handler(submit_fsm, state=FSMAdmin.submit)


# -*- coding:utf-8 -*-
# @Time     :2023/1/30 6:32 下午
# @Author   :CHNJX
# @File     :loader_swagger.py
# @Desc     :
import json
from os import path, listdir
from os.path import isdir

import six
import yaml

def get_loader(filename):
    if filename.endswith(('.json', '.yml', '.yaml')):
        loader = json.load if filename.endswith('.json') else yaml.load
    else:
        with open(filename, 'r', 'utf-8') as f:
            contents = f.read().strip()
            loader = json.load if contents[0] in ['{', '['] else yaml.load
    return loader

def modify_spec_data(field, spec_data, data):
    if not isinstance(spec_data, dict) or not isinstance(data, dict):
        return None
    for k, v in data.items():
        if k in spec_data[field]:
            spec_data[field][k].update(v)
        else:
            spec_data[field][k] = v

def get_ref_filepath(filename, ref_file):
    ref_file = path.normpath(path.join(path.dirname(filename), ref_file))
    return ref_file

def load_file(filename, spec_data):
    loader = get_loader(filename)
    with open(filename, 'r', encoding='utf-8') as f:
        data = loader(f) if filename.endswith('.json') else loader(f, yaml.Loader)
        spec_data.update(data)
        for field, values in six.iteritems(data):
            if field not in ['definitions', 'parameters', 'paths'] or not isinstance(values, dict):
                continue
            for _field, value in six.iteritems(values):
                if _field == '$ref' and value.endswith('.yml'):
                    _filepath = get_ref_filepath(filename, value)
                    field_data = load_swagger(_filepath)
                    spec_data[field] = field_data
                elif '$ref' in value:
                    v = value.pop('$ref', '')
                    if not v:
                        continue
                    _filepath = get_ref_filepath(filename, v)
                    field_data = load_swagger(_filepath)
                    modify_spec_data(field, spec_data, field_data)

def load_swagger(filename):
    spec_data = {}
    files = listdir(filename) if isdir(filename) else [filename]
    for f in files:
        if f != filename:
            f = filename + '/' + f
        load_file(f, spec_data)
    return spec_data


'''Module for the detection of the cut plane'''
import json
import logging
import itertools as it
import operator
from pathlib import Path
import warnings
from typing import List, Union

import neurom as nm
import numpy as np

from neurom import geom
from neurom.core import Section, Morphology
from scipy import optimize, special

from neuror.cut_plane import legacy_detection, planes
from neuror.exceptions import NeuroRError

L = logging.getLogger(__name__)

class CutPlane(planes.HalfSpace):
    '''The cut plane class.

    It is composed of a HalfSpace and a morphology
    The morphology is part of the HalfSpace, the cut space is
    the complementary HalfSpace.
    '''

    def __init__(self, coefs: List[float],
                 upward: bool,
                 morphology: Union[None, str, Path, Morphology],
                 bin_width: float):
        '''Cut plane ctor.

        Args:
            coefs: the [abcd] coefficients of a plane equation: ``a X + b Y + c Z + d = 0``
            upward: if ``True``, the morphology points satisfy: ``a X + b Y + c Z + d > 0``
                    else, they satisfy: ``a X + b Y + c Z + d < 0``
            morphology: the morphology
            bin_width: the bin width
        '''
        super().__init__(coefs[0], coefs[1], coefs[2], coefs[3], upward)

        if isinstance(morphology, Morphology):
            self.morphology = morphology
        elif isinstance(morphology, (str, Path)):
            self.morphology = nm.load_morphology(morphology)
        elif morphology is not None:
            raise NeuroRError(f'Unsupported morphology type: {type(morphology)}')

        self.bin_width = bin_width
        self.cut_leaves_coordinates = None
        self.status = None
        self.minus_log_prob = None
        if morphology is not None:
            self._compute_cut_leaves_coordinates()
            self._compute_probabilities()
            self._compute_status()

    @classmethod
    def from_json(cls, cut_plane_obj, morphology=None):
        '''Factory constructor from a JSON file.

        Args:
            cut_plane_obj (dict|str|pathlib.Path): a cut plane
                It can be a python dictionary or a path to a json file that contains one
            morphology (~neurom.core.morphology.Morphology): the morphology passed to the
                :class:`CutPlane` object
        '''
        assert isinstance(cut_plane_obj, (str, dict, Path))

        if not isinstance(cut_plane_obj, dict):
            with open(cut_plane_obj) as f:
                cut_plane_obj = json.load(f)

        data = cut_plane_obj['cut-plane']
        obj = CutPlane([data['a'], data['b'], data['c'], data['d']],
                       data['upward'],
                       morphology,
                       cut_plane_obj['details']['bin-width'])

        obj.cut_leaves_coordinates = np.array(cut_plane_obj['cut-leaves'])
        obj.status = cut_plane_obj['status']
        obj.minus_log_prob = cut_plane_obj['details']['-LogP']
        return obj

    # pylint: disable=arguments-differ
    @classmethod
    def from_rotations_translations(cls, transformations, morphology, bin_width):
        plane = planes.PlaneEquation.from_rotations_translations(transformations)
        return cls(plane.coefs, True, morphology, bin_width)

    @classmethod
    def find(cls, neuron, bin_width=3,
             searched_axes=('X', 'Y', 'Z'),
             searched_half_spaces=(-1, 1),
             fix_position=None):
        """Find and return the cut plane that is oriented along X, Y or Z.

        6 potential positions are considered: for each axis, they correspond
        to the coordinate of the first and last point of the neuron.

        Description of the algorithm:

        1) The distribution of all points along X, Y and Z is computed
           and put into 3 histograms.
        2) For each histogram we look at the first and last empty bins
           (ie. the last bin before the histogram starts rising,
           and the first after it reaches zero again). Under the assumption
           that there is no cut plane, the posteriori probability
           of observing this empty bin given the value of the not-empty
           neighbour bin is then computed.
        3) The lowest probability of the 6 probabilities (2 for each axes)
           corresponds to the cut plane

        Args:
            neuron (~neurom.core.morphology.Morphology|str|pathlib.Path): a morphology
            bin_width: The size of the binning

            display: where or not to display the control plots
                     Note: It is the user responsability to call matplotlib.pyplot.show()

            searched_axes: x, y or z. Specify the planes for which to search the cut plane

            searched_half_spaces: A negative value means the morphology lives
                on the negative side of the plane, and a positive one the opposite.

            fix_position: If not None, this is the position
                for which to search the cut plane. Only the orientation will be searched for.
                This can be useful to find the orientation of a plane whose position is known.

        Returns:
            A cut plane object
        """
        warnings.warn("This method will be deprecated in favor or cut_leaves.find_cut_leaves"
                      "it also has bugs if one uses +1 in searched_half_spaces and multiple"
                      "combinations of searched_arguments.", DeprecationWarning)
        if not isinstance(neuron, Morphology):
            neuron = nm.load_morphology(neuron)

        # pylint: disable=invalid-unary-operand-type
        coef_d = -fix_position if fix_position is not None else 0

        cut_planes = [CutPlane([int(axis.upper() == 'X'), int(axis.upper() == 'Y'),
                                int(axis.upper() == 'Z'), coef_d],
                               upward=(side > 0),
                               morphology=neuron,
                               bin_width=bin_width)
                      for axis, side in it.product(searched_axes, searched_half_spaces)]

        best_cut_plane = max(cut_planes, key=operator.attrgetter('minus_log_prob'))

        if fix_position is None:
            _, bins = best_cut_plane.histogram()
            # The orientation of the plane is defined such as the morphology lives
            # on the positive side once coordinates are projected
            # (see HalfSpace.project_on_directed_normal)
            # So the first bin is where we look for the cut plane
            best_cut_plane.coefs[3] = bins[0]
        else:
            best_cut_plane.coefs[3] = coef_d
        return CutPlane(
            best_cut_plane.coefs, best_cut_plane.upward, neuron, best_cut_plane.bin_width)

    @classmethod
    def find_legacy(cls, neuron, axis):
        '''Find the cut points according to the legacy algorithm

        As implemented in:
        https://bbpgitlab.epfl.ch/nse/morphologyrepair/BlueRepairSDK/-/blob/main/BlueRepairSDK/src/repair.cpp#L263
        '''
        if not isinstance(neuron, Morphology):
            neuron = nm.load_morphology(neuron)

        cut_leaves, side = legacy_detection.internal_cut_detection(neuron, axis)

        plane = cls([int(axis.upper() == 'X'), int(axis.upper() == 'Y'),
                     int(axis.upper() == 'Z'), 0],
                    upward=(side < 0),
                    morphology=None,
                    bin_width=0)
        plane.morphology = nm.load_morphology(neuron)
        plane.cut_leaves_coordinates = cut_leaves
        return plane

    @property
    def cut_sections(self):
        '''Returns sections that ends within the cut plane'''
        leaves = np.array([leaf
                           for neurite in self.morphology.neurites
                           for leaf in nm.iter_sections(neurite, iterator_type=Section.ileaf)])
        leaves_coord = [leaf.points[-1, nm.COLS.XYZ] for leaf in leaves]
        return leaves[self.distance(leaves_coord) < self.bin_width]

    def _compute_cut_leaves_coordinates(self):
        '''Returns cut leaves coordinates'''
        leaves = [leaf.points[-1, nm.COLS.XYZ] for leaf in self.cut_sections]
        self.cut_leaves_coordinates = np.vstack(leaves) if leaves else np.array([])

    def to_json(self):
        '''Return a dictionary with the following items:

            - status: 'ok' if everything went right, else an informative string
            - cut_plane: a tuple (plane, position) where 'plane' is 'X', 'Y' or 'Z'
                       and 'position' is the position
            - cut_leaves: an np.array of all termination points in the cut plane
            - figures: if 'display' option was used, a dict where values are tuples (fig, ax)
                     for each figure
            - details: A dict currently only containing -LogP of the bin where the cut plane was
                     found
        '''
        return {'cut-leaves': self.cut_leaves_coordinates,
                'status': self.status,
                'details': {'-LogP': self.minus_log_prob, 'bin-width': self.bin_width},
                'cut-plane': super().to_json()}

    def histogram(self):
        '''Get the point distribution projected along the normal to the plane

        Returns:
            a numpy.histogram
        '''
        points = _get_points(self.morphology)
        projected_points = self.project_on_directed_normal(points)
        min_, max_ = np.min(projected_points, axis=0), np.max(projected_points, axis=0)
        binning = np.arange(min_, max_ + self.bin_width, self.bin_width)
        return np.histogram(projected_points, bins=binning)

    def _compute_probabilities(self):
        '''Returns -log(p) where p is the a posteriori probabilities of the observed values
        in the bins X min, X max, Y min, Y max, Z min, Z max

        Parameters:
            hist: a dict of the X, Y and Z 1D histograms

        Returns: a dict of -log(p) values'''

        hist = self.histogram()
        if not hist[0].size:
            self.minus_log_prob = np.nan
        else:
            self.minus_log_prob = get_minus_log_p(0, hist[0][0])
        self._compute_status()
        return self.minus_log_prob

    def _compute_status(self):
        '''Returns ok if the probability that there is a cut plane is high enough'''
        _THRESHOLD = 50
        if np.isnan(self.minus_log_prob):
            self.status = 'The proba is NaN, something went wrong'
        elif self.minus_log_prob < _THRESHOLD:
            self.status = ('The probability that there is in fact NO '
                           f'cut plane is high: -log(p) = {self.minus_log_prob}!')
        else:
            self.status = 'ok'

def _success_function(params, points, bin_width):
    '''The success function is low (=good) when the difference of points
    on the left side and right side of the plane is high'''
    plane = planes.PlaneEquation.from_rotations_translations(params)
    n_left, n_right = plane.count_near_plane(points, bin_width)
    res = -abs(n_left - n_right)
    return res

def _minimize(x0, points, bin_width):
    '''Returns a tuple of the optimized values of
    (rot_x, rot_y, rot_z, transl_x, transl_y, transl_z)'''
    delta_angle = 10  # in degrees
    delta_transl = 10
    delta = np.array([delta_angle, delta_angle, delta_angle,
                      delta_transl, delta_transl, delta_transl])
    bounds_min = x0 - delta
    bounds_max = x0 + delta
    bounds = list(zip(bounds_min, bounds_max))
    result = optimize.minimize(_success_function,
                               x0=x0,
                               args=(points, bin_width),
                               bounds=bounds,
                               method='Nelder-Mead')

    if result.status:
        raise NeuroRError(result.message)
    return result.x

def get_minus_log_p(k, mu):
    '''Compute -Log(p|k) where p is the a posteriori probability to observe k counts
    in bin given than the mean value was "mu":
    demo: p(k|mu) = exp(-mu) * mu**k / k!
    '''
    return mu - k * np.log(mu) + np.log(special.factorial(k))

def _get_points(neuron):
    return np.array([point
                     for neurite in (neuron.neurites or [])
                     for section in nm.iter_sections(neurite)
                     for point in section.points])

def plot(neuron, result, inline=False):
    '''Plot the neuron, the cut plane and the cut leaves.

    Args:
        neuron (~neurom.core.morphology.Morphology): the neuron to be plotted
        result (dict): the cut plane object in dictionary form
        inline (bool): if True, plot as an interactive plot (for example in a Jupyter notebook)
    '''
    try:
        from plotly_helper.neuron_viewer import NeuronBuilder
        from plotly_helper.object_creator import scatter
        from plotly_helper.shapes import line
    except ImportError as e:
        raise ImportError(
            'neuror[plotly] is not installed.'
            ' Please install it by doing: pip install neuror[plotly]') from e

    bbox = geom.bounding_box(neuron)

    plane = result['cut-plane']

    for display_plane, idx in [('xz', 0), ('yz', 1), ('3d', None)]:
        builder = NeuronBuilder(neuron, display_plane, inline=inline,
                                line_width=4, title=str(neuron.name))
        if idx is not None:
            if plane['a'] == 0 and plane['b'] == 0:
                builder.helper.add_shapes([
                    line(bbox[0][idx], -plane['d'], bbox[1][idx], -plane['d'], width=4)
                ])
            builder.helper.add_data({'a': scatter(result['cut-leaves'][:, [idx, 2]],
                                                  showlegend=False, width=5)})
        else:
            builder.helper.add_data({'a': scatter(result['cut-leaves'], width=2)})
        builder.plot()


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

INPUT_SIZE = 8
OUTPUT_SIZE = 5
In_data = np.eye(5000, INPUT_SIZE)
Out_data = np.eye(5000, OUTPUT_SIZE)
##构建输入数据beg
def readData(infile, outfile):
	with open(infile, 'r') as f:
		index = 0
		for line in f.readlines():
			inStr = line.strip()
			inSet = list(map(np.float32, inStr.split(' ')))
			In_data[index] = inSet
			index += 1

	with open(outfile, 'r') as f:
		index = 0
		for line in f.readlines():
			outStr = line.strip()
			outSet = list(map(np.float32, outStr.split(' ')))
			Out_data[index] = outSet
			index += 1
readData('./data/input.txt', './data/output.txt')
##构建数据end

# Make up some real data
# x_data = np.linspace(-1, 1, 300)[:, np.newaxis]
# noise = np.random.normal(0, 0.05, x_data.shape)
# y_data = np.square(x_data) - 0.5 + noise
x_data = np.linspace(-1, 1, 300)[:, np.newaxis]
noise = np.random.normal(0, 0.05, x_data.shape)
y_data = np.square(x_data) - 0.5 + noise

In_size = 1
Out_size = 1
Neural_size = 10

class DNN(object):
	def __init__(self, input_size, output_size, neural_size):
		self.input_size = input_size
		self.output_size = output_size
		self.neurals = neural_size

		with tf.name_scope('inputs'):
			self.xs = tf.placeholder(tf.float32, [None, input_size], name='xs')
			self.ys = tf.placeholder(tf.float32, [None, output_size], name='ys')
		with tf.variable_scope('in_hidden'):
			self.l1 = self.add_input_layer(self.xs, self.input_size, self.neurals, activation_function=tf.nn.relu)
		with tf.variable_scope('out_hidden'):
			self.prediction = self.add_output_layer(self.l1, self.neurals, self.output_size, activation_function=None)
		with tf.name_scope('cost'):
			self.cost = self.compute_cost()
		with tf.name_scope('train'):
			self.train_step = tf.train.GradientDescentOptimizer(0.1).minimize(self.cost)

	def add_input_layer(self, inputs, in_size, out_size, activation_function=None):

		Weights = tf.Variable(tf.random_normal([in_size, out_size]))
		biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)

		Wx_plus_b = tf.matmul(inputs, Weights) + biases
		if activation_function is None:
			outputs = Wx_plus_b
		else:
			outputs = activation_function(Wx_plus_b)
		return outputs
	def add_output_layer(self, inputs, in_size, out_size, activation_function=None):
	    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
	    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
	    Wx_plus_b = tf.matmul(inputs, Weights) + biases
	    if activation_function is None:
	        outputs = Wx_plus_b
	    else:
	        outputs = activation_function(Wx_plus_b)
	    return outputs
	def compute_cost(self):
		return tf.reduce_mean(tf.reduce_sum(tf.square(self.ys-self.prediction), reduction_indices=[1]))

if __name__ == '__main__':
	with tf.Session() as sess:
		model = DNN(In_size, Out_size, Neural_size)
		init = tf.initialize_all_variables()
		sess.run(init)
		fig = plt.figure()
		ax = fig.add_subplot(1,1,1)
		ax.scatter(x_data, y_data)
		plt.ion()
		plt.show()
		for i in range(1000):
			# training
			sess.run(model.train_step, feed_dict={model.xs: x_data, model.ys: y_data})
			if i % 50 == 0:
			# to visualize the result and improvement
				try:
					ax.lines.remove(lines[0])
				except Exception:
					pass
				prediction_value = sess.run(model.prediction, feed_dict={model.xs: x_data})
				print(prediction_value)
				# plot the prediction
				lines = ax.plot(x_data, prediction_value, 'r-', lw=5)
				plt.pause(1)


from lab_python_oop import Circle, Rectangle, Square, Color
from lab_python_oop.AF import Figure
from datetime import datetime

def main():
    BLUE = Color.Color("Синий")
    GREEN = Color.Color("Зелёный")
    RED = Color.Color("Красный")

    r = Rectangle.Rectangle(2,2,BLUE)
    print(f"{r}")
    print("="*60)
    c = Circle.Circle(2, GREEN)
    print(f"{c}")
    print("="*60)
    s = Square.Square(2,RED)
    print(f"{s}")
    print("="*60)

    print(f"Текущая дата: {datetime.now()}") #получение текущего времени

if __name__ == "__main__":
    main()


from django.test import TestCase
from django.urls import reverse
from django.core.exceptions import ValidationError
from django.db import IntegrityError
from .models import Video

'''
reverse wil convert the name of the url into an actual path.

'''

class TestHomePageMessage(TestCase):

    def test_app_title_message_shown_on_home_page(self):
        url = reverse('home')
        response = self.client.get(url)
        self.assertContains(response, 'Music Videos')

class TestAddVideos(TestCase):

    def test_add_video(self):  # Adding a video, added to DB and video_id created.

        valid_video = {
            'name': 'Jon Spencer',
            'url': 'https://www.youtube.com/watch?v=IWY4gmbOz24',
            'notes': 'Song Worm Town'
        }
        url = reverse('add_video')
        response = self.client.post(url, data=valid_video, follow=True)  # follow will allow redirect to the youtube page to not error.

        self.assertTemplateUsed('video_collection/video_catalog.html')
        # does the video catelog show the new video
        self.assertContains(response, 'Jon Spencer' )
        self.assertContains(response, 'Song Worm Town')
        self.assertContains(response, 'https://www.youtube.com/watch?v=IWY4gmbOz24')

        video_count= Video.objects.count()
        self.assertEqual(1, video_count)  # checking database is getting added.

        video = Video.objects.first()

        self.assertEqual( 'Jon Spencer', video.name)
        self.assertEqual( 'Song Worm Town', video.notes)
        self.assertEqual( 'https://www.youtube.com/watch?v=IWY4gmbOz24', video.url)
        self.assertEqual( 'IWY4gmbOz24', video.video_id)

    def test_add_video_invalid_urls_not_added(self):

        invalid_video_urls = [
            'https://www.youtube.com/'
            'https://www.youtube.com/watch?'
            'https://www.youtube.com/watch?abc123'
            'https://www.youtube.com/watch?v='
            'https://www.youtube.com/watch'
            'https://minneapolis.edu'
            'https://minneapolis.edu?v=098786'
        ]
        for  invalid_video_url in  invalid_video_urls:

            new_video = {
                'name': 'example',
                'url':  invalid_video_url,
                'notes': 'example note'
            }
            url = reverse('add_video')
            response = self.client.post(url, new_video)

            self.assertTemplateNotUsed('video_collection/add.html')

            messages = response.context['messages']
            message_texts = [ message.message for message in messages]

            self.assertIn('Invalid YouTube URL', message_texts)
            self.assertIn('Please check that you enter data all in the fields.', message_texts)

        video_count= Video.objects.count()
        self.assertEqual(0, video_count)  # checking database is mot getting added to.

class TestVideoCatelog(TestCase):

    def test_all_videos_displayed_in_correct_order(self):

        v1 =Video.objects.create(name='ZYX', notes='example', url='https://www.youtube.com/watch?v=123')
        v2 =Video.objects.create(name='abc', notes='example', url='https://www.youtube.com/watch?v=124')
        v3 =Video.objects.create(name='AAA', notes='example', url='https://www.youtube.com/watch?v=125')
        v4 =Video.objects.create(name='lmn', notes='example', url='https://www.youtube.com/watch?v=126')

        expected_video_order = [v3,v2,v4,v1]  # simply list in order expected

        url = reverse('video_catalog')
        response = self.client.get(url)

        videos_in_template = list(response.context['videos'])

        self.assertEqual(videos_in_template, expected_video_order)

    def test_no_video_message(self):
        url = reverse('video_catalog')
        response = self.client.get(url)
        self.assertContains(response, 'No Videos')  # must match, same as empty in HTML template
        self.assertEqual(0, len(response.context['videos']))

    def test_one_video_number_message_one_video(self):
        v1 =Video.objects.create(name='ZYX', notes='example', url='https://www.youtube.com/watch?v=123')
        url = reverse('video_catalog')
        response = self.client.get(url)

        self.assertContains(response, '1 video')
        self.assertNotContains(response, '1 videos')

    def test_video_number_message_two_video(self):
        v1 =Video.objects.create(name='ZYX', notes='example', url='https://www.youtube.com/watch?v=123')
        v2 =Video.objects.create(name='abc', notes='example', url='https://www.youtube.com/watch?v=124')
        url = reverse('video_catalog')
        response = self.client.get(url)

        self.assertContains(response, '2 videos')

class TestVideoSearch(TestCase):

    pass

class TestVideoMode(TestCase):

    def test_invalid_url_raises_validation_error(self):
        invalid_video_urls = [
            'https://www.youtube.com/watch',
            'https://www.youtube.com/watch/somethingelse',
            'https://www.youtube.com/watch/somethingelse?v=1234567',
            'https://www.youtube.com/watch?',
            'https://www.youtube.com/watch?abc=123',
            'https://www.youtube.com/watch?v=',
            'https://github.com',
            '12345678',
            'htttttttttps://www.youtube.com/watch',
            'http://www.youtube.com/watch/somethingelse?v=1234567',
            'https://minneapolis.edu',
            'https://minneapolis.edu?v=123456'
        ]

        for  invalid_video_url in  invalid_video_urls:
            with self.assertRaises(ValidationError):
                Video.objects.create(name='example', url=invalid_video_url, notes='example note')

        self.assertEqual(0, Video.objects.count())

    def test_duplicate_video_raises_intgrety_error(self):
        v1 =Video.objects.create(name='ZYX', notes='example', url='https://www.youtube.com/watch?v=123')
        with self.assertRaises(IntegrityError):
            Video.objects.create(name='ZYX', notes='example', url='https://www.youtube.com/watch?v=123')


# -*- coding: utf-8 -*-
"""
Loop replacemnet

@author: Roberto
"""

# para correr lienas de codigo
# 1. seleccionar lineas de codigo
# 2. presioner tecla F9

import numpy as np
import pandas as pd
from pandas import DataFrame, Series
import re # Regex
import pyreadr  # Load R dataset
import os # for usernanme y set direcotrio

# pip install pyreadr  (correr linea esta linead e codigo en la consola)

# data.Rdata

# Change directory

user = os.getlogin()   # Username

# Set directorio

os.chdir(f"C:/Users/{user}/Documents/GitHub/1ECO35_2023_1/Lectures/Lecture_3") # Set directorio

#%% Ejemplo de header

print("Hola Mundo")

#%% Funciones lambda

# Función tradicional #

def interes(C, T, R):
  i = C*T*R
  return i

interes(5000, 24, 0.02)

# forma parsimoniosa de generar funciones

interes2 = lambda C, T, R : C*T*R

interes2(5000, 24, 0.02)

# Más ejemplos

# map permite aplicar una función que a cada elemento de un conjunto de datos  (array, listas)

genero = ['F', 'M', 'M', 'F', 'M']

list( map( lambda x: 0 if x == 'M' else 1, genero )  )

NSE = ['A', 'A', 'C', 'B', 'C']

list( map( lambda x: 3 if x == 'A' else (2 if x == 'B' else 1), NSE )  )

# Más ejemplos #

vector = np.arange(100)

map( lambda x: np.sqrt(x) - np.mean(vector), vector )

list( map( lambda x: np.sqrt(x) - np.mean(vector), vector)   )

np.sqrt(vector) - np.mean(vector)

'''
Ejemplos
'''

'''
Función 1
'''

def cube(x):

    out = x*(1/3) - 0.5*x

    return out

list( map( lambda x: cube(x) , vector)   )

'''
Función 2, de estandarización
'''

def sdv(x,mean,sd):

    out = (x-mean)/sd

    return out

list( map( lambda x, mean = np.mean(vector), sd = np.std(vector): sdv(x,mean,sd) , vector)  )

# función explicita en la función lambda

list( map( lambda x: (x - np.mean(vector))/np.std(vector), vector)  )

'''
Función 3, If statement

Valores menos a 50 asigne el numero 1 y asignar missing values para valores mayor i igual a 50
'''

vector = np.arange(100)

def function2(x):

    if x < 50:
         out = 1
    else:
         out = np.nan # Missing values

    return out

list( map( lambda x: function2(x) , vector)   )

'''
Función 4, extrae lo numeros de un texto

La función extrae los numeros de texto

'''

texto_vector = np.array(["Municipio San Luis: 12450","Municipio La victoria: 1450",
                         "Municipio La Molina: 3550","Municipio Ate: 506"])

list( map( lambda x: int(re.sub('\D',"", x)) , texto_vector)   )

# \D : todo menos numero

# import re (regular expression)

# \D: significa todo menos a los dígitos

# re.sub( patron de texto, sustitución, texto)

#%% apply_along_axis - Matrix

''' Loop replacement in Matrix '''

np.random.seed(15632)
x1 = np.random.normal(0,1,500) # normal distribution
x2 = np.random.normal(0,1,500) # normal distribution
x3 = np.random.normal(0,1,500) # normal distribution
x4 = np.random.normal(0,1,500) # normal distribution

X = np.column_stack((np.ones(500),x1,x2,x3,x4))

print(X.shape)

'''

En el caso de aplicar funciones como mean, std, y entre otros se puede aplicar pro filas o columnas

axis = 0 se aplica la función a cada columa
axis = 1 se aplica la función por filas

Numpy apply for matrix

numpy.apply_along_axis(func1d, axis, arr, *args, **kwargs)

'''

# mead y desviación estandar por columnas

np.mean(X, axis=0)   # axis = 0 (se aplica por columnas)
np.std(X, axis=0)

# mead y desviación estandar por filas

np.mean(X, axis=1)
len( np.mean(X, axis=1) )
np.std(X, axis=1)

'''

Dos formas de estandarizar una matriz

'''

XNormed = (X - np.mean(X, axis=0))/np.std(X, axis=0)

def standarize(x):
       out = (x - np.mean(x))/np.std(x)

       return out

X_std_2 = np.apply_along_axis(standarize, 0, X)

# axis = 0, se aplicará la función a los elementos de cada columna

#%% Apply - Dataframe

'''
 We use US census data from the year 2012 to analyse the effect of gender
 and interaction effects of other variables with gender on wage jointly.
 The dependent variable is the logarithm of the wage, the target variable is *female*
 (in combination with other variables). All other variables denote some other
 socio-economic characteristics, e.g. marital status, education, and experience.
 For a detailed description of the variables we refer to the help page.
 '''

cps2012_env = pyreadr.read_r("../../data/cps2012.Rdata") # output formato diccionario

cps2012_env  # es un diccionario. En la llave "data" está la base de datos
cps2012 = cps2012_env[ 'data' ] # extrae información almacenada en la llave data del diccionario cps2012_env
dt = cps2012.describe()

# Borrar variables constantes

variance_cols = cps2012.var().to_numpy() # to numpy

X = cps2012.iloc[ : ,  np.where( variance_cols != 0   )[0] ]

# np.where( variance_cols != 0   ) resulta la posición de lasa columnas con varianza != 0

#np.where( variance_cols != 0   )[0] # array

# np.where() permite obtener la posición de columnas que cumplen la condición

# Retirar la media de las variables

def demean(x):
    dif = x - np.mean( x ) # tima la media de la columna
    return dif

X = X.apply( demean, axis = 0 )  # axis :0 se aplica la función por columna

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

# Segundo ejemplo de base de datos

datos = pd.read_csv("../../data/BDD_compras_consumidores.csv", sep = ";")

datos['Channel'].value_counts()

datos['Region'].value_counts()

datos.info()

# Convertimos a categórica el nombre de las columnas:

datos['Channel'] = datos['Channel'].astype("category")
datos['Region'] = datos['Region'].astype("category")

datos.info()

# Apply #

# Las ventas totales por tipo de producto

datos.iloc[:,2:8].apply(lambda x: sum(x), axis = 0)  # axis = 0 , operación a nivel columna

# "iloc" permite seleccionar filas o columnas usando sus posiciones.

# ventas total por cada observación

datos['ventas'] = datos.iloc[:,2:8].apply(lambda x: sum(x), axis = 1) # axis = 0 , operación a nivel fila

#datos['ventas'] = datos['milk'] + datos['fresh']+ datos['grocery'] (poco eficiente)

# promedio por tipo de producto

datos.iloc[:,2:8].apply(lambda x: np.mean(x), axis = 0)

# minimo valor de la venta por tipo de producto

datos.iloc[:,2:8].apply(lambda x: np.min(x), axis = 0)

# máximo valor de la venta por tipo de producto

datos.iloc[:,2:8].apply(lambda x: np.max(x), axis = 0)

# cambio de moneda

datos2 = datos.iloc[:,2:8].apply(lambda x: x/3.9)

#datos3 = pd.concat([datos.iloc[:,:2],datos2], axis = 1) # se une a nivel columna o de forma horizontal

#%% *args

"""
The special syntax *args in function definitions in python is used to pass a variable number
of arguments to a function. The object *args is a tuple that contains all the arguments.
 When you build your code, you should consider *args as a tuple.
"""

'''
*args : tipo tuple o array
'''

"Keyword: *args, incluir una cantidad variable de argumentos"

def calculator(x,y,w,z,a,b):

    return x+y+w+z+a+b

calculator(10,15)

def calculator( *args ):

    print( f"args is a {type( args )}" )

    vector = np.array( list(args) )  # *args : tuple

    minimo = np.min(vector)

    maximo = np.max(vector)

    prod = np.prod(vector)

    return prod, minimo, maximo, args

calculator( 8, 9, 100, 3, 5, 51,58)

'''
*args se puede usar otro nombre siempre que se use * al inicio
'''

def calculator( *list_vars ):

    print( f"args is a {type( list_vars )}" )

    vector = np.array( list_vars )  # *args : tuple

    minimo = np.min(vector)

    maximo = np.max(vector)

    result = np.prod(vector)

    return result, minimo, maximo

calculator( 8, 9, 50, 40, 10, 1)

#%%  **Kwargs

'''
**Kwargs is an acronym of keyword arguments.
It works exactly like *Args but instead of accepting a variable number of positional arguments,
it accepts a variable number of keyword or named arguments.
'''

'''
**kwargs: tipo diccionario
'''

def calculator( *list_vars, **kwargs):

    print( type( list_vars ) )
    print( type( kwargs ) )

    if ( kwargs[ 'function' ] == "media" ) :

        # Get the first value
        result = np.mean( list_vars )

    elif ( kwargs[ 'function' ] == "adicion" ) :

        result = sum(list_vars)

    elif ( kwargs['function'] == "median"):

        result = np.median(list_vars)  # *list_vars

    else:
        raise ValueError( f"The function argument {kwargs[ 'function' ]} is not supported." )

        # Mensaje de error por tipo de argumento

    return result

calculator( 4, 5, 6, 7, 8, function = "adicion" )

calculator( 4, 5, 6, function = "media" )

calculator(100,300,50, function = "adicion")

calculator(100,300,50, function = "median")

calculator(100,300,50, function = "varainza")

# calculator( 4, 5, 6, 7, 8, function = "inversa" )

calculator( np.arange(10), function = "media" )

'''
Example using dataset cps2012
'''

def transform(Data, *select, **function) -> pd.DataFrame: #output DataFrame

    select = list(select)  # se transforma a una lista
    Data_select = Data[select] # se filtra por columnas

    if function['method'] == "demean":

        X = Data_select.apply(lambda row: row - np.mean(row), axis =0)

    elif function['method'] == "estandarize":

        X = Data_select.apply(lambda row: (row - np.mean(row))/np.std(row), axis =0)

    return X

transform(cps2012, "lnw", "exp1","exp2", method = "estandarize")

transform(cps2012, "lnw", "exp1","exp2", "exp3", "exp4", method = "demean")


import re
from statistics import median
from operator import itemgetter

def le_assinatira():
    """
        The function reads the values of the linguistic features
        of the model and returns a signature to be compared with
        the texts provided.
    """
    print("Welcome to the automatic COH-PIAH detector.")

    wal = float(input("Enter the average word size: "))
    ttr = float(input("Enter the Type-Token interface: "))
    hlr = float(input("Enter the Reason Hapax Legomana: "))
    sal = float(input("Enter the sentence size: "))
    sac = float(input("Enter the average sentence complexity: "))
    pal = float(input("Enter the average sentence size: "))

    return [wal, ttr, hlr, sal, sac, pal]

def le_textos():
    i = 1
    textos = []
    texto = input(f"Enter text {str(i)} (press enter to exit): ")
    while texto:
        textos.append(texto)
        i += 1
        texto = input(f"Enter text {str(i)} (press enter to exit): ")
    return textos

def separa_sentencas(texto: str) -> list:
    """
       The function receives a text and returns a list of sentences
       within the text.
    """
    sentencas = re.split(r"[.!?]+", texto)
    if sentencas[-1] == "":
        del sentencas[-1]
    return sentencas

def separa_frases(sentenca: str) -> list:
    """
        The function receives a sentence and returns
        a list of phrases within the sentence.
    """
    return re.split(r"[,:;]+", sentenca)

def separa_palavras(frase: str) -> list:
    """
        The function receives a sentence and returns a list
        of words within the sentence.
    """
    return frase.split()

def n_palavras_unicas(lista_palavras):
    """
        This function receives a list of words and returns the
        number of words that appear only once.
    """
    freq = dict()
    unicas = 0
    for palavra in lista_palavras:
        p = palavra.lower()
        if p in freq:
            if freq[p] == 1:
                unicas -= 1
            freq[p] += 1
        else:
            freq[p] = 1
            unicas += 1

    return unicas

def n_palavras_diferentes(lista_palavras):
    """
        This function receives a list of words and returns the
        number of different words used.
    """
    freq = dict()
    for palavra in lista_palavras:
        p = palavra.lower()
        if p in freq:
            freq[p] += 1
        else:
            freq[p] = 1

    return len(freq)

def compara_assinatura(as_a, as_b):
    """
        TO IMPLEMENT. This function receives two text signatures
        and must return the degree of similarity in the signatures.
    """
    diferenca = sum(abs(a - b) for a, b in zip(as_a, as_b))
    print(diferenca)
    print(diferenca / 6)
    return diferenca / 6

def calcula_assinatura(texto):
    """
        TO IMPLEMENT. This function receives a text and must return
        the text signature.
    """
    return [
        calcula_tamanho_medio_palavra(texto),
        calcula_type_token(texto),
        calcula_hapax_legomana(texto),
        calcula_tamanho_medio_sentenca(texto),
        calcula_complexidade_sentenca(texto),
        calcula_tamanho_medio_frase(texto),
    ]

def avalia_textos(textos, ass_cp):
    """
        TO IMPLEMENT. This function receives a list of texts and
        an ass_cp signature and must return the number (1 to n) of
        the text most likely to have been infected with COH-PIAH.
    """
    aux = {}
    for k, v in enumerate(textos):
        ass = calcula_assinatura(v)
        aux[k + 1] = compara_assinatura(ass_cp, ass)
    aux = sorted(aux.items(), key=itemgetter(1))
    return aux[0][0]

def calcula_tamanho_medio_palavra(texto: str) -> float:
    frases = retorna_lista_frases(texto)
    palavras = []
    for frase in frases:
        palavras.extend([len(palavra) for palavra in separa_palavras(frase)])

    return sum(palavras) / len(palavras)

def calcula_type_token(texto: str) -> float:
    frases = retorna_lista_frases(texto)
    palavras = []
    for frase in frases:
        palavras.extend(separa_palavras(frase))
    return n_palavras_diferentes(palavras) / len(palavras)

def calcula_hapax_legomana(texto: str) -> float:
    frases = retorna_lista_frases(texto)
    palavras = []
    for frase in frases:
        palavras.extend(separa_palavras(frase))
    return n_palavras_diferentes(palavras) / n_palavras_unicas(palavras)

def calcula_tamanho_medio_sentenca(texto: str) -> float:
    average = []
    for item in separa_sentencas(texto):
        average.append(len(item))
    return median(average)

def calcula_complexidade_sentenca(texto: str) -> float:
    sentencas = separa_sentencas(texto)
    return len(retorna_lista_frases(texto)) / len(sentencas)

def calcula_tamanho_medio_frase(texto: str) -> float:
    tamanho_das_frases = [len(item) for item in retorna_lista_frases(texto)]
    return sum(tamanho_das_frases) / len(retorna_lista_frases(texto))

def retorna_lista_frases(texto: str) -> list:
    frases = []
    sentencas = separa_sentencas(texto)
    for item in sentencas:
        frases.extend(separa_frases(item))
    return frases

if __name__ == "__main__":
    assinatura = le_assinatira()
    textos = le_textos()
    result = avalia_textos(textos, assinatura)
    print(result)