import datetime
import glob
import math
import os
import traceback

import psutils.custom_errors as cerr
import psutils.globals as psu_globals
import psutils.argtype as psu_at
import psutils.script_action as psu_act
import psutils.string as psu_str
from psutils.print_methods import *

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

### Argument globals ###

## Argument strings ("ARGSTR_")
ARGSTR_DSTDIR_GLOBAL = '--dstdir-global'
ARGSTR_TASKLIST = '--tasklist'
ARGSTR_SRCLIST = '--srclist'
ARGSTR_SRCLIST_ROOTED = '--srclist-rooted'
ARGSTR_SRCLIST_PREFIX = '--srclist-prefix'
ARGSTR_SRCLIST_PREFIX_DST = '--srclist-prefix-dst'
ARGSTR_SRCLIST_SUFFIX = '--srclist-suffix'
ARGSTR_SRCLIST_ROOTDIR = '--srclist-rootdir'
ARGSTR_SRCLIST_DELIM = '--srclist-delim'
ARGSTR_SRCLIST_NOGLOB = '--srclist-noglob'
ARGSTR_SRCLIST_IGNORE_MISSING = '--srclist-ignore-missing'
ARGSTR_SYNC_TREE = '--sync-tree'
ARGSTR_TRANSPLANT_TREE = '--transplant-tree'
ARGSTR_COLLAPSE_TREE = '--collapse-tree'

## Argument help info ("ARGHLP_", only when needed outside of argparse)
ARGHLP_SRCLIST_FORMAT = None  # set globally in pre_argparse()
ARGHLP_SRCLIST_ROOTED_FORMAT = None  # set globally in pre_argparse()

## Argument groups ("ARGGRP_" lists of "ARGSTR_" argument strings)
ARGGRP_SRC = [ARGSTR_SRCLIST, ARGSTR_SRCLIST_ROOTED]
ARGGRP_DST = [ARGSTR_DSTDIR_GLOBAL]
ARGGRP_SYNC_MODE = [ARGSTR_SYNC_TREE, ARGSTR_TRANSPLANT_TREE]

## Argument collections ("ARGCOL_" lists of "ARGGRP_" argument strings)
ARGCOL_MUT_EXCL_SET = [
    ARGGRP_SYNC_MODE
]

## Argument modes ("ARGMOD_", used for mutually exclusive arguments that control the same mechanic)
# (It's generally better to use a single argument with multiple choices, but sometimes we want
#  to use multiple `action='store_true'` arguments instead.)
ARGMOD_SYNC_MODE_NULL = 0
ARGMOD_SYNC_MODE_SYNC_TREE = 1
ARGMOD_SYNC_MODE_TRANSPLANT_TREE = 2

## Argument defaults ("ARGDEF_")
ARGDEF_SRCLIST_DELIM = ','
ARGDEF_BUNDLEDIR = os.path.realpath(os.path.join(os.path.expanduser('~'), 'scratch', 'task_bundles'))

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

### Custom globals ###

PATH_SEPARATORS_LIST = psu_globals.PATH_SEPARATORS_LIST
PATH_SEPARATORS_CAT = ''.join(PATH_SEPARATORS_LIST)

SYNC_MODE_GLOBAL = None

PATH_TYPE_UNKNOWN = 0
PATH_TYPE_FILE = 1
PATH_TYPE_DIR = 2
PATH_TYPE_DNE = 3

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

def pre_argparse(srclist_delim=ARGDEF_SRCLIST_DELIM,
                 argstr_dst=None):
    global ARGHLP_SRCLIST_FORMAT, ARGHLP_SRCLIST_ROOTED_FORMAT

    provided_srclist_delimiter = psu_act.get_script_arg_values(ARGSTR_SRCLIST_DELIM)
    srclist_delimiter = provided_srclist_delimiter if provided_srclist_delimiter is not None else srclist_delim

    ARGHLP_SRCLIST_FORMAT = ' '.join([
        "\n(1) All 'src_path' line items (only when argument {} directory is provided)".format('/'.join([argstr for argstr in [argstr_dst, ARGSTR_DSTDIR_GLOBAL] if argstr is not None])),
        "\n(2) A single 'src_path{}dst_dir' line at top followed by all 'src_path' line items,".format(srclist_delimiter),
        "where 'dst_dir' is a directory used for all items in the list",
        "\n(3) All 'src_path{}dst_path' line items".format(srclist_delimiter)
    ])

    ARGHLP_SRCLIST_ROOTED_FORMAT = ' '.join([
        "A header line is expected containing 'src_rootdir[{}dst_rootdir]' to signify that".format(srclist_delimiter),
        "the folder structure from 'src_rootdir' down to 'src_path' for each 'src_path[{}dst_rootdir]'".format(srclist_delimiter),
        "line item will be replicated within the destination root directory.",
        "\nIf 'src_path' items are absolute paths, each is expected to start with the",
        "'src_rootdir' path EXACTLY as it appears in the header. If a 'src_path' item does not",
        "start with 'src_rootdir', then the 'src_path' item will be treated as a relative path",
        "within 'src_rootdir'.",
    ])

def add_srclist_arguments(parser,
                          srclist_delim=ARGDEF_SRCLIST_DELIM):
    parser.add_argument(
        '-dg', ARGSTR_DSTDIR_GLOBAL,
        type=psu_at.ARGTYPE_PATH(argstr=ARGSTR_DSTDIR_GLOBAL,
            existcheck_fn=os.path.isfile,
            existcheck_reqval=False,
            accesscheck_reqtrue=os.W_OK,
            accesscheck_parent_if_dne=True),
        help=' '.join([
            "Path to directory in which all output files will be created.",
            "This destination directory will override all destination paths in source lists.",
        ])
    )

    parser.add_argument(
        '-tl', ARGSTR_TASKLIST,
        type=psu_at.ARGTYPE_PATH(argstr=ARGSTR_TASKLIST,
            existcheck_fn=os.path.isfile,
            existcheck_reqval=True,
            accesscheck_reqtrue=os.R_OK),
        nargs='+',
        action='append',
        help=' '.join([
            "[write me]"
        ])
    )

    parser.add_argument(
        '-sl', ARGSTR_SRCLIST,
        type=psu_at.ARGTYPE_PATH(argstr=ARGSTR_SRCLIST,
            existcheck_fn=os.path.isfile,
            existcheck_reqval=True,
            accesscheck_reqtrue=os.R_OK),
        nargs='+',
        action='append',
        help=' '.join([
            "Path to textfile list of 'src_path[{}dst_path]' tasks to be performed.".format(ARGSTR_SRCLIST_DELIM),
            ARGHLP_SRCLIST_FORMAT,
        ])
    )
    parser.add_argument(
        '-slr', ARGSTR_SRCLIST_ROOTED,
        type=psu_at.ARGTYPE_PATH(argstr=ARGSTR_SRCLIST_ROOTED,
            existcheck_fn=os.path.isfile,
            existcheck_reqval=True,
            accesscheck_reqtrue=os.R_OK),
        nargs='+',
        action='append',
        help=' '.join([
            "Path to textfile list of 'src_path[{}dst_rootdir]' tasks to be performed.".format(ARGSTR_SRCLIST_DELIM),
            ARGHLP_SRCLIST_ROOTED_FORMAT,
        ])
    )

    parser.add_argument(
        '-slp', ARGSTR_SRCLIST_PREFIX,
        type=psu_at.ARGTYPE_PATH(argstr=ARGSTR_SRCLIST_PREFIX,
            abspath_fn=None,
            existcheck_fn=os.path.isdir,
            existcheck_reqval=True),
        help=' '.join([
            "Directory path to prepend to all source paths in {} and {} textfiles.".format(ARGSTR_SRCLIST, ARGSTR_SRCLIST_ROOTED),
        ])
    )
    parser.add_argument(
        '-slpd', ARGSTR_SRCLIST_PREFIX_DST,
        type=psu_at.ARGTYPE_PATH(argstr=ARGSTR_SRCLIST_PREFIX_DST,
            abspath_fn=None,
            existcheck_fn=os.path.isfile,
            existcheck_reqval=False,
            accesscheck_reqtrue=os.W_OK,
            accesscheck_parent_if_dne=True),
        help=' '.join([
            "Directory path to prepend to all destination paths in {} and {} textfiles.".format(ARGSTR_SRCLIST, ARGSTR_SRCLIST_ROOTED),
        ])
    )
    parser.add_argument(
        '-sls', ARGSTR_SRCLIST_SUFFIX,
        type=str,
        help=' '.join([
            "String to append to all source paths in {} and {} textfiles.".format(ARGSTR_SRCLIST, ARGSTR_SRCLIST_ROOTED),
        ])
    )

    parser.add_argument(
        '-slrd', ARGSTR_SRCLIST_ROOTDIR,
        type=psu_at.ARGTYPE_PATH(argstr=ARGSTR_SRCLIST_PREFIX,
            abspath_fn=None,
            existcheck_fn=os.path.isdir,
            existcheck_reqval=True),
        help=' '.join([
            "Root directory used for source paths in {} textfiles when determining relative".format(ARGSTR_SRCLIST),
            "paths within destination directories. This path will be prepended to all source paths",
            "that do not already start with this absolute path.",
        ])
    )

    parser.add_argument(
        ARGSTR_SRCLIST_DELIM,
        type=str,
        default=srclist_delim,
        help=' '.join([
            "Delimiter used to separate source and destination paths in {} and {} textfiles.".format(ARGSTR_SRCLIST, ARGSTR_SRCLIST_ROOTED),
        ])
    )
    parser.add_argument(
        ARGSTR_SRCLIST_NOGLOB,
        action='store_true',
        help=' '.join([
            "Do not interpret '*' character as a wildcard for path-globbing in {} and {} textfiles.".format(ARGSTR_SRCLIST, ARGSTR_SRCLIST_ROOTED),
        ])
    )
    parser.add_argument(
        '-slim', ARGSTR_SRCLIST_IGNORE_MISSING,
        action='store_true',
        help="[write me]"
    )

    parser.add_argument(
        '-st', ARGSTR_SYNC_TREE,
        action='store_true',
        help=' '.join([
            "Copy contents of source directories directly into destination directories."
            "\nIf neither the {} nor {} options are specified, source directory paths".format(ARGSTR_SYNC_TREE, ARGSTR_TRANSPLANT_TREE),
            "that end with '{}' are automatically treated this way.".format(PATH_SEPARATORS_LIST),
        ])
    )
    parser.add_argument(
        '-tt', ARGSTR_TRANSPLANT_TREE,
        action='store_true',
        help=' '.join([
            "Copy contents of source directories into a folder under destination directories",
            "bearing the name of the source directory (i.e. 'dstdir/srcdir_name/').",
            "\nIf neither the {} nor {} options are specified, source directory paths".format(ARGSTR_SYNC_TREE, ARGSTR_TRANSPLANT_TREE),
            "that do not end with '{}' are automatically treated this way.".format(PATH_SEPARATORS_LIST),
        ])
    )
    parser.add_argument(
        '-ct', ARGSTR_COLLAPSE_TREE,
        action='store_true',
        help=' '.join([
            "Copy all files within source directories into destination directories one level deep,"
            "effectively collapsing the copied source directory tree.",
        ])
    )

def write_task_bundles(task_list, tasks_per_bundle,
                       dstdir, bundle_prefix,
                       task_delim=',', task_fmt='%s'):
    try:
        import numpy as np
        imported_numpy = False
    except ImportError:
        imported_numpy = False
        if task_fmt != '%s':
            raise
    bundle_prefix = os.path.join(dstdir, '{}_{}'.format(bundle_prefix, datetime.now().strftime("%Y%m%d%H%M%S")))
    jobnum_total = int(math.ceil(len(task_list) / float(tasks_per_bundle)))
    jobnum_fmt = psu_str.get_index_fmtstr(jobnum_total)
    bundle_file_list = []
    print("Writing task bundle text files in directory: {}".format(dstdir))
    for jobnum, tasknum in enumerate(range(0, len(task_list), tasks_per_bundle), 1):
        bundle_file = '{}_{}.txt'.format(bundle_prefix, jobnum_fmt.format(jobnum))
        task_bundle = task_list[tasknum:tasknum+tasks_per_bundle]
        if len(task_bundle) == 0:
            with open(bundle_file, 'w'):
                pass
        elif imported_numpy:
            np.savetxt(bundle_file, task_bundle, fmt=task_fmt, delimiter=task_delim)
        else:
            join_task_items = type(task_bundle[0]) in (tuple, list)
            with open(bundle_file, 'w') as bundle_file_fp:
                for task in task_bundle:
                    task_line = str(task) if not join_task_items else task_delim.join([str(arg) for arg in task])
                    bundle_file_fp.write(task_line+'\n')
        bundle_file_list.append(bundle_file)
    return bundle_file_list

def read_task_bundle(bundle_file, args_dtype=str, args_delim=',', header_rows=0,
                     ncol_strict=True, ncol_min=None, ncol_max=None,
                     allow_1d_task_list=True, read_header=False):
    try:
        import numpy as np
        imported_numpy = False
    except ImportError:
        imported_numpy = False

    task_list_ncols_min = None
    task_list_ncols_max = None

    if ncol_strict and imported_numpy:
        loadtxt_dtype = np.dtype(str) if args_dtype is str else args_dtype
        if read_header:
            loadtxt_skiprows = 0
            loadtxt_maxrows = header_rows
        else:
            loadtxt_skiprows = header_rows
            loadtxt_maxrows = None
        try:
            task_list = np.loadtxt(bundle_file, dtype=loadtxt_dtype, delimiter=args_delim,
                                   skiprows=loadtxt_skiprows, max_rows=loadtxt_maxrows,
                                   ndmin=2)
        except ValueError as e:
            if str(e).startswith("Wrong number of columns"):
                traceback.print_exc()
                raise cerr.DimensionError("Inconsistent number of columns in `bundle_file`: {}".format(bundle_file))
            else:
                raise
        task_list_ncols = task_list.shape[1] if task_list.ndim == 2 else task_list.shape[0]
        task_list_ncols_min = task_list_ncols
        task_list_ncols_max = task_list_ncols
        if ncol_min is not None and task_list_ncols is not None and task_list_ncols < ncol_min:
            raise cerr.DimensionError("`bundle_file` line has {} columns, less than required minimum ({}): {}".format(
                                 task_list_ncols, ncol_min, bundle_file))
        if ncol_max is not None and task_list_ncols is not None and task_list_ncols > ncol_max:
            raise cerr.DimensionError("`bundle_file` line has {} columns, more than required maximum ({}): {}".format(
                                 task_list_ncols, ncol_max, bundle_file))
        if allow_1d_task_list and task_list_ncols == 1:
            task_list = task_list[:, 0]

        task_list = task_list.tolist()

    else:
        with open(bundle_file, 'r') as bundle_file_fp:
            if read_header:
                task_list = []
                for i in range(header_rows):
                    header_line = bundle_file_fp.readline().strip()
                    if header_line != '':
                        task_list.append(header_line)
            else:
                task_list = [line for line in bundle_file_fp.read().splitlines() if line.strip() != '']
                if header_rows > 0 and len(task_list) > 0:
                    task_list = task_list[header_rows:]
        if len(task_list) > 0:
            if args_delim is not None:
                if type(args_dtype) in (tuple, list):
                    task_list = [[args_dtype[col_num](arg.strip()) for col_num, arg in enumerate(task.split(args_delim))] for task in task_list]
                else:
                    task_list = [[args_dtype(arg.strip()) for arg in task.split(args_delim)] for task in task_list]

                task_list_ncols = None
                if ncol_min is not None or ncol_max is not None:
                    task_list_ncols = [len(task) for task in task_list]
                    task_list_ncols_min = min(task_list_ncols)
                    task_list_ncols_max = max(task_list_ncols)
                    if task_list_ncols_min == task_list_ncols_max:
                        task_list_ncols = task_list_ncols_min
                elif ncol_strict:
                    first_task_ncols = len(task_list[0])
                    if all(len(task) == first_task_ncols for task in task_list):
                        task_list_ncols = first_task_ncols
                if ncol_strict and task_list_ncols is None:
                    raise cerr.DimensionError("Inconsistent number of columns in `bundle_file`: {}".format(bundle_file))

                if allow_1d_task_list and not read_header:
                    task_list = [task[0] if len(task) == 1 else task for task in task_list]
            elif not allow_1d_task_list or read_header:
                task_list = [[args_dtype(arg.strip())] for arg in task_list]
            else:
                task_list = [args_dtype(arg.strip()) for arg in task_list]

    if ncol_min is not None and task_list_ncols_min is not None and task_list_ncols_min < ncol_min:
        raise cerr.DimensionError("`bundle_file` line has {} columns, less than required minimum ({}): {}".format(
                             task_list_ncols_min, ncol_min, bundle_file))
    if ncol_max is not None and task_list_ncols_min is not None and task_list_ncols_max > ncol_max:
        raise cerr.DimensionError("`bundle_file` line has {} columns, more than required maximum ({}): {}".format(
                             task_list_ncols_max, ncol_max, bundle_file))

    if read_header and len(task_list) == 1 and type(task_list[0]) is list:
        task_list = task_list[0]

    return task_list

class Tasklist2(object):
    def __init__(self, header=None, tasks=None, argname_taskindex_dict=None):
        self.header = header
        self.tasks = tasks
        self.argname_taskindex_dict = argname_taskindex_dict

    def read(self, tasklist_file_or_buff, col_delim=',',
             uniform_dtype=str,
             col_argname_dtype_def=None,  # collection of tuples like ((0, str))
             args=None, col_argstr_def=None,
             ncol_min=None, ncol_max=None, ncol_strict=True,
             expect_header=None, check_header_for_argnames=None,
             allow_missing_header=None, ncol_strict_header_separate=False):

        header_items = None
        task_array = []

        if col_argname_dtype_def is not None and col_argstr_def is not None:
            raise cerr.InvalidArgumentError(
                "`col_argname_dtype_def` and `col_argstr_def` arguments are mutually exclusive"
            )

        if args is None and col_argname_dtype_def is None and col_argstr_def is None:
            if expect_header is None:
                expect_header = False
            if check_header_for_argnames is True or allow_missing_header is True:
                raise cerr.InvalidArgumentError(
                    "`check_header_for_argnames` or `allow_missing_header` can only be True "
                    "when either `col_argname_dtype_def` or `col_argstr_def` are provided"
                )
            check_header_for_argnames = False
            allow_missing_header = False
        else:
            if expect_header is None:
                expect_header = True
            if check_header_for_argnames is None:
                check_header_for_argnames = True
            if col_argname_dtype_def is None and col_argstr_def is None:
                if allow_missing_header is True:
                    raise cerr.InvalidArgumentError(
                        "`allow_missing_header` can only be True "
                        "when either `col_argname_dtype_def` or `col_argstr_def` are provided"
                    )
                allow_missing_header = False
            elif allow_missing_header is None:
                allow_missing_header = True

        col_argname_dtype_def_derived = None

        if col_argname_dtype_def is not None:
            col_argname_dtype_def_derived = col_argname_dtype_def
        elif col_argstr_def is not None:
            if args is None:
                raise cerr.InvalidArgumentError("`args` argument must be provided along with `col_argstr_def`")
            col_argname_dtype_def_derived = [(argstr, args.argstr2argtype[argstr]) for argstr in col_argstr_def]

        with open(tasklist_file_or_buff, 'r') as tasklist_fp:

            first_line = tasklist_fp.readline().strip()
            if first_line == '':
                return
            first_line_items = [item.strip() for item in first_line.split(col_delim)]
            line_num = 1

            if not expect_header:
                task_line = first_line
                task_line_items = first_line_items
            else:
                header_line = first_line
                header_items = first_line_items

                if check_header_for_argnames:
                    assert col_argname_dtype_def is not None or col_argstr_def is not None
                    assert col_argname_dtype_def_derived is not None

                    header_argname_set = set(header_items)
                    col_argname_def_list = [argname for argname, argtype in col_argname_dtype_def_derived]
                    col_argname_def_set = set(col_argname_def_list)

                    if header_argname_set.issubset(col_argname_def_set):
                        argname_dtype_dict = {argname: dtype for argname, dtype in col_argname_dtype_def_derived}
                        col_argname_dtype_def_derived = [(argname, argname_dtype_dict[argname]) for argname in header_items]

                    elif args is not None and header_argname_set.issubset(set(args.all_argstr)):
                        if col_argname_dtype_def is not None:
                            warning(
                                "Argument names in header line ({}) of tasklist file ({}) are not a "
                                "subset of `col_argname_dtype_def` argument names ({}), but are instead "
                                "a subset of script ArgumentPasser argument strings which will be "
                                "leveraged instead.".format(
                                    header_line, tasklist_file_or_buff, col_argname_def_set
                                )
                            )
                        elif col_argstr_def is not None:
                            warning(
                                "Argument strings in header line ({}) of tasklist file ({}) are not "
                                "the expected set of `col_argstr_def` argument strings ({})".format(
                                    header_line, tasklist_file_or_buff, col_argname_def_set
                                )
                            )
                        col_argname_dtype_def_derived = [(argstr, args.argstr2argtype[argstr]) for argstr in header_items]

                    elif allow_missing_header:
                        if col_argname_dtype_def is not None:
                            warning(
                                "Tasklist file ({}) does not have an acceptable header line, "
                                "so it will be assumed that columns follow the provided "
                                "`col_argname_dtype_def` order precisely as given: {}".format(
                                    tasklist_file_or_buff, col_argname_def_list
                                )
                            )
                        elif col_argstr_def is not None:
                            warning(
                                "Tasklist file ({}) does not have an acceptable header line, "
                                "so it will be assumed that columns follow the provided "
                                "`col_argstr_def` order precisely as given: {}".format(
                                    tasklist_file_or_buff, col_argname_def_list
                                )
                            )
                        header_items = None
                    else:
                        raise cerr.ScriptArgumentError("Tasklist file does not have an acceptable header line: {}".format(tasklist_file_or_buff))

                if header_items is not None:
                    ncol_header = len(header_items)
                    if ncol_min is not None and ncol_header < ncol_min:
                        raise cerr.ScriptArgumentError(
                            "Tasklist file header line has {} columns, less than `ncol_min` required minimum ({}): {}".format(
                                ncol_header, ncol_min, tasklist_file_or_buff
                            ))
                    if ncol_max is not None and ncol_header > ncol_max:
                        raise cerr.ScriptArgumentError(
                            "Tasklist file header line has {} columns, more than `ncol_max` required maximum ({}): {}".format(
                                ncol_header, ncol_max, tasklist_file_or_buff
                            ))
                    if ncol_max is not None and ncol_header < ncol_max:
                        raise cerr.ScriptArgumentError(
                            "Tasklist file header line has {} columns, less than `ncol_max` possible maximum ({}): {}".format(
                                ncol_header, ncol_max, tasklist_file_or_buff
                            ))

                if header_items is None:
                    task_line = first_line
                    task_line_items = first_line_items
                else:
                    task_line = tasklist_fp.readline().strip()
                    task_line_items = [item.strip() for item in task_line.split(col_delim)]

                    if len(task_line_items) != len(header_items):
                        errmsg = ("Tasklist file ({}) number of columns mismatch between header ({}) "
                                  "and body ({})".format(tasklist_file_or_buff, len(header_items), len(task_line_items)))
                        if ncol_strict and not ncol_strict_header_separate:
                            raise cerr.ScriptArgumentError(errmsg)
                        else:
                            warning(errmsg)

            if col_argname_dtype_def_derived is not None:
                col_dtype_def_derived = [dtype for argname, dtype in col_argname_dtype_def_derived]
                # if ncol_min is None:
                #     ncol_min = len(col_argname_dtype_def_derived)
                if ncol_max is None:
                    ncol_max = len(col_argname_dtype_def_derived)
            else:
                col_dtype_def_derived = None

            if ncol_strict:
                ncol_body = len(task_line_items)
            ncol_body_max = float('-inf')

            while task_line != '':
                line_num += 1

                ncol_task_line = len(task_line_items)
                if ncol_strict and ncol_task_line != ncol_body:
                    raise cerr.ScriptArgumentError(
                        "Tasklist file {}, line {}: Number of columns ({}) breaks from constant "
                        "number of columns ({}) established prior to this line (`ncol_strict=True`)".format(
                            tasklist_file_or_buff, line_num, ncol_task_line, ncol_body
                        ))
                elif ncol_min is not None and ncol_task_line < ncol_min:
                    raise cerr.ScriptArgumentError(
                        "Tasklist file {}, line {}: Number of columns ({}) is less than `ncol_min` "
                        "required minimum ({})".format(
                            tasklist_file_or_buff, line_num, ncol_task_line, ncol_min
                        ))
                elif ncol_max is not None and ncol_task_line > ncol_max:
                    raise cerr.ScriptArgumentError(
                        "Tasklist file {}, line {}: Number of columns ({}) is more than `ncol_max` "
                        "required maximum ({})".format(
                            tasklist_file_or_buff, line_num, ncol_task_line, ncol_max
                        ))

                if ncol_task_line > ncol_body_max:
                    ncol_body_max = ncol_task_line

                if col_dtype_def_derived is not None:
                    task_line_values = [col_dtype_def_derived[col_idx](item) for col_idx, item in enumerate(task_line_items)]
                else:
                    task_line_values = [uniform_dtype(item) for item in task_line_items]

                task_array.append(task_line_values)

                task_line = tasklist_fp.readline().strip()
                task_line_items = [item.strip() for item in task_line.split(col_delim)]

            if col_argname_dtype_def_derived is None:
                col_argname_dtype_def_derived = [(argname, uniform_dtype) for argname in range(ncol_body_max)]

    def write(self, tasklist_file):
        pass

class Tasklist(object):
    def __init__(self, tasklist_file, args_dtype=str, args_delim=',', header_rows=0,
                 ncol_strict=True, ncol_strict_header_separate=False, ncol_min=None, ncol_max=None,
                 allow_1d_task_list=True, header_dtype=str):
        self.tasklist_file = tasklist_file
        self.header = None
        if header_rows > 0:
            self.header = read_task_bundle(
                tasklist_file, args_dtype=header_dtype, args_delim=args_delim, header_rows=header_rows,
                ncol_strict=ncol_strict, ncol_min=ncol_min, ncol_max=ncol_max,
                allow_1d_task_list=allow_1d_task_list, read_header=True
            )
        self.tasks = read_task_bundle(
            tasklist_file, args_dtype=args_dtype, args_delim=args_delim, header_rows=header_rows,
            ncol_strict=ncol_strict, ncol_min=ncol_min, ncol_max=ncol_max,
            allow_1d_task_list=allow_1d_task_list, read_header=False
        )
        if (    self.header is not None and ncol_strict and not ncol_strict_header_separate
            and len(self.header) > 0 and len(self.tasks) > 0):
                header_ncols = len(self.header[0]) if type(self.header[0]) is list else len(self.header)
                tasks_ncols = len(self.tasks[0]) if type(self.tasks[0]) is list else 1
                if header_ncols != tasks_ncols:
                    raise cerr.DimensionError("Inconsistent number of columns in `tasklist_file`: {}".format(tasklist_file))

def parse_src_args(args, argstr_src, argstr_dst, skip_dir_adjust=False):
    global SYNC_MODE_GLOBAL

    arg_dst = args.get(argstr_dst) if args.get(argstr_dst) is not None else args.get(ARGSTR_DSTDIR_GLOBAL)
    arg_srclist_prefix = args.get(ARGSTR_SRCLIST_PREFIX)
    arg_srclist_prefix_dst = args.get(ARGSTR_SRCLIST_PREFIX_DST)
    arg_srclist_suffix = args.get(ARGSTR_SRCLIST_SUFFIX)
    arg_srclist_rootdir = args.get(ARGSTR_SRCLIST_ROOTDIR)
    arg_srclist_noglob = args.get(ARGSTR_SRCLIST_NOGLOB)

    if args.get(ARGSTR_SYNC_TREE):
        SYNC_MODE_GLOBAL = ARGMOD_SYNC_MODE_SYNC_TREE
    elif args.get(ARGSTR_TRANSPLANT_TREE):
        SYNC_MODE_GLOBAL = ARGMOD_SYNC_MODE_TRANSPLANT_TREE
    else:
        SYNC_MODE_GLOBAL = ARGMOD_SYNC_MODE_NULL

    src_list = []
    srclist_tasklists = []
    srclist_rooted_tasklists = []

    ## Parse and validate multiple source arguments

    if args.get(argstr_src):
        if arg_dst is None:
            args.parser.error("argument {}/{} is required when {} is provided".format(
                argstr_dst, ARGSTR_DSTDIR_GLOBAL, argstr_src))
        src_list.extend(args.get(argstr_src))

    if args.get(ARGSTR_SRCLIST):
        for srclist_file in args.get(ARGSTR_SRCLIST):
            try:
                if arg_dst is None:
                    srclist_header = read_task_bundle(
                        srclist_file, ncol_min=1, ncol_max=2,
                        header_rows=1, read_header=True,
                        args_delim=args.get(ARGSTR_SRCLIST_DELIM)
                    )
                    if len(srclist_header) == 0:
                        warning("{} textfile is empty: {}".format(ARGSTR_SRCLIST, srclist_file))
                        continue
                    elif len(srclist_header) != 2:
                        raise cerr.DimensionError
                tasklist = Tasklist(
                    srclist_file, ncol_min=1, ncol_max=2, ncol_strict=True, ncol_strict_header_separate=True,
                    header_rows=1,
                    args_delim=args.get(ARGSTR_SRCLIST_DELIM)
                )
                if len(tasklist.header) == 0:
                    tasklist.header = None
                elif len(tasklist.tasks) == 0:
                    tasklist.tasks = tasklist.header
                    tasklist.header = None
                elif len(tasklist.tasks) > 0:
                    if len(tasklist.header) == 1:
                        if type(tasklist.tasks[0]) is list and len(tasklist.tasks[0]) == 2:
                            raise cerr.DimensionError
                        tasklist.tasks.insert(0, tasklist.header[0])
                        tasklist.header = None
                    elif len(tasklist.header) == 2:
                        if type(tasklist.tasks[0]) is list and len(tasklist.tasks[0]) == 2:
                            tasklist.tasks.insert(0, tasklist.header)
                            tasklist.header = None
                        elif type(tasklist.tasks[0]) is list and len(tasklist.tasks[0]) == 1:
                            tasklist.tasks.insert(0, [tasklist.header[0]])
                        else:
                            tasklist.tasks.insert(0, tasklist.header[0])
                if type(tasklist.header) is list and len(tasklist.header) == 0:
                    tasklist.header = None
            except cerr.DimensionError as e:
                traceback.print_exc()
                args.parser.error(
                    "{} {}; {} textfiles must be structured in one of the following formats:\n{}".format(
                        ARGSTR_SRCLIST, srclist_file, ARGSTR_SRCLIST, ARGHLP_SRCLIST_FORMAT
                ))

            tasklist_tasks_sanitized = []
            tasklist_src_dne = []
            task_type_is_list = (len(tasklist.tasks) > 0 and type(tasklist.tasks[0]) is list)

            for task in tasklist.tasks:
                task_src = task[0] if task_type_is_list else task

                if arg_srclist_prefix is not None:
                    task_src = os.path.join(arg_srclist_prefix, task_src)
                if arg_srclist_suffix is not None:
                    task_src = task_src + arg_srclist_suffix
                if arg_srclist_rootdir is not None:
                    if not task_src.startswith(arg_srclist_rootdir):
                        task_src = os.path.join(arg_srclist_rootdir, task_src)

                if (not arg_srclist_noglob and '*' in task_src) or os.path.exists(task_src):
                    if args.get(ARGSTR_SRCLIST_IGNORE_MISSING):
                        tasklist_tasks_sanitized.append(task)
                else:
                    tasklist_src_dne.append(task_src)

            if args.get(ARGSTR_SRCLIST_IGNORE_MISSING):
                tasklist.tasks = tasklist_tasks_sanitized
            elif len(tasklist_src_dne) > 0:
                args.parser.error("{} {}; source paths do not exist:\n{}".format(
                    ARGSTR_SRCLIST, srclist_file, '\n'.join(tasklist_src_dne)
                ))

            if len(tasklist.tasks) > 0:
                if arg_srclist_rootdir is not None:
                    tasklist.header = [arg_srclist_rootdir]
                    srclist_rooted_tasklists.append(tasklist)
                else:
                    srclist_tasklists.append(tasklist)

    if args.get(ARGSTR_SRCLIST_ROOTED):
        for srclist_file in args.get(ARGSTR_SRCLIST_ROOTED):
            try:
                if arg_dst is None:
                    srclist_first_two_lines = read_task_bundle(
                        srclist_file, ncol_min=1, ncol_max=2,
                        header_rows=2, read_header=True, allow_1d_task_list=False,
                        args_delim=args.get(ARGSTR_SRCLIST_DELIM)
                    )
                    if len(srclist_first_two_lines) == 0:
                        warning("{} textfile is empty: {}".format(ARGSTR_SRCLIST_ROOTED, srclist_file))
                        continue
                    elif 2 not in [len(line_items) for line_items in srclist_first_two_lines]:
                        raise cerr.DimensionError
                    else:
                        srclist_header = srclist_first_two_lines[0]
                        src_rootdir = srclist_header[0]
                        dst_rootdir = srclist_header[1] if len(srclist_header) == 2 else None
                        if not os.path.isdir(src_rootdir):
                            args.parser.error(
                                "{} {}; source root directory in header must be an existing directory: {}".format(
                                ARGSTR_SRCLIST_ROOTED, srclist_file, src_rootdir
                            ))
                        if dst_rootdir is not None and os.path.isfile(dst_rootdir):
                            args.parser.error(
                                "{} {}; destination root directory in header cannot be an existing file: {}".format(
                                ARGSTR_SRCLIST_ROOTED, srclist_file, dst_rootdir
                            ))
                tasklist = Tasklist(
                    srclist_file, ncol_min=1, ncol_max=2, ncol_strict=True, ncol_strict_header_separate=True,
                    header_rows=1,
                    args_delim=args.get(ARGSTR_SRCLIST_DELIM)
                )
            except cerr.DimensionError as e:
                traceback.print_exc()
                args.parser.error("{} {}; {} textfiles must be structured as follows:\n{}".format(
                    ARGSTR_SRCLIST_ROOTED, srclist_file, ARGSTR_SRCLIST_ROOTED, ARGHLP_SRCLIST_ROOTED_FORMAT
                ))

            tasklist_tasks_sanitized = []
            tasklist_src_dne = []
            task_type_is_list = (len(tasklist.tasks) > 0 and type(tasklist.tasks[0]) is list)

            for task in tasklist.tasks:
                if task_type_is_list:
                    task_src, task_dst_rootdir = task
                    if os.path.isfile(task_dst_rootdir):
                        args.parser.error(
                            "{} {}; destination root directory cannot be an existing file: {}".format(
                            ARGSTR_SRCLIST_ROOTED, tasklist.tasklist_file, task_dst_rootdir
                        ))
                else:
                    task_src = task

                if arg_srclist_prefix is not None:
                    task_src = os.path.join(arg_srclist_prefix, task_src)
                if arg_srclist_suffix is not None:
                    task_src = task_src + arg_srclist_suffix
                if (not arg_srclist_noglob and '*' in task_src) or os.path.exists(task_src):
                    if args.get(ARGSTR_SRCLIST_IGNORE_MISSING):
                        tasklist_tasks_sanitized.append(task)
                else:
                    tasklist_src_dne.append(task_src)

            if args.get(ARGSTR_SRCLIST_IGNORE_MISSING):
                tasklist.tasks = tasklist_tasks_sanitized
            elif len(tasklist_src_dne) > 0:
                args.parser.error("{} {}; source paths do not exist:\n{}".format(
                    ARGSTR_SRCLIST_ROOTED, srclist_file, '\n'.join(tasklist_src_dne)
                ))

            if len(tasklist.tasks) > 0:
                srclist_rooted_tasklists.append(tasklist)

    arg_dst_can_be_file = False
    if args.get(argstr_src) and args.get(argstr_dst) and not os.path.isdir(args.get(argstr_dst)):
        if len(src_list) == 1 and not (args.get(ARGSTR_SRCLIST) or args.get(ARGSTR_SRCLIST_ROOTED)):
            arg_dst_can_be_file = True

    ### Build list of tasks to be performed

    all_task_list = []

    ## Standardize source and destination paths to SYNC-style for file copy tasks

    for src_path in src_list:
        dst_path = arg_dst
        dst_path = adjust_dst_path(src_path, dst_path, arg_dst_can_be_file,
                                   skip_dir_adjust=skip_dir_adjust)
        all_task_list.append((src_path, dst_path))

    for tasklist in srclist_tasklists:

        tasklist_dst_can_be_file = False
        if args.get(ARGSTR_DSTDIR_GLOBAL) is not None:
            tasklist_dst_dir = args.get(ARGSTR_DSTDIR_GLOBAL)
        elif tasklist.header is not None:
            tasklist_dst_dir = tasklist.header[1]
        else:
            tasklist_dst_dir = None
            tasklist_dst_can_be_file = True

        if tasklist_dst_dir is None:
            dst_path_type = PATH_TYPE_UNKNOWN
        else:
            if not os.path.exists(tasklist_dst_dir):
                dst_path_type = PATH_TYPE_DNE
            elif os.path.isdir(tasklist_dst_dir):
                dst_path_type = PATH_TYPE_DIR
            elif os.path.isfile(tasklist_dst_dir):
                dst_path_type = PATH_TYPE_FILE
            else:
                dst_path_type = PATH_TYPE_UNKNOWN

        task_type_is_list = (len(tasklist.tasks) > 0 and type(tasklist.tasks[0]) is list)
        for task in tasklist.tasks:
            if task_type_is_list:
                src_path = task[0]
                if tasklist_dst_dir is not None:
                    dst_path = tasklist_dst_dir
                else:
                    dst_path = task[1]
                    if arg_srclist_prefix_dst is not None:
                        dst_path = os.path.join(arg_srclist_prefix_dst, dst_path)
            else:
                src_path = task
                dst_path = tasklist_dst_dir if tasklist_dst_dir is not None else arg_dst

            if arg_srclist_prefix is not None:
                src_path = os.path.join(arg_srclist_prefix, src_path)
            if arg_srclist_suffix is not None:
                src_path = src_path + arg_srclist_suffix

            if not arg_srclist_noglob and '*' in src_path:
                src_path_glob = glob.glob(src_path)
                if len(src_path_glob) == 0:
                    warning("{} {}; no source files found matching pattern: {}".format(
                        ARGSTR_SRCLIST, tasklist.tasklist_file, src_path
                    ))
                for src_path_single in src_path_glob:
                    dst_path_single = adjust_dst_path(
                        src_path_single, dst_path, dst_can_be_file=False, dst_path_type=dst_path_type,
                        sync_mode_default=ARGMOD_SYNC_MODE_TRANSPLANT_TREE, skip_dir_adjust=skip_dir_adjust
                    )
                    all_task_list.append((src_path_single, dst_path_single))
            else:
                dst_path = adjust_dst_path(
                    src_path, dst_path, tasklist_dst_can_be_file, dst_path_type,
                    skip_dir_adjust=skip_dir_adjust
                )
                all_task_list.append((src_path, dst_path))

    for tasklist in srclist_rooted_tasklists:

        src_rootdir = tasklist.header[0]
        if arg_srclist_prefix is not None:
            src_rootdir = os.path.join(arg_srclist_prefix, src_rootdir)

        if not os.path.isdir(src_rootdir):
            args.parser.error(
                "{} {}; source root directory in header must be an existing directory: {}".format(
                ARGSTR_SRCLIST_ROOTED, tasklist.tasklist_file, src_rootdir
            ))

        if args.get(ARGSTR_DSTDIR_GLOBAL) is not None:
            tasklist_dst_rootdir = args.get(ARGSTR_DSTDIR_GLOBAL)
        elif len(tasklist.header) == 2:
            tasklist_dst_rootdir = tasklist.header[1]
        else:
            tasklist_dst_rootdir = None

        sync_mode = SYNC_MODE_GLOBAL
        if sync_mode == ARGMOD_SYNC_MODE_NULL:
            # Assume user expects the new destination directory to mirror the source directory
            sync_mode = ARGMOD_SYNC_MODE_SYNC_TREE

        src_rootdir_dirname = os.path.basename(src_rootdir.rstrip(PATH_SEPARATORS_CAT))
        if tasklist_dst_rootdir is not None and sync_mode == ARGMOD_SYNC_MODE_TRANSPLANT_TREE:
            tasklist_dst_rootdir = os.path.join(tasklist_dst_rootdir, src_rootdir_dirname)

        task_type_is_list = (len(tasklist.tasks) > 0 and type(tasklist.tasks[0]) is list)
        for task in tasklist.tasks:
            if task_type_is_list:
                src_path = task[0]
                dst_rootdir = tasklist_dst_rootdir if tasklist_dst_rootdir is not None else task[1]
            else:
                src_path = task
                dst_rootdir = tasklist_dst_rootdir if tasklist_dst_rootdir is not None else arg_dst

            if arg_srclist_prefix_dst is not None:
                dst_rootdir = os.path.join(arg_srclist_prefix_dst, dst_rootdir)

            if os.path.isfile(dst_rootdir):
                args.parser.error(
                    "{} {}; destination root directory cannot be an existing file: {}".format(
                    ARGSTR_SRCLIST_ROOTED, tasklist.tasklist_file, dst_rootdir
                ))

            if arg_srclist_prefix is not None:
                src_path = os.path.join(arg_srclist_prefix, src_path)
            if arg_srclist_suffix is not None:
                src_path = src_path + arg_srclist_suffix

            if not src_path.startswith(src_rootdir):
                # Assume source path is relative from the source root directory
                src_path_from_root = src_path
                src_path = os.path.join(src_rootdir, src_path_from_root)

            if not arg_srclist_noglob and '*' in src_path:
                src_path_glob = glob.glob(src_path)
                if len(src_path_glob) == 0:
                    warning("{} {}; no source files found matching pattern: {}".format(
                        ARGSTR_SRCLIST_ROOTED, tasklist.tasklist_file, src_path
                    ))
            else:
                src_path_glob = [src_path]
            for src_path_single in src_path_glob:

                src_path_from_root = src_path_single.replace(src_rootdir, '') if src_path_single.startswith(src_rootdir) else src_path_single
                src_path_from_root = src_path_from_root.lstrip(PATH_SEPARATORS_CAT)
                if tasklist_dst_rootdir is None and sync_mode == ARGMOD_SYNC_MODE_TRANSPLANT_TREE:
                    dst_path_single = os.path.join(dst_rootdir, src_rootdir_dirname, src_path_from_root)
                else:
                    dst_path_single = os.path.join(dst_rootdir, src_path_from_root)

                all_task_list.append((src_path_single, dst_path_single))

    return all_task_list

def adjust_dst_path(src_path, dst_path, dst_can_be_file=False, dst_path_type=PATH_TYPE_UNKNOWN,
                    sync_mode_default=ARGMOD_SYNC_MODE_NULL, skip_dir_adjust=False):
    # global SYNC_MODE_GLOBAL

    if dst_path_type == PATH_TYPE_DIR or (dst_path_type == PATH_TYPE_UNKNOWN and os.path.isdir(dst_path)):
        if os.path.isfile(src_path):
            dst_path = os.path.join(dst_path, os.path.basename(src_path))
        elif not skip_dir_adjust:
            # src_path is a directory
            sync_mode = SYNC_MODE_GLOBAL if SYNC_MODE_GLOBAL != ARGMOD_SYNC_MODE_NULL else sync_mode_default
            if sync_mode == ARGMOD_SYNC_MODE_NULL:
                sync_mode = ARGMOD_SYNC_MODE_SYNC_TREE if psu_str.endswith_one_of_coll(src_path, PATH_SEPARATORS_LIST) else ARGMOD_SYNC_MODE_TRANSPLANT_TREE
            if sync_mode == ARGMOD_SYNC_MODE_TRANSPLANT_TREE:
                dst_path = os.path.join(dst_path, os.path.basename(src_path.rstrip(PATH_SEPARATORS_CAT)))
            if not psu_str.endswith_one_of_coll(dst_path, PATH_SEPARATORS_LIST):
                dst_path = dst_path+os.path.sep

    elif dst_path_type == PATH_TYPE_FILE or (dst_path_type == PATH_TYPE_UNKNOWN and os.path.isfile(dst_path)):
        if os.path.isdir(src_path):
            raise cerr.ScriptArgumentError(
                "source directory ({}) cannot overwrite existing destination file ({})".format(src_path, dst_path)
            )
        else:
            # src_path is a file
            pass

    else:
        # dst_path does not yet exist
        if os.path.isfile(src_path):
            if dst_can_be_file and not psu_str.endswith_one_of_coll(dst_path, PATH_SEPARATORS_LIST):
                # dst_path will be the exact path of the file copy
                pass
            else:
                dst_path = os.path.join(dst_path, os.path.basename(src_path))
        elif not skip_dir_adjust:
            # src_path is a directory; dst_path will be a new destination directory
            sync_mode = SYNC_MODE_GLOBAL if SYNC_MODE_GLOBAL != ARGMOD_SYNC_MODE_NULL else sync_mode_default
            if sync_mode == ARGMOD_SYNC_MODE_NULL:
                if (    not psu_str.endswith_one_of_coll(src_path, PATH_SEPARATORS_LIST)
                    and     psu_str.endswith_one_of_coll(dst_path, PATH_SEPARATORS_LIST)):
                    # Assume user expects the new destination directory to contain the mirrored source directory
                    sync_mode = ARGMOD_SYNC_MODE_TRANSPLANT_TREE
                else:
                    # Assume user expects the new destination directory to mirror the source directory
                    sync_mode = ARGMOD_SYNC_MODE_SYNC_TREE
            if sync_mode == ARGMOD_SYNC_MODE_TRANSPLANT_TREE:
                dst_path = os.path.join(dst_path, os.path.basename(src_path.rstrip(PATH_SEPARATORS_CAT)))
            if not psu_str.endswith_one_of_coll(dst_path, PATH_SEPARATORS_LIST):
                dst_path = dst_path+os.path.sep

    return dst_path


from collections import defaultdict
import sys
sys.setrecursionlimit(10 ** 5)
v, e = map(int, input().split())
graph = defaultdict(list)
count = [0 for _ in range(v+1)]
visited = [0 for _ in range(v+1)]

for _ in range(e):
    a, b = map(int, input().split())
    count[a] += 1
    count[b] += 1
    graph[a].append(b)
    graph[b].append(a)

def dfs(x):
    for nx in graph[x]:
        if not visited[nx]:
            visited[nx] = 1
            dfs(nx)

odd = 0 # 차수가 홀수인 정점 체크
for i in range(1, v+1):
    if count[i] % 2:
        odd += 1

# 차가 홀수인 정점이 2개 이하면 오일러 경로 조건 만족
if odd <= 2:
    visited[1] = 1
    dfs(1)
    if sum(visited) == v: # 모든 정점을 방문하는지 확인
        print("YES")
    else:
        print("NO")
else:
    print("NO")


#coding:utf-8
import pandas as pd
import numpy as np

class KNNa(object):

    #获取训练数据集
    def getTrainData(self):
        dataSet = pd.read_csv('iris.csv', header=None)
        dataSetNP = np.array(dataSet[1:150])
        trainData = dataSetNP[:,0:dataSetNP.shape[1]-1]   #获得训练数据
        labels = dataSetNP[:,dataSetNP.shape[1]-1]    #获得训练数据类别
        return trainData,labels

    #得到测试数据的类别
    def classify(self, testData, trainData, labels, k):
        #计算测试数据与训练数据之间的欧式距离
        dist = []
        for i in range(len(trainData)):
            td = trainData[i,:]   #训练数据
            dist.append(np.linalg.norm(testData - td))   #欧式距离 （平方和，再开方）
        dist_collection = np.array(dist)   #获得所有的欧氏距离，并转换为array类型
        dist_index = dist_collection.argsort()[0:k]   #按升序排列，获得前k个下标
        k_labels = labels[dist_index]   #获得对应下标的类别

        #计算k个数据中，类别的数目
        k_labels = list(k_labels)   #转换为list类型
        labels_count = {}
        for i in k_labels:
            labels_count[i] = k_labels.count(i)  #计算每个类别出现的次数
        testData_label = max(labels_count, key=labels_count.get)   #次数出现最多的类别
        return testData_label

if __name__ == '__main__':
    kn = KNNa()
    trainData,labels = kn.getTrainData()   #获得训练数据集,iris从第2行到第150行的149条数据
    testData = np.array([5.1, 3.5, 1.4, 0.2])   #取iris中的数据的第1行
    k = 10   #最近邻数据数目
    testData_label = kn.classify(testData,trainData,labels,k)    #获得测试数据的分类类别
    print('测试数据的类别：',testData_label)


import inspect
from typing import Union
from .. import _LOGGER

from ..const import (
    TEXT_UNKNOWN,
    WISERDEVICE,
    WiserAwayActionEnum,
    WiserDeviceModeEnum,
    WiserShutterAwayActionEnum,
)
from ..helpers.misc import is_value_in_list
from ..helpers.signal import _WiserSignalStrength
from ..rest_controller import _WiserRestController

class _WiserDevice(object):
    """Class representing a wiser heating device"""

    def __init__(
        self,
        wiser_rest_controller: _WiserRestController,
        endpoint: str,
        data: dict,
        device_type_data: dict,
        schedule: dict = None,
    ):
        self._data = data
        self._device_type_data = device_type_data
        self._wiser_rest_controller = wiser_rest_controller
        self._endpoint = endpoint

    async def _send_command(self, cmd: dict, device_level: bool = False):
        """
        Send control command to the smart plug
        param cmd: json command structure
        return: boolen - true = success, false = failed
        """
        if device_level:
            result = await self._wiser_rest_controller._send_command(
                WISERDEVICE.format(self.id), cmd
            )
            if result:
                self._data = result
        else:
            result = await self._wiser_rest_controller._send_command(
                self._endpoint.format(self.id), cmd
            )
            if result:
                self._device_type_data = result
        if result:
            _LOGGER.debug(
                "Wiser device - {} command successful".format(
                    inspect.stack()[1].function
                )
            )
            return True
        return False

    @property
    def device_lock_enabled(self) -> bool:
        """Get or set device lock"""
        return self._data.get("DeviceLockEnabled", False)

    async def set_device_lock_enabled(self, enabled: bool = False):
        if await self._send_command({"DeviceLockEnabled": enabled}, True):
            self._device_lock_enabled = enabled
            return True

    @property
    def device_type_id(self) -> int:
        """Get the device id for the specific device type"""
        return self._data.get("id")

    @property
    def identify(self) -> bool:
        """Get or set if the identify function is enabled"""
        return self._data.get("IdentifyActive", False)

    async def set_identify(self, enabled: bool = False):
        if await self._send_command({"Identify": enabled}, True):
            self._indentify_active = enabled
            return True

    @property
    def firmware_version(self) -> str:
        """Get firmware version of device"""
        return self._data.get("ActiveFirmwareVersion", TEXT_UNKNOWN)

    @property
    def id(self) -> int:
        """Get id of device"""
        return self._data.get("id")

    @property
    def model(self) -> str:
        """Get model of device"""
        # Lights and shutters currently have model identifier as Unknowm
        return (
            self._data.get("ProductType", TEXT_UNKNOWN)
            if self._data.get("ModelIdentifier") == TEXT_UNKNOWN
            else self._data.get("ModelIdentifier", TEXT_UNKNOWN)
        )

    @property
    def name(self) -> str:
        """Get name of device - ProductType + id"""
        return f"{self.product_type}-{self.id}"

    @property
    def node_id(self) -> int:
        """Get zigbee node id of device"""
        return self._data.get("NodeId", 0)

    @property
    def product_identifier(self) -> str:
        """Get product identifier of device"""
        return self._data.get("ProductIdentifier", TEXT_UNKNOWN)

    @property
    def product_model(self) -> str:
        """Get product model of device"""
        return self._data.get("ProductModel", TEXT_UNKNOWN)

    @property
    def parent_node_id(self) -> int:
        """Get zigbee node id of device this device is connected to"""
        return self._data.get("ParentNodeId", 0)

    @property
    def product_type(self) -> str:
        """Get product type of device"""
        return self._data.get("ProductType", TEXT_UNKNOWN)

    @property
    def room_id(self) -> int:
        """Get heating actuator room id"""
        return self._device_type_data.get("RoomId", 0)

    @property
    def serial_number(self) -> str:
        """Get serial number of device"""
        return self._data.get("SerialNumber", TEXT_UNKNOWN)

    @property
    def signal(self) -> _WiserSignalStrength:
        """Get zwave network information"""
        return _WiserSignalStrength(self._data)

class _WiserElectricalDevice(_WiserDevice):
    """Class representing a wiser electrical device"""

    def __init__(
        self,
        wiser_rest_controller: _WiserRestController,
        endpoint: str,
        data: dict,
        device_type_data: dict,
        schedule: dict,
    ):
        super().__init__(
            wiser_rest_controller, endpoint, data, device_type_data, schedule
        )
        self._schedule = schedule

        # Add device id to schedule
        if self._schedule:
            self._schedule._assignments.append(
                {"id": self.device_type_id, "name": self.name}
            )
            self._schedule._device_ids.append(self.id)

    @property
    def available_modes(self):
        """Get available modes"""
        return [mode.value for mode in WiserDeviceModeEnum]

    @property
    def available_away_mode_actions(self):
        """Get available away mode actions"""
        return [action.value for action in WiserAwayActionEnum]

    @property
    def away_mode_action(self) -> str:
        """Get or set the away action of the light (off or no change)"""
        return self._device_type_data.get("AwayAction", TEXT_UNKNOWN)

    async def set_away_mode_action(
        self, action: Union[WiserAwayActionEnum, WiserShutterAwayActionEnum, str]
    ) -> bool:
        if (
            type(action) == WiserAwayActionEnum
            or type(action) == WiserShutterAwayActionEnum
        ):
            action = action.value

        if is_value_in_list(action, self.available_away_mode_actions):
            if await self._send_command({"AwayAction": action}):
                self._away_action = action
                return True
        else:
            raise ValueError(
                f"{action} is not a valid away mode action.  Valid modes are {self.available_away_mode_actions}"
            )

    @property
    def device_type_id(self) -> int:
        """Get the device id for the specific device type"""
        return self._device_type_data.get("id")

    @property
    def mode(self) -> str:
        """Get or set the current mode of the light (Manual or Auto)"""
        return self._device_type_data.get("Mode", TEXT_UNKNOWN)

    async def set_mode(self, mode: Union[WiserDeviceModeEnum, str]) -> bool:
        if type(mode) == WiserDeviceModeEnum:
            mode = mode.value
        if is_value_in_list(mode, self.available_modes):
            return await self._send_command({"Mode": mode.title()})
        else:
            raise ValueError(
                f"{mode} is not a valid mode.  Valid modes are {self.available_modes}"
            )

    @property
    def name(self) -> str:
        return self._device_type_data.get("Name", TEXT_UNKNOWN)

    async def set_name(self, name: str) -> bool:
        if await self._send_command({"Name": name}):
            return True

    @property
    def schedule(self):
        """Get the schedule of the light"""
        return self._schedule

    @property
    def schedule_id(self) -> int:
        """Get the schedule id for the light"""
        return self._device_type_data.get("ScheduleId")


import csv

count = 0
list_duration = []

with open("submission.csv", "r") as File:
    reader = csv.reader(File)
    count = 0
    for i in reader:
        if count >= 1:
            list_duration.append(float(i[1]))
        elif count == 0:
            count += 1
            continue

max_duration = max(list_duration)

def create_special_object(duration_gt=0, duration_gte=0, duration_lt = max_duration, duration_lte = max_duration):
    data_special = []
    with open("submission.csv", "r") as File:
        reader = csv.reader(File)
        count_reader = 0 #переменная для контроля невывода первой строки документа "submission.csv"
        for line in reader:
            if count_reader == 0:
                count_reader += 1
                continue
            else:
                if float(line[1])  > duration_gt and float(line[1]) >= duration_gte and float(line[1]) < duration_lt and float(line[1]) <= duration_lte:
                    data_special.append((line[0], float(line[1])))
    return data_special

data = create_special_object(duration_gt=600, duration_gte=0, duration_lt = 1500, duration_lte = max_duration)

print(len(data))


import flask
import json
import base64
import flask_cors

app=flask.Flask(__name__)

flask_cors.CORS(app, resources={r"/api/*": {"origins": "*"}})

_RETURN_INVALID_REQUEST_PARAMETER = json.dumps({
    'Error': {
        'Code': 'NotParameterGet',
                'Message': 'Invalid request parameter got, check parameter'
    }
})

from modules.part_segementation.app import segementation
@app.route('/api/part_seg', methods=['post'])
def part():
    """
        Parameter
        ---
            - `image` (base64)
    """

    if not flask.request.data:  # check if valid data
        return _RETURN_INVALID_REQUEST_PARAMETER
    # parase paramter
    data_json = json.loads(flask.request.data.decode('utf-8'))

    if 'image' not in data_json:
        return json.dumps({
            'Error': {
                'Code': 'Part_seg.LossParameter',
                'Message': 'parameter "image" not found in request'
            }
        })
    try:
        # image_data = base64.b64decode(data_json['image'])
        # SELECT_IMAGE = image_data
        return json.dumps({
            'image':'data:image/jpeg;base64,'+segementation(data_json['image'])
        })
    except:
        return json.dumps({
            'Error': {
                'Code': 'UploadImage.DecodeImageError',
                'Message': 'decode image error'
            }
        })

if __name__=='__main__':
    app.run(
        host='127.0.0.1',
        # host='192.168.1.131',
        # port=2021
        )


# https://leetcode.com/problems/count-complete-tree-nodes/

# Definition for a binary tree node.
class TreeNode(object):
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

example_root = TreeNode(
    val=1,
    left=TreeNode(
        val=2,
        left=TreeNode(val=4),
        right=TreeNode(val=5)
    ),
    right=TreeNode(
        val=3,
        left=TreeNode(val=6)
    )
)

class Solution(object):
    def countNodes(self, root):
        counter = 0
        def rec(tree):
            nonlocal counter
            if tree:
                print(tree.val)
                counter += 1
                rec(tree.left)
                rec(tree.right)

        rec(root)
        return counter

print('res =', Solution().countNodes(example_root))  # res = 6


from datetime import datetime
from random import choice
from unittest import TestCase
from time import time

from notmany.store.base import Interval
from notmany.store.file import Store
from tests.utils import temporary_directory, dt, seconds

class SavingMetricsBaseTestCase(TestCase):

    def test_I_can_save_metrics(self):
        self.fail()

    def test_I_can_save_two_different_metrics(self):
        self.fail()

    def test_I_can_save_2_h_worth_of_metrics(self):
        self.fail()

    def test_I_can_save_48_h_worth_of_metrics(self):
        self.fail()

class SavingMetricsWeeksTestCase(TestCase):

    def setUp(self):
        self.start = dt('2018-03-03T12:30:00')

    def save_days_of_metric(self, store, days):
        cpu, mem = 10, 4400
        start = time()
        count = 3600 * 24 * days
        for i in range(count):
            steps = [-2, -1, 0, 1, 2]
            cpu += choice(steps)
            mem += choice(steps)
            store.record('temp', timestamp=self.start + seconds(i), data='cpu:{},mem:{}'.format(cpu, mem))
        print('Saving records {} took {} bucket size {}'.format(count, time() - start, store.bucket_size))

    def retrieve(self, store):
        start = time()
        records = list(store.retrieve(name='temp', interval=Interval(
            start=self.start,
            delta=seconds(3600 * 24 * 7)
        )))

        print('Retrieving {} records took {}'.format(len(records), time() - start))
        return records

    def test_foo(self):
        store = Store(directory='store', bucket_size=600)
        #self.save_days_of_metric(store=store, days=7)
        start = time()
        delta = seconds(3600 * 24)
        records = list(store.retrieve_raw(name='temp', interval=Interval(
            start=dt('2018-03-03T12:30:00'),
            delta=seconds(3600 * 24 * 21)
        )))

        print('Retrieving {} records took {}'.format(len(records), time() - start))

        # start = time()
        # records.sort()
        # print('Sorting took {}'.format(time() - start))

    def test_I_can_save_and_retrieve_1_week_of_one_metric(self):
        with temporary_directory() as tem_dir:
            store = Store(directory=tem_dir, bucket_size=600)
            self.save_days_of_metric(store=store, days=7)

            records = self.retrieve(store)
            start = time()
            records.sort()
            print('Sorting took {}'.format(time() - start))
            self.assertEqual(len(records), 3600 * 24 * 7)

    def test_I_can_save_and_retrieve_2_week_of_one_metric(self):
        with temporary_directory() as tem_dir:
            store = Store(directory=tem_dir, bucket_size=600)
            self.save_days_of_metric(store=store, days=14)

            start = time()
            records = list(store.retrieve(name='temp', interval=Interval(
                start=self.start,
                delta=seconds(3600 * 24 * 14)
            )))

            print('Retrieving {} records took {}'.format(len(records), time() - start))
            start = time()
            records.sort()
            print('Sorting took {}'.format(time() - start))
            self.assertEqual(len(records), 3600 * 24 * 14)

    def test_I_can_save_4_weeks_of_2_metrics(self):
        self.fail()

class ReadingMetricsTestCase(TestCase):

    def test_I_can_read_last_10_minutes(self):
        self.fail()

    def test_I_can_read_last_2_hours(self):
        self.fail()

    def test_I_can_read_last_48_hours(self):
        self.fail()

    def test_I_can_read_last_week(self):
        self.fail()

    def test_I_can_read_last_month(self):
        self.fail()


#! C:\Users\yeshw\Desktop\Umass\Geology_497\Lab4
"""
This code converts velocities

Yesh 3/2/2020
"""
inputUnits = input('Specify units (mph or m/s)')

if (inputUnits != 'mph' and inputUnits != 'm/s'):
    inputUnits = 'mph'

try:
    inputVel = float(input('Enter the velocity '))
except ValueError:
    inputVel = 10.0

if (inputUnits == 'mph'):
    try:
        convertUnit = float(input('Enter 1 to convert to m/s or Enter 2 to convert to Km/hr'))
    except ValueError:
        convertUnit = 1.0
    if (convertUnit == 1):
        print(inputVel, ' ' , inputUnits, ' = ', '{:.2f}'.format(inputVel/2.237), ' m/s')
    else:
        print(inputVel, ' ' , inputUnits, ' = ', '{:.2f}'.format(inputVel*1.609), ' km/hr')
else:
    try:
        convertUnit = float(input('Enter 1 to convert to mph or Enter 2 to convert to Km/hr'))
    except ValueError:
        convertUnit = 1.0
    if (convertUnit == 1):
        print(inputVel, ' ' , inputUnits, ' = ', '{:.2f}'.format(inputVel*2.237), ' mph')
    else:
        print(inputVel, ' ' , inputUnits, ' = ', '{:.2f}'.format(inputVel*3.6), ' km/hr')


def validate(name):
    flag_count = 0

    if len(name) == 10:
        flag_count += 1

    name_list = list(name)
    name_set = set(name_list)
    if len(name_set) == len(name_list):
        flag_count += 1

    if name.isalnum():
        flag_count += 1

    num_list = []
    for char in name:
        if char.isnumeric():
            num_list.append(char)
    if len(num_list) >= 3:
        flag_count += 1

    upper_list = []
    for char in name:
        if char.isupper():
            upper_list.append(char)
    if len(upper_list) >= 2:
        flag_count += 1

    if flag_count == 5:
        print("Valid")
    else:
        print("Invalid")

if __name__ == '__main__':
    n = int(input())
    for i in range(n):
        uid = str(input())
        validate(uid)


from django.shortcuts import render, redirect
from .helpers import load_books_from_file, save_books_to_file, delete_book_from_file,get_book_index_by_id,search_books_in_file
from .models import Book
from django.shortcuts import redirect, HttpResponse

def book_list(request):
    books = load_books_from_file()
    return render(request, 'library/book_list.html', {'books': books})

def add_book(request):
    if request.method == 'POST':
        title = request.POST['title']
        author = request.POST['author']
        genre = request.POST['genre']
        height = request.POST['height']
        publisher = request.POST['publisher']
        books = load_books_from_file()
        book_ids = [book['book_id'] for book in books]
        book_id = max(book_ids) + 1 if book_ids else 1
        book = {
            'book_id': book_id,
            'title': title,
            'author': author,
            'genre': genre,
            'height': int(height),
            'publisher': publisher
        }
        books.append(book)
        save_books_to_file(books)
        return redirect('book_list')
    return render(request, 'library/add_book.html')

def delete_book(request, book_id):
    books = load_books_from_file()
    book_index = get_book_index_by_id(int(book_id), books)
    if book_index is not None:
        book = books[book_index]
        success = delete_book_from_file(book)
        if success:
            return redirect('book_list')
    return HttpResponse('Book not found.')

def search_books(request):
    if request.method == 'POST':
        name = request.POST['name']
        genre = request.POST['genre']
        books = search_books_in_file(name, genre)
        return render(request, 'library/book_list.html', {'books': books})
    return redirect('book_list')


#!/usr/bin/env python

"""PatternCalibration Detection Tests

use `python -m plant_detection.tests.test_pattern_detection /images/`

For Plant Detection.
"""
import os
import sys
import cv2
import numpy as np
from plant_detection.PatternCalibration import PatternCalibration

def result(found):
    'Generate result text.'
    return '\033[92mOK\033[0m' if found else '\033[91mMISS\033[0m'

def save_output(filepath):
    'Save side-by-side pattern detection image results.'
    def _fn(ret, img, original):
        output_dir = '{}/output/'.format(os.path.dirname(filepath))
        if not os.path.exists(output_dir):
            os.mkdir(output_dir)
        img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        img = np.concatenate((original, img), axis=1)
        if ret:
            text = 'OK'
            color = (0, 255, 0)
        else:
            text = 'MISS'
            color = (0, 0, 255)
        font = cv2.FONT_HERSHEY_SIMPLEX
        location = (original.shape[1] - 50, 50)
        img = cv2.putText(img, text, location, font, 2, color, 10)
        cv2.imwrite(output_dir + filepath.split('/')[-1], img)
    return _fn

def process_images(directory):
    'Detect pattern in images in directory.'
    pattern_calibration = PatternCalibration({})
    img_files = [f for f in os.listdir(directory) if '.' in f]
    count = 0
    for filename in img_files:
        img_path = directory + filename
        print(img_path)
        if os.path.exists(img_path):
            img = cv2.imread(img_path)
            ret, _centers = pattern_calibration.find_pattern(
                img, save_output=save_output(img_path))
            print('{:<16} {}'.format(result(ret), img_path))
            count += 1
        else:
            print('path not found:', img_path)

    if count > 0:
        print('{} result images saved to {}'.format(
            count, input_dir + 'output/'))

if __name__ == '__main__':
    input_dir = sys.argv[1]
    process_images(input_dir)


from model.db_connect import pyodbc_connect as odbc
import logging
from datetime import datetime
import pandas as pd
import numpy as np
from datetime import datetime
from azure.logging_setup import setup_azure_logger
logger = setup_azure_logger(__name__)

def select_dantai_nm(dantai_cd):
    query = "SELECT dantai_nm " \
            "FROM dantai " \
            "WHERE dantai_cd ={}".format(dantai_cd)
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def select_file_ids(dantai_cd):
    query = "SELECT 不動産登記簿ファイルID "\
            "FROM v_toukibo_bukken "\
            "WHERE 全国地方公共団体コード = {} " \
            "GROUP BY 不動産登記簿ファイルID".format(dantai_cd)
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def select_chiban_rireki_by_file_id(file_id):
    query = "SELECT 不動産登記簿ファイルID "\
            "FROM toukibo_chiban_rireki "\
            "WHERE 不動産登記簿ファイルID = {}".format(file_id)
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def delete_chiban_rireki_by_file_id(file_id, cursor):
    query = "DELETE FROM toukibo_chiban_rireki "\
            "WHERE 不動産登記簿ファイルID = {}".format(file_id)
    try:
        return cursor.execute(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def select_bukken_view(dantai_cd, file_id):
    query = "SELECT * " \
            "FROM v_toukibo_bukken " \
            "WHERE 全国地方公共団体コード ={0} "\
            "AND 不動産登記簿ファイルID = {1}".format(dantai_cd, file_id)
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def select_hyoujirireki_view(dantai_cd, file_id):
    query = "SELECT * " \
            "FROM v_toukibo_hyouji_rireki " \
            "WHERE 全国地方公共団体コード ={0}"\
            "AND 不動産登記簿ファイルID = {1} "\
            "ORDER BY 全国地方公共団体コード, 不動産登記簿ファイルID, 項番, 表示履歴番号".format(dantai_cd, file_id)
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def select_shoyusha_view(dantai_cd, file_id):
    query = "SELECT * " \
            "FROM v_toukibo_shoyuken " \
            "WHERE 全国地方公共団体コード ={0}"\
            "AND 不動産登記簿ファイルID = {1}".format(dantai_cd, file_id)
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def select_chibanrireki_view(dantai_cd):
    query = "SELECT * " \
            "FROM v_chiban_rireki " \
            "WHERE 全国地方公共団体コード ='{}'".format(dantai_cd)
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def select_v_jusho_code_meishou(dantai_cd):

    query = "SELECT DISTINCT " \
            "市区町村コード " \
            ",大字コード" \
            ",大字名 " \
            "FROM dbo.v_juusho_code_meishou " \
           "WHERE 市区町村コード = '{}'".format(dantai_cd)
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def select_sikuchouson_codes(str_sikuchoson_code):
    query = "SELECT " \
            "DISTINCT 市区町村コード " \
            "FROM dbo.v_toukibo_bukken " \
           f"WHERE 全国地方公共団体コード IN ({str_sikuchoson_code})"
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def change_ooaza_kokyo_ninni_view(dantai_cd):
    query = "SELECT * " \
            "FROM v_change_ooaza_koukyou_ninni " \
            "WHERE 全国地方公共団体コード ='{}'".format(dantai_cd)
    try:
        return odbc.exec_query(query)
    except Exception as e:
        logging.error('An exception occurred: {}'.format(e))
        raise e

def query_insert_chibanrireki(items, cursor):
    values=""
    file_id = items.at[0, "不動産登記簿ファイルID"]
    logger.info(f"紐づけ管理Function:HB002:insert_chibanrireki:file_id:{file_id}, length:{len(items)}")
    for index,colums in items.iterrows():
        hudousan_file_id=colums["不動産登記簿ファイルID"]
        koban=colums["項番"]
        henkou_rireki=colums["変更履歴"]
        kousingappi=colums["更新年月日"]
        if colums["更新年月日"] is np.nan or colums["更新年月日"]=='':
            kousingappi='NULL'
        if kousingappi!='NULL':
            kousingappi=f"'{kousingappi}'"
        sakujyo_flag=colums["削除フラグ"]
        henkou_go=colums["変更後"]
        ooaza=colums["大字名"]
        koaza=colums["小字名"]
        if colums["小字名"] is np.nan:
            koaza='NULL'
        else:
            koaza_tmp=colums["小字名"]
            koaza=f"'{koaza_tmp}'"
        if colums["小字名"] =="":
            koaza='NULL'
        if colums["小字名"] == "nan":
            koaza='NULL'
        chiban=colums["地番"]
        chimoku=colums["地目"]

        if index==0:
            values+=f"({hudousan_file_id},{koban},N'{henkou_rireki}',{kousingappi},'{sakujyo_flag}',N'{henkou_go}',N'{ooaza}',{koaza},N'{chiban}',N'{chimoku}') "
        if index>0 and values!="":
            values+=f",({hudousan_file_id},{koban},N'{henkou_rireki}',{kousingappi},'{sakujyo_flag}',N'{henkou_go}',N'{ooaza}',{koaza},N'{chiban}',N'{chimoku}') "
        if values=="":
            values+=f"({hudousan_file_id},{koban},N'{henkou_rireki}',{kousingappi},'{sakujyo_flag}',N'{henkou_go}',N'{ooaza}',{koaza},N'{chiban}',N'{chimoku}') "

        if index%500==0 and index!=0:
            query = "INSERT INTO toukibo_chiban_rireki ("\
                    "不動産登記簿ファイルID, 項番, 変更履歴, 更新年月日, 削除フラグ, 変更後, 大字名, 小字名, 地番, 地目) "\
                    f"VALUES {values}"

            cursor.execute(query)
            values=""

    if(len(values) > 0):
        query = "INSERT INTO toukibo_chiban_rireki ("\
                "不動産登記簿ファイルID, 項番, 変更履歴, 更新年月日, 削除フラグ, 変更後, 大字名, 小字名, 地番, 地目) "\
                f"VALUES {values}"
        # logger.info(f"query002:{query}")
        cursor.execute(query)

def query_delete_chibanrireki(toukibo_id_list,cursor):
    query = "DELETE FROM toukibo_chiban_rireki "\
            f"WHERE 不動産登記簿ファイルID IN ({toukibo_id_list})"
    cursor.execute(query)

def query_update_chibanrireki(merged_toukibo,cursor):
    values=""
    for index,columns in merged_toukibo.iterrows():
        chiban_rireki_id = columns["地番履歴ID"]
        ooaza = columns["大字名"]
        values+=f"WHEN 地番履歴ID ={chiban_rireki_id} "\
        f"THEN '{ooaza}' "

    query = "UPDATE toukibo_chiban_rireki SET "\
            f"大字名= CASE {values} "\
            "END;"
    #logger.info(f"query002:{query}")
    cursor.execute(query)


# @Author    : 小杜同学
# @Email       : anmutu@hotmail.com
# @Github     : www.github.com/anmutu
# @怎么肥四  : https://www.zmfei4.com
# @Time        : 2020/5/18 23:40

# https://leetcode-cn.com/problems/plus-one/

# 给定一个由整数组成的非空数组所表示的非负整数，在该数的基础上加一。
# 最高位数字存放在数组的首位， 数组中每个元素只存储单个数字。
# 你可以假设除了整数 0 之外，这个整数不会以零开头

# example
# 输入: [4,3,2,1]
# 输出: [4,3,2,2]

def plus_one(digits):
    """
    把数组转换成数字。
    数字+1
    然后转换回去
    """
    digits = [str(i) for i in digits]
    num = int("".join(digits))
    num += 1
    digits = [int(i) for i in str(num)]
    return digits

if __name__ == "__main__":
    digits = [4, 3, 2, 1]
    res = plus_one(digits)
    print(res)

# 已经两个链接表A为：1->2->3
# B :4->5->6
# 使之结果为9->7->5


from __future__ import print_function
import os
import re
import codecs
import copy
import numpy as np
np.random.seed(0)
import random
random.seed(0)
from collections import Counter
import torch

def create_dico(item_list):
    """
    Create a dictionary of items from a list of list of items.
    """
    assert type(item_list) is list
    dico = {}
    for items in item_list:
        for item in items:
            if item not in dico:
                dico[item] = 1
            else:
                dico[item] += 1
    return dico

def create_mapping(dico):
    """
    Create a mapping (item to ID / ID to item) from a dictionary.
    Items are ordered by decreasing frequency.
    """
    sorted_items = sorted(dico.items(), key=lambda x: (-x[1], x[0]))
    id_to_item = {i: v[0] for i, v in enumerate(sorted_items)}
    item_to_id = {v: k for k, v in id_to_item.items()}
    return item_to_id, id_to_item

def augment_with_pretrained(dictionary, ext_emb_path, words):
    """
    Augment the dictionary with words that have a pretrained embedding.
    If `words` is None, we add every word that has a pretrained embedding
    to the dictionary, otherwise, we only add the words that are given by
    `words` (typically the words in the development and test sets.)
    """
    print('Loading pretrained embeddings from %s...' % ext_emb_path)
    assert os.path.isfile(ext_emb_path)

    # Load pretrained embeddings from file
    pretrained = set([
        line.rstrip().split()[0].strip()
        for line in codecs.open(ext_emb_path, 'r', 'utf-8')
        if len(ext_emb_path) > 0
    ])

    if words is None:
        for word in pretrained:
            if word not in dictionary:
                dictionary[word] = 0
    else:
        for word in words:
            if any(x in pretrained for x in [
                word,
                word.lower(),
                re.sub('\d', '0', word.lower())
            ]) and word not in dictionary:
                dictionary[word] = 0

    word_to_id, id_to_word = create_mapping(dictionary)
    return dictionary, word_to_id, id_to_word

def tag_mapping(dataset):
    """
    Create a dictionary and a mapping of tags, sorted by frequency.
    """
    tags = [s[1] for s in dataset]
    dico = Counter(tags)
    tag_to_id, id_to_tag = create_mapping(dico)
    print("Found %i unique named entity tags" % len(dico))
    return dico, tag_to_id, id_to_tag

def prepare_dataset(dataset, word_to_id, tag_to_id):
    """
    Prepare the dataset. Return a list of lists of dictionaries containing:
        - word indexes
        - word char indexes
        - tag indexes
    """
    def f(x): return x.lower()
    data = []
    for s in dataset:
        str_words = [w for w in s[0].split()]
        words = [word_to_id[f(w) if f(w) in word_to_id else '<UNK>']
                 for w in str_words]
        tag = tag_to_id[s[1]]
        data.append({
            'str_words': str_words,
            'words': words,
            'tag': tag,
        })
    return data

def pad_seq(seq, max_length, PAD_token=0):

    seq += [PAD_token for i in range(max_length - len(seq))]
    return seq

def create_batches(dataset, batch_size, order='keep'):

    newdata = copy.deepcopy(dataset)
    if order=='sort':
        newdata.sort(key = lambda x:len(x['words']))
    elif order=='random':
        random.shuffle(newdata)

    newdata = np.array(newdata)
    batches = []
    num_batches = np.ceil(len(dataset)/float(batch_size)).astype('int')

    for i in range(num_batches):
        batch_data = newdata[(i*batch_size):min(len(dataset),(i+1)*batch_size)]

        words_seqs = [itm['words'] for itm in batch_data]
        target_seqs = [itm['tag'] for itm in batch_data]
        str_words_seqs = [itm['str_words'] for itm in batch_data]

        seq_pairs = sorted(zip(words_seqs, target_seqs, str_words_seqs,
                               range(len(words_seqs))), key=lambda p: len(p[0]), reverse=True)

        words_seqs, target_seqs, str_words_seqs, sort_info = zip(*seq_pairs)

        words_lengths = np.array([len(s) for s in words_seqs])
        words_padded = np.array([pad_seq(s, np.max(words_lengths)) for s in words_seqs])
        words_mask = (words_padded!=0).astype('int')

        outputdict = {'words':words_padded, 'tags': target_seqs, 'wordslen': words_lengths,
                      'tagsmask':words_mask, 'str_words': str_words_seqs, 'sort_info': sort_info}

        batches.append(outputdict)

    return batches

def log_gaussian(x, mu, sigma):
    return float(-0.5 * np.log(2 * np.pi) - np.log(np.abs(sigma))) - (x - mu)**2 / (2 * sigma**2)

def log_gaussian_logsigma(x, mu, logsigma):
    return float(-0.5 * np.log(2 * np.pi)) - logsigma - (x - mu)**2 / (2 * torch.exp(logsigma)**2)

def bayes_loss_function(l_pw, l_qw, l_likelihood, n_batches, batch_size):
    return ((1./n_batches) * (l_qw - l_pw) - l_likelihood).sum() / float(batch_size)


from uuid import UUID

from fastapi import Depends
from warehouse.api_client.client import WarehouseGeneralClient
from warehouse.models.warehouse import WarehouseListFilters, WarehouseModel

from svc.api.errors.warehouse import WarehouseNotFoundError
from svc.infrastructure.warehouse.models import WarehouseShortModel

class WarehouseManager:
    def __init__(
        self,
        warehouse_client: WarehouseGeneralClient = Depends(WarehouseGeneralClient.instance),
    ) -> None:
        self._warehouse_client = warehouse_client

    async def get_warehouses(self, warehouse_ids: list[UUID]) -> list[WarehouseModel]:
        if not warehouse_ids:
            return []

        response = await self._warehouse_client.warehouse.list_warehouses(
            WarehouseListFilters(
                warehouse_ids=warehouse_ids,
                offset=0,
                limit=len(warehouse_ids),
            )
        )

        if response.error is not None:
            raise Exception(response.error.code)

        if not response.result:
            return []

        return response.result.items

    async def get_single_warehouse(self, warehouse_id: UUID) -> WarehouseShortModel:
        response = await self._warehouse_client.warehouse.get_warehouse(warehouse_id=warehouse_id)
        if response.error is not None:
            raise Exception(response.error.code)

        if not response.result:
            raise WarehouseNotFoundError()

        res: WarehouseModel = response.result
        return WarehouseShortModel(
            id=res.id,
            active=res.active,
            tz=res.tz,
        )


# program to check if two arrays have same elements

def checkTwoArrays(A, B):
    table = {}
    for a in A:
        if a in table:
            table[a] += 1
        elif a not in table:
            table[a] = 1
        else:
            table[a] += 0
    # print(table)

    for b in B:
        if b in table and table[b] > 0:
            table[b] -= 1

    for element in table:
        if table[element] != 0:
            print('Arrays are different')
            return

    print('Arrays have same elements')

A = [3, 63, 2, 7, 3, 8, 6, 358, 53, 1]
B = [3, 6, 358, 53, 1, 3, 63, 2, 7]

checkTwoArrays(A, B)


import argparse
import logging
import os
import signal
import config
from multiprocessing import Process, Event as MEvent
from threading import Thread, Event
from time import sleep
from typing import List, Union

from kafka.coordinator.assignors.range import RangePartitionAssignor
from kafka.coordinator.assignors.roundrobin import RoundRobinPartitionAssignor

from consumer.common.message_handler import MessageHandler
from consumer.concurrent.concurrent_consumer import ConcurrentKafkaConsumer
from consumer.parallel.parallel_consumer import ParallelKafkaConsumer

parser = argparse.ArgumentParser()
parser.add_argument('--mode', choices=['concurrent', 'parallel'])

logging.basicConfig(level=logging.DEBUG,
                    format="[%(asctime)s.%(msecs)03d][%(threadName)s][%(levelname)s][%(name)s] - %(message)s",
                    datefmt='%H:%M:%S')

config = {
    'bootstrap_servers': config.KAFKA_BOOTSTRAP,
    'api_version': (2, 1, 0),
    'group_id': 'my_consumer',
    'client_id': 'my_client_id',
    'enable_auto_commit': False,
    'auto_offset_reset': 'earliest',
    'partition_assignment_strategy': (RangePartitionAssignor, RoundRobinPartitionAssignor)
}

def start_concurrent_consumer(topic, app_reference):
    logging.getLogger('kafka').setLevel(logging.WARNING)
    c = ConcurrentKafkaConsumer(
        kafka_config=config,
        event_handlers={'Event': MessageHandler()},
        max_workers=2,
        topic=topic)
    app_reference.append(c)
    c.start()

def start_parallel_consumer(topic):
    logging.basicConfig(level=logging.DEBUG,
                        format="[%(asctime)s.%(msecs)03d][%(threadName)s][%(levelname)s][%(name)s] - %(message)s",
                        datefmt='%H:%M:%S')
    logging.getLogger('kafka').setLevel(logging.WARNING)
    logging.getLogger().info('Starting topic %s... on PID: %s', topic, os.getpid())
    c = ParallelKafkaConsumer(
        kafka_config=config,
        event_handlers={'Event': MessageHandler()},
        max_workers=2,
        topic=topic)
    signal.signal(signal.SIGTERM, c.stop)
    signal.signal(signal.SIGINT, c.stop)
    c.start()

thread_stop_event = Event()
process_stop_event = MEvent()

def start_and_wait(pool: List[Union[Thread, Process]], stop_event):
    [p.start() for p in pool]

    while not stop_event.wait(5):
        for p in pool:
            if not p.is_alive():
                logging.getLogger().warning("%s crashed", p.name)
                # signal.raise_signal(signal.SIGINT)

    # Wait for graceful shutdown
    while all([p.is_alive() for p in pool]):
        logging.info('Waiting for shutdown... %s', [p.is_alive() for p in pool])
        sleep(1)

def _handle_stop_thread(*args):
    logging.info('Setting Threading stop event... %s', args)
    thread_stop_event.set()
    logging.info('Stopping consumers %s', [c for c in consumer_references])
    [c.stop() for c in consumer_references]

def _handle_stop_process(*args):
    logging.info('Setting Processing stop event...')
    process_stop_event.set()

if __name__ == '__main__':
    program_args = parser.parse_args()

    if program_args.mode == 'concurrent':
        signal.signal(signal.SIGTERM, _handle_stop_thread)
        signal.signal(signal.SIGINT, _handle_stop_thread)

        consumer_references = []

        threads = [
            # ConsumerThread-T1 will subscribe and handle all partitions of topic T1
            Thread(target=start_concurrent_consumer, args=('T1', consumer_references), daemon=False, name='ConsumerThread-T1'),
            # ConsumerThread-T2 will subscribe and handle all partitions of topic T2
            Thread(target=start_concurrent_consumer, args=('T2', consumer_references), daemon=False, name='ConsumerThread-T2')
        ]

        start_and_wait(threads, thread_stop_event)

    else:
        signal.signal(signal.SIGTERM, _handle_stop_process)
        signal.signal(signal.SIGINT, _handle_stop_process)

        processes = [
            # ConsumerProcess-T1 will subscribe and handle all partitions of topic T1
            Process(target=start_parallel_consumer, args=('T1',), daemon=False, name='ConsumerProcess-T1'),
            # ConsumerProcess-T2 will subscribe and handle all partitions of topic T2
            Process(target=start_parallel_consumer, args=('T2',), daemon=False, name='ConsumerProcess-T2')
        ]

        start_and_wait(processes, process_stop_event)


""" aoc_03_2.py

    Solution for Advent of Code 2017 day 03 part 2
"""

import itertools

import aoc_03_input

def get_square(num):
    ''' get_square(num)

        Return the numth square in the spiral.
    '''
    return list(generate_spiral(num))[num-1]

def generate_spiral(max_square):
    ''' spiral(max_square)

        A generator that generates a spiral according to Advent of Code 2017,
        day 03 part 2.
    '''

    spiral = {}

    current_dir = (0, 1)
    cord = (0, 0)

    for i in range(0, max_square):
        if i > max_square:
            break

        spiral[cord] = value(cord, spiral)
        yield {'cord': cord, 'value': spiral[cord]}

        current_dir = get_direction(cord, current_dir, spiral)
        cord = (cord[0] + current_dir[0], cord[1] + current_dir[1])

def get_direction(cord, current_direction, spiral):
    ''' get_direction(cord, current_direction, spiral)

        Given a cordinate and the current_direction, it checks whatever turning
        now would end up in a non occupied square, if so, return the new
        direction, otherwise we keep going in the same direction.
    '''

    new_direction = next_direction(current_direction)

    check_cord = (cord[0] + new_direction[0],
                  cord[1] + new_direction[1])

    if spiral.get(check_cord):
        return current_direction

    return new_direction

def next_direction(direction):
    ''' next_direction(direction)

        Maps a given direction to a new direction so that we turn left all the
        time in the spiral.
    '''
    direction_mappings = {
        (0, 1): (1, 0),
        (1, 0): (0, -1),
        (0, -1): (-1, 0),
        (-1, 0): (0, 1)
    }
    return direction_mappings[direction]

def value(cord, spiral):
    ''' value(cord, spiral)

        Get all values from neighbouring squares, and sums them up which is the
        value the square at cord should have.
    '''
    if cord == (0, 0):
        return 1

    neightbours_offsets = list(itertools.product([1, 0, -1], [1, 0, -1]))
    neightbours_offsets.remove((0, 0))  # Not interested in ourself

    neighbour_values = map(lambda offset: spiral.get((cord[0] + offset[0],
                                                      cord[1] + offset[1]), 0),
                           neightbours_offsets)
    return sum(neighbour_values)

def main():
    ''' main()

        Main function that use the input from Advent of Code and print the
        answer for day threes problem.
    '''
    target = aoc_03_input.get_input()

    # The square we want will always be in a square earlier than the target
    # value
    for square in generate_spiral(target):
        if square['value'] > target:
            print(square['value'])
            break

if __name__ == '__main__':
    main()


# 1. Імпортувати модуль pygame
import pygame
# 2. Імпортувати модуль os
import os
# 3. Імпортувати модуль random
import random
# 4. Ініціалізувати налаштування pygame
pygame.init()
# 5. Створити функцію create_path, що знаходить абсолютний шлях до файлу.
# Застосовуємо модуль os.path та його методи abspath(), join()
def create_path(file):
    return os.path.join(os.path.abspath(__file__+"/.."),file)
# 6. Створити клас Sprite, в методі init задати 5 параметрів зі значенням за замовчуванням None
    # - координата x
    # - координата y
    # - ширина
    # - висота
    # - ім'я зображення
class Sprite:
    def __init__(self,x=None,y=None,width=None,height=None,name_img=None):
        # 7. Задати 7 властивостей:
        # - X
        # - Y
        # - WIDTH
        # - HEIGHT
        # - NAME_IMG
        # - IMAGE
        self.X = x
        self.Y = y
        self.WIDTH = width
        self.HEIGHT = height
        self.NAME_IMG = name_img
        self.IMAGE = None
    # 8. Створити метод завантаження зображення до властивості IMAGE
    def load_image(self):
        # - створи змінну path, яка зберігає абсолютний шлях до файлу зображення, застосовуємо функцію create_path
        # - завантажити зображення до всластивості IMAGE
        # - трансформувати збережене в IMAGE зображення до розмірів,
        #   що збережені у властивостях WIDTH, HEIGHT. Та зберегти повторно у властивості IMAGE.
        self.IMAGE = create_path(self.NAME_IMG)
        self.IMAGE = pygame.image.load(self.IMAGE)
        self.IMAGE = pygame.transform.scale(self.IMAGE,(self.WIDTH,self.HEIGHT))
    # 9. Створити метод, що промальовує завантажене зображення із властивості IMAGE, метод приймає один параметр - ігрове вікно
    def blit_sprite(self,play):
        # - застосовуємо метод blit() модуля pygame
        play.blit(self.IMAGE,(self.X,self.Y))
# 8. За класом створюємо ігровое вікно з ім'ям win
play = pygame.display.set_mode((500,500))
# 9. Задаємо назву ігрового вікна
pygame.display.set_caption("Привет, Николай!")
# 10. Створюємо основну функцію гри run_game:
def run_game():
    # - створюємо метод clock
    fps = pygame.time.Clock()
    # - створюємо об'єкт класу Sprite з ім'ям sprite,
    sprite = Sprite(0,0,250,250,"nanyat.png")
    sprite.load_image()
    # - задаємо змінну game, що відповідає за роботу циклу
    game = True
    # - задаємо ігровий цикл while,
    while game == True:
    # - задаємо умову закриття ігрового вікна,
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game = False
    # - задаємо фон ігрового вікна (мотод fill)
        play.fill((0,255,80))
    # - задіємо об'єкт sprite і викликаємо його метод blit_sprite(), малюємо зображення на ігровому вікні, в центрі екрану
        sprite.blit_sprite(play)
        fps.tick(random.randint(0,60))
        pygame.display.flip()
    # - задаємо оновлення ігрового екрану
# 11. І найголовніше – викликаємо основну функцію гри
run_game()