# Copyright (c) 2020, TU Wien, Department of Geodesy and Geoinformation
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#    * Redistributions of source code must retain the above copyright notice,
#      this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#      documentation and/or other materials provided with the distribution.
#    * Neither the name of TU Wien, Department of Geodesy and Geoinformation
#      nor the names of its contributors may be used to endorse or promote
#      products derived from this software without specific prior written
#      permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL TU WIEN DEPARTMENT OF GEODESY AND
# GEOINFORMATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""
Readers for lvl1b and lvl2 data in bufr format.
"""

import os
from datetime import datetime, timedelta
import warnings
from tempfile import NamedTemporaryFile
from gzip import GzipFile

import numpy as np

from pygeobase.io_base import ImageBase
from pygeobase.io_base import MultiTemporalImageBase
from pygeobase.io_base import IntervalReadingMixin
from pygeobase.object_base import Image

try:
    from pybufr_ecmwf import raw_bufr_file
    from pybufr_ecmwf import ecmwfbufr
    from pybufr_ecmwf import ecmwfbufr_parameters
except ImportError:
    warnings.warn(
        'pybufr-ecmwf can not be imported, H08 and H07 images can '
        'not be read.')

class AscatL1BufrFile(ImageBase):
    """
    Reads ASCAT L1b data in BUFR format.

    Parameters
    ----------
    filename : str
        Filename path.
    mode : str, optional
        Opening mode. Default: r
    msg_name_lookup: dict, optional
        Dictionary mapping bufr msg number to parameter name.
        See :ref:`ascatformattable`.

        Default:

             === =====================================================
             Key Value
             === =====================================================
             4: "Satellite Identifier",
             6: "Direction Of Motion Of Moving Observing Platform",
             16: "Orbit Number",
             17: "Cross-Track Cell Number",
             21: "f_Beam Identifier",
             22: "f_Radar Incidence Angle",
             23: "f_Antenna Beam Azimuth",
             24: "f_Backscatter",
             25: "f_Radiometric Resolution (Noise Value)",
             26: "f_ASCAT KP Estimate Quality",
             27: "f_ASCAT Sigma-0 Usability",
             34: "f_ASCAT Land Fraction",
             35: "m_Beam Identifier",
             36: "m_Radar Incidence Angle",
             37: "m_Antenna Beam Azimuth",
             38: "m_Backscatter",
             39: "m_Radiometric Resolution (Noise Value)",
             40: "m_ASCAT KP Estimate Quality",
             41: "m_ASCAT Sigma-0 Usability",
             48: "m_ASCAT Land Fraction",
             49: "a_Beam Identifier",
             50: "a_Radar Incidence Angle",
             51: "a_Antenna Beam Azimuth",
             52: "a_Backscatter",
             53: "a_Radiometric Resolution (Noise Value)",
             54: "a_ASCAT KP Estimate Quality",
             55: "a_ASCAT Sigma-0 Usability",
             62: "a_ASCAT Land Fraction"
             === =====================================================
    """

    def __init__(self, filename, mode='r', msg_name_lookup=None, **kwargs):
        """
        Initialization of i/o object.

        """
        zipped = False
        if os.path.splitext(filename)[1] == '.gz':
            zipped = True

        # for zipped files use an unzipped temporary copy
        if zipped:
            with NamedTemporaryFile(delete=False) as tmp_fid:
                with GzipFile(filename) as gz_fid:
                    tmp_fid.write(gz_fid.read())
                filename = tmp_fid.name

        super(AscatL1BufrFile, self).__init__(filename, mode=mode,
                                              **kwargs)
        if msg_name_lookup is None:
            msg_name_lookup = {
                4: "Satellite Identifier",
                6: "Direction Of Motion Of Moving Observing Platform",
                16: "Orbit Number",
                17: "Cross-Track Cell Number",
                21: "f_Beam Identifier",
                22: "f_Radar Incidence Angle",
                23: "f_Antenna Beam Azimuth",
                24: "f_Backscatter",
                25: "f_Radiometric Resolution (Noise Value)",
                26: "f_ASCAT KP Estimate Quality",
                27: "f_ASCAT Sigma-0 Usability",
                34: "f_ASCAT Land Fraction",
                35: "m_Beam Identifier",
                36: "m_Radar Incidence Angle",
                37: "m_Antenna Beam Azimuth",
                38: "m_Backscatter",
                39: "m_Radiometric Resolution (Noise Value)",
                40: "m_ASCAT KP Estimate Quality",
                41: "m_ASCAT Sigma-0 Usability",
                48: "m_ASCAT Land Fraction",
                49: "a_Beam Identifier",
                50: "a_Radar Incidence Angle",
                51: "a_Antenna Beam Azimuth",
                52: "a_Backscatter",
                53: "a_Radiometric Resolution (Noise Value)",
                54: "a_ASCAT KP Estimate Quality",
                55: "a_ASCAT Sigma-0 Usability",
                62: "a_ASCAT Land Fraction"}
        self.msg_name_lookup = msg_name_lookup

    def read(self, timestamp=None):
        """
        Read specific image for given datetime timestamp.

        Parameters
        ----------
        timestamp : datetime.datetime
            exact observation timestamp of the image that should be read

        Returns
        -------
        data : dict
            dictionary of numpy arrays that hold the image data for each
            variable of the dataset
        metadata : dict
            dictionary of numpy arrays that hold the metadata
        timestamp : datetime.datetime
            exact timestamp of the image
        lon : numpy.array or None
            array of longitudes, if None self.grid will be assumed
        lat : numpy.array or None
            array of latitudes, if None self.grid will be assumed
        time_var : string or None
            variable name of observation times in the data dict, if None all
            observations have the same timestamp
        """
        # lookup table between names and message number in the BUFR file

        data = {}
        dates = []
        # 13: Latitude (High Accuracy)
        latitude = []
        # 14: Longitude (High Accuracy)
        longitude = []

        with BUFRReader(self.filename) as bufr:
            for message in bufr.messages():
                # read fixed fields
                latitude.append(message[:, 12])
                longitude.append(message[:, 13])
                years = message[:, 6].astype(int)
                months = message[:, 7].astype(int)
                days = message[:, 8].astype(int)
                hours = message[:, 9].astype(int)
                minutes = message[:, 10].astype(int)
                seconds = message[:, 11].astype(int)

                dates.append(
                    julday(months, days, years, hours, minutes, seconds))

                # read optional data fields
                for mid in self.msg_name_lookup:
                    name = self.msg_name_lookup[mid]

                    if name not in data:
                        data[name] = []

                    data[name].append(message[:, mid - 1])

        dates = np.concatenate(dates)
        longitude = np.concatenate(longitude)
        latitude = np.concatenate(latitude)
        n_records = latitude.shape[0]

        for mid in self.msg_name_lookup:
            name = self.msg_name_lookup[mid]
            data[name] = np.concatenate(data[name])

        data['jd'] = dates
        if 'Direction Of Motion Of Moving Observing Platform' in data:
            data['as_des_pass'] = (data[
                "Direction Of Motion Of Moving Observing Platform"
            ] < 270).astype(np.uint8)

        if 'Cross-Track Cell Number' in data:
            if data['Cross-Track Cell Number'].max() == 82:
                data['swath_indicator'] = 1 * (
                    data['Cross-Track Cell Number'] > 41)
            elif data['Cross-Track Cell Number'].max() == 42:
                data['swath_indicator'] = 1 * (
                    data['Cross-Track Cell Number'] > 21)
            else:
                raise ValueError("Unsuspected node number.")
            n_lines = n_records / max(data['Cross-Track Cell Number'])
            data['line_num'] = np.arange(n_lines).repeat(
                max(data['Cross-Track Cell Number']))

        # There are strange elements with a value of 32.32 instead of the
        # typical nan_values
        # Since some elements rly have this value we check the other triplet
        # data of that beam to filter the nan_values out
        beams = ['f', 'm', 'a']
        for beam in beams:
            azi = beam + '_Antenna Beam Azimuth'
            sig = beam + '_Backscatter'
            inc = beam + '_Radar Incidence Angle'
            if azi in data:
                mask_azi = data[azi] == 32.32
                mask_sig = data[sig] == 1.7e+38
                mask_inc = data[inc] == 1.7e+38
                mask = np.all([mask_azi, mask_sig, mask_inc], axis=0)
                data[azi][mask] = 1.7e+38

        # 1 ERS 1
        # 2 ERS 2
        # 3 METOP-1 (Metop-B)
        # 4 METOP-2 (Metop-A)
        # 5 METOP-3 (Metop-C)
        sat_id = {3: 1, 4: 2, 5: 3}

        metadata = {}
        metadata['SPACECRAFT_ID'] = np.int8(
            sat_id[data['Satellite Identifier'][0]])
        metadata['ORBIT_START'] = np.uint32(data['Orbit Number'][0])

        return Image(longitude, latitude, data, metadata,
                     timestamp, timekey='jd')

    def write(self, data):
        raise NotImplementedError()

    def flush(self):
        pass

    def close(self):
        pass

class AscatL2SsmBufrFile(ImageBase):
    """
    Reads ASCAT SSM swath files in BUFR format. There are the
    following products:

    - H101 SSM ASCAT-A NRT O 12.5 Metop-A ASCAT NRT SSM orbit geometry
    12.5 km sampling
    - H102 SSM ASCAT-A NRT O 25.0 Metop-A ASCAT NRT SSM orbit geometry
    25 km sampling
    - H16  SSM ASCAT-B NRT O 12.5 Metop-B ASCAT NRT SSM orbit geometry
    12.5 km sampling
    - H103 SSM ASCAT-B NRT O 25.0 Metop-B ASCAT NRT SSM orbit geometry
    25 km sampling
    - H104 SSM ASCAT-C NRT O 12.5 Metop-C ASCAT NRT SSM orbit geometry
    12.5 km sampling
    - H105 SSM ASCAT-C NRT O 25.0 Metop-C ASCAT NRT SSM orbit geometry
    25 km sampling
    - EUMETSAT ASCAT Soil Moisture at 12.5 km Swath Grid - Metop in BUFR format
    - EUMETSAT ASCAT Soil Moisture at 25.0 km Swath Grid - Metop in BUFR format

    Parameters
    ----------
    filename : str
        Filename path.
    mode : str, optional
        Opening mode. Default: r
    msg_name_lookup: dict, optional
        Dictionary mapping bufr msg number to parameter name.
        See :ref:`ascatformattable`.

        Default:

             === =====================================================
             Key Value
             === =====================================================
             4: "Satellite Identifier",
             6: "Direction Of Motion Of Moving Observing Platform",
             16: "Orbit Number",
             17: "Cross-Track Cell Number",
             21: "f_Beam Identifier",
             22: "f_Radar Incidence Angle",
             23: "f_Antenna Beam Azimuth",
             24: "f_Backscatter",
             25: "f_Radiometric Resolution (Noise Value)",
             26: "f_ASCAT KP Estimate Quality",
             27: "f_ASCAT Sigma-0 Usability",
             34: "f_ASCAT Land Fraction",
             35: "m_Beam Identifier",
             36: "m_Radar Incidence Angle",
             37: "m_Antenna Beam Azimuth",
             38: "m_Backscatter",
             39: "m_Radiometric Resolution (Noise Value)",
             40: "m_ASCAT KP Estimate Quality",
             41: "m_ASCAT Sigma-0 Usability",
             48: "m_ASCAT Land Fraction",
             49: "a_Beam Identifier",
             50: "a_Radar Incidence Angle",
             51: "a_Antenna Beam Azimuth",
             52: "a_Backscatter",
             53: "a_Radiometric Resolution (Noise Value)",
             54: "a_ASCAT KP Estimate Quality",
             55: "a_ASCAT Sigma-0 Usability",
             62: "a_ASCAT Land Fraction",
             65: "Surface Soil Moisture (Ms)",
             66: "Estimated Error In Surface Soil Moisture",
             67: "Backscatter",
             68: "Estimated Error In Sigma0 At 40 Deg Incidence Angle",
             69: "Slope At 40 Deg Incidence Angle",
             70: "Estimated Error In Slope At 40 Deg Incidence Angle",
             71: "Soil Moisture Sensitivity",
             72: "Dry Backscatter",
             73: "Wet Backscatter",
             74: "Mean Surface Soil Moisture",
             75: "Rain Fall Detection",
             76: "Soil Moisture Correction Flag",
             77: "Soil Moisture Processing Flag",
             78: "Soil Moisture Quality",
             79: "Snow Cover",
             80: "Frozen Land Surface Fraction",
             81: "Inundation And Wetland Fraction",
             82: "Topographic Complexity"
             === =====================================================
    """

    def __init__(self, filename, mode='r', msg_name_lookup=None, **kwargs):
        """
        Initialization of i/o object.

        """
        super(AscatL2SsmBufrFile, self).__init__(filename, mode=mode,
                                                 **kwargs)
        if msg_name_lookup is None:
            msg_name_lookup = {
                4: "Satellite Identifier",
                6: "Direction Of Motion Of Moving Observing Platform",
                16: "Orbit Number",
                17: "Cross-Track Cell Number",
                21: "f_Beam Identifier",
                22: "f_Radar Incidence Angle",
                23: "f_Antenna Beam Azimuth",
                24: "f_Backscatter",
                25: "f_Radiometric Resolution (Noise Value)",
                26: "f_ASCAT KP Estimate Quality",
                27: "f_ASCAT Sigma-0 Usability",
                34: "f_ASCAT Land Fraction",
                35: "m_Beam Identifier",
                36: "m_Radar Incidence Angle",
                37: "m_Antenna Beam Azimuth",
                38: "m_Backscatter",
                39: "m_Radiometric Resolution (Noise Value)",
                40: "m_ASCAT KP Estimate Quality",
                41: "m_ASCAT Sigma-0 Usability",
                48: "m_ASCAT Land Fraction",
                49: "a_Beam Identifier",
                50: "a_Radar Incidence Angle",
                51: "a_Antenna Beam Azimuth",
                52: "a_Backscatter",
                53: "a_Radiometric Resolution (Noise Value)",
                54: "a_ASCAT KP Estimate Quality",
                55: "a_ASCAT Sigma-0 Usability",
                62: "a_ASCAT Land Fraction",
                65: "Surface Soil Moisture (Ms)",
                66: "Estimated Error In Surface Soil Moisture",
                67: "Backscatter",
                68: "Estimated Error In Sigma0 At 40 Deg Incidence Angle",
                69: "Slope At 40 Deg Incidence Angle",
                70: "Estimated Error In Slope At 40 Deg Incidence Angle",
                71: "Soil Moisture Sensitivity",
                72: "Dry Backscatter",
                73: "Wet Backscatter",
                74: "Mean Surface Soil Moisture",
                75: "Rain Fall Detection",
                76: "Soil Moisture Correction Flag",
                77: "Soil Moisture Processing Flag",
                78: "Soil Moisture Quality",
                79: "Snow Cover",
                80: "Frozen Land Surface Fraction",
                81: "Inundation And Wetland Fraction",
                82: "Topographic Complexity"}
        self.msg_name_lookup = msg_name_lookup

    def read(self, timestamp=None, ssm_masked=False):
        """
        Read specific image for given datetime timestamp.

        Parameters
        ----------
        timestamp : datetime.datetime (optional)
            exact observation timestamp of the image that should be read
        ssm_masked : flag (optional)
            set to True to filter data by ssm values

        Returns
        -------
        data : dict
            dictionary of numpy arrays that hold the image data for each
            variable of the dataset
        metadata : dict
            dictionary of numpy arrays that hold the metadata
        timestamp : datetime.datetime
            exact timestamp of the image
        lon : numpy.array or None
            array of longitudes, if None self.grid will be assumed
        lat : numpy.array or None
            array of latitudes, if None self.grid will be assumed
        time_var : string or None
            variable name of observation times in the data dict, if None all
            observations have the same timestamp
        """
        # lookup table between names and message number in the BUFR file

        data = {}
        dates = []
        # 13: Latitude (High Accuracy)
        latitude = []
        # 14: Longitude (High Accuracy)
        longitude = []

        with BUFRReader(self.filename) as bufr:
            for message in bufr.messages():
                # read fixed fields
                latitude.append(message[:, 12])
                longitude.append(message[:, 13])
                years = message[:, 6].astype(int)
                months = message[:, 7].astype(int)
                days = message[:, 8].astype(int)
                hours = message[:, 9].astype(int)
                minutes = message[:, 10].astype(int)
                seconds = message[:, 11].astype(int)

                dates.append(
                    julday(months, days, years, hours, minutes, seconds))

                # read optional data fields
                for mid in self.msg_name_lookup:
                    name = self.msg_name_lookup[mid]

                    if name not in data:
                        data[name] = []

                    data[name].append(message[:, mid - 1])

        dates = np.concatenate(dates)
        longitude = np.concatenate(longitude)
        latitude = np.concatenate(latitude)
        n_records = latitude.shape[0]

        for mid in self.msg_name_lookup:
            name = self.msg_name_lookup[mid]
            data[name] = np.concatenate(data[name])
            if mid == 74:
                # ssm mean is encoded differently
                data[name] = data[name] * 100

        data['jd'] = dates

        if 'Direction Of Motion Of Moving Observing Platform' in data:
            data['as_des_pass'] = (data[
                "Direction Of Motion Of Moving Observing Platform"]
                <= 270).astype(np.uint8)

        if 'Cross-Track Cell Number' in data:
            if data['Cross-Track Cell Number'].max() == 82:
                data['swath_indicator'] = 1 * (
                    data['Cross-Track Cell Number'] > 41)
            elif data['Cross-Track Cell Number'].max() == 42:
                data['swath_indicator'] = 1 * (
                    data['Cross-Track Cell Number'] > 21)
            else:
                raise ValueError("Unsuspected node number.")
            n_lines = n_records / max(data['Cross-Track Cell Number'])
            data['line_num'] = np.arange(n_lines).repeat(
                max(data['Cross-Track Cell Number']))

        # There are strange elements with a value of 32.32 instead of the
        # typical nan_values
        # Since some elements rly have this value we check the other triplet
        # data of that beam to filter the nan_values out
        beams = ['f', 'm', 'a']
        for beam in beams:
            azi = beam + '_Antenna Beam Azimuth'
            sig = beam + '_Backscatter'
            inc = beam + '_Radar Incidence Angle'
            if azi in data:
                mask_azi = data[azi] == 32.32
                mask_sig = data[sig] == 1.7e+38
                mask_inc = data[inc] == 1.7e+38
                mask = np.all([mask_azi, mask_sig, mask_inc], axis=0)
                data[azi][mask] = 1.7e+38

        # if the ssm_masked is True we mask out data with missing ssm value
        if 65 in self.msg_name_lookup and ssm_masked is True:
            # mask all the arrays based on fill_value of soil moisture
            valid_data = np.where(data[self.msg_name_lookup[65]] != 1.7e+38)
            latitude = latitude[valid_data]
            longitude = longitude[valid_data]
            for name in data:
                data[name] = data[name][valid_data]

        # 1 ERS 1
        # 2 ERS 2
        # 3 METOP-1 (Metop-B)
        # 4 METOP-2 (Metop-A)
        # 5 METOP-3 (Metop-C)
        sat_id = {3: 1, 4: 2, 5: 3}

        metadata = {}

        try:
            metadata['SPACECRAFT_ID'] = np.int8(
                sat_id[data['Satellite Identifier'][0]])
        except KeyError:
            metadata['SPACECRAFT_ID'] = 0

        try:
            metadata['ORBIT_START'] = np.uint32(data['Orbit Number'][0])
        except KeyError:
            metadata['ORBIT_START'] = 0

        return Image(longitude, latitude, data, metadata,
                     timestamp, timekey='jd')

    def resample_data(self, image, index, distance, weights, **kwargs):
        """
        Takes an image and resample (interpolate) the image data to
        arbitrary defined locations given by index and distance.

        Parameters
        ----------
        image : object
            pygeobase.object_base.Image object
        index : np.array
            Index into image data defining a look-up table for data elements
            used in the interpolation process for each defined target
            location.
        distance : np.array
            Array representing the distances of the image data to the
            arbitrary defined locations.
        weights : np.array
            Array representing the weights of the image data that should be
            used during resampling.
            The weights of points not to use are set to np.nan
            This array is of shape (x, max_neighbors)

        Returns
        -------
        image : object
            pygeobase.object_base.Image object
        """
        total_weights = np.nansum(weights, axis=1)

        resOrbit = {}
        # resample backscatter
        for name in image.dtype.names:
            if name in ['Soil Moisture Correction Flag',
                        'Soil Moisture Processing Flag']:
                # The flags are resampled by taking the minimum flag This works
                # since any totally valid observation has the flag 0 and
                # overrides the flagged observations. This is true in cases
                # where the data was set to NaN by the flag as well as when the
                # data was set to 0 or 100. The last image element is the one
                # standing for NaN so we fill it with all flags filled to not
                # interfere with the minimum.
                image[name][-1] = 255
                bits = np.unpackbits(image[name].reshape(
                    (-1, 1)).astype(np.uint8), axis=1)
                resampled_bits = np.min(bits[index, :], axis=1)
                resOrbit[name] = np.packbits(resampled_bits)
            else:
                resOrbit[name] = np.nansum(
                    image[name][index] * weights, axis=1) / total_weights

        return resOrbit

    def write(self, data):
        raise NotImplementedError()

    def flush(self):
        pass

    def close(self):
        pass

class AscatL2SsmBufr(MultiTemporalImageBase):
    """
    Class for reading HSAF ASCAT SSM images in bufr format.
    The images have the same structure as the ASCAT 3 minute pdu files
    and these 2 readers could be merged in the future
    The images have to be uncompressed in the following folder structure
    path - month_path_str (default 'h07_%Y%m_buf')

    For example if path is set to /home/user/hsaf07 and month_path_str is left
    to the default 'h07_%Y%m_buf' then the images for March 2012 have to be in
    the folder /home/user/hsaf07/h07_201203_buf/

    Parameters
    ----------
    path: string
        path where the data is stored
    month_path_str: string, optional
        if the files are stored in folders by month as is the standard on the
        HSAF FTP Server then please specify the string that should be used in
        datetime.datetime.strftime Default: 'h07_%Y%m_buf'
    day_search_str: string, optional
        to provide an iterator over all images of a day the
        method _get_possible_timestamps looks for all available images on a day
        on the harddisk. This string is used in datetime.datetime.strftime and
        in glob.glob to search for all files on a day.
        Default : 'h07_%Y%m%d_*.buf'
    file_search_str: string, optional
        this string is used in datetime.datetime.strftime and glob.glob to find
        a 3 minute bufr file by the exact date.
        Default: 'h07_{datetime}*.buf'
    datetime_format: string, optional
        datetime format by which {datetime} will be replaced in file_search_str
        Default: %Y%m%d_%H%M%S
    msg_name_lookup: dict, optional
        Dictionary mapping bufr msg number to parameter name.
        See :ref:`ascatformattable`.

        Default:

             === =====================================================
             Key Value
             === =====================================================
             4:  "Satellite Identifier",
             6:  "Direction Of Motion Of Moving Observing Platform",
             16: "Orbit Number",
             17: "Cross-Track Cell Number",
             21: "f_Beam Identifier",
             22: "f_Radar Incidence Angle",
             23: "f_Antenna Beam Azimuth",
             24: "f_Backscatter",
             25: "f_Radiometric Resolution (Noise Value)",
             26: "f_ASCAT KP Estimate Quality",
             27: "f_ASCAT Sigma-0 Usability",
             34: "f_ASCAT Land Fraction",
             35: "m_Beam Identifier",
             36: "m_Radar Incidence Angle",
             37: "m_Antenna Beam Azimuth",
             38: "m_Backscatter",
             39: "m_Radiometric Resolution (Noise Value)",
             40: "m_ASCAT KP Estimate Quality",
             41: "m_ASCAT Sigma-0 Usability",
             48: "m_ASCAT Land Fraction",
             49: "a_Beam Identifier",
             50: "a_Radar Incidence Angle",
             51: "a_Antenna Beam Azimuth",
             52: "a_Backscatter",
             53: "a_Radiometric Resolution (Noise Value)",
             54: "a_ASCAT KP Estimate Quality",
             55: "a_ASCAT Sigma-0 Usability",
             62: "a_ASCAT Land Fraction",
             65: "Surface Soil Moisture (Ms)",
             66: "Estimated Error In Surface Soil Moisture",
             67: "Backscatter",
             68: "Estimated Error In Sigma0 At 40 Deg Incidence Angle",
             69: "Slope At 40 Deg Incidence Angle",
             70: "Estimated Error In Slope At 40 Deg Incidence Angle",
             71: "Soil Moisture Sensitivity",
             72: "Dry Backscatter",
             73: "Wet Backscatter",
             74: "Mean Surface Soil Moisture",
             75: "Rain Fall Detection",
             76: "Soil Moisture Correction Flag",
             77: "Soil Moisture Processing Flag",
             78: "Soil Moisture Quality",
             79: "Snow Cover",
             80: "Frozen Land Surface Fraction",
             81: "Inundation And Wetland Fraction",
             82: "Topographic Complexity"
             === =====================================================
    """

    def __init__(self, path, month_path_str='h07_%Y%m_buf',
                 day_search_str='h07_%Y%m%d_*.buf',
                 file_search_str='h07_{datetime}*.buf',
                 datetime_format='%Y%m%d_%H%M%S',
                 filename_datetime_format=(4, 19, '%Y%m%d_%H%M%S'),
                 msg_name_lookup=None):
        self.path = path
        self.month_path_str = month_path_str
        self.day_search_str = day_search_str
        self.file_search_str = file_search_str
        self.filename_datetime_format = filename_datetime_format
        super(AscatL2SsmBufr, self).__init__(path, AscatL2SsmBufrFile,
                                             subpath_templ=[month_path_str],
                                             fname_templ=file_search_str,
                                             datetime_format=datetime_format,
                                             exact_templ=False,
                                             ioclass_kws={
                                                 'msg_name_lookup':
                                                     msg_name_lookup})

    def _get_orbit_start_date(self, filename):
        orbit_start_str = \
            os.path.basename(filename)[self.filename_datetime_format[0]:
                                       self.filename_datetime_format[1]]
        return datetime.strptime(orbit_start_str,
                                 self.filename_datetime_format[2])

    def tstamps_for_daterange(self, startdate, enddate):
        """
        Get the timestamps as datetime array that are possible for the
        given day.

        For this product it is not fixed but has to be looked up from
        the hard disk since bufr files are not regular spaced and only
        europe is in this product. For a global product a 3 minute
        spacing could be used as a fist approximation

        Parameters
        ----------
        startdate : datetime.date or datetime.datetime
            start date
        enddate : datetime.date or datetime.datetime
            end date

        Returns
        -------
        dates : list
            list of datetimes
        """
        file_list = []
        delta_all = enddate - startdate
        timestamps = []

        for i in range(delta_all.days + 1):
            timestamp = startdate + timedelta(days=i)

            files = self._search_files(
                timestamp, custom_templ=self.day_search_str)

            file_list.extend(sorted(files))

        for filename in file_list:
            timestamps.append(self._get_orbit_start_date(filename))

        timestamps = [dt for dt in timestamps if (
            startdate <= dt <= enddate)]
        return timestamps

class AscatL2SsmBufrChunked(IntervalReadingMixin, AscatL2SsmBufr):
    """
    Reads BUFR files but does not return them on a file by file basis but in
    bigger chunks. For example it allows to read multiple 3 minute PDU's in
    half orbit chunks of 50 minutes. This speeds up operations like e.g.
    resampling of the data.

    Parameters
    ----------
    chunk_minutes: int, optional
        How many minutes should a chunk of data cover.
    """

    def __init__(self, path, month_path_str='h07_%Y%m_buf',
                 day_search_str='h07_%Y%m%d_*.buf',
                 file_search_str='h07_{datetime}*.buf',
                 datetime_format='%Y%m%d_%H%M%S',
                 filename_datetime_format=(4, 19, '%Y%m%d_%H%M%S'),
                 msg_name_lookup=None, chunk_minutes=50):
        super(AscatL2SsmBufrChunked, self).__init__(
            path,
            month_path_str=month_path_str,
            day_search_str=day_search_str,
            file_search_str=file_search_str,
            datetime_format=datetime_format,
            filename_datetime_format=filename_datetime_format,
            msg_name_lookup=msg_name_lookup,
            chunk_minutes=chunk_minutes)

class BUFRReader(object):
    """
    BUFR reader based on the pybufr-ecmwf package but faster

    Parameters
    ----------
    filename : string
        filename of the bufr file
    kelem_guess : int, optional
        if the elements per variable in as message are known
        please specify here.
        Otherwise the elements will be found out via trial and error
        This works most of the time but is not 100 percent failsafe
        Default: 500
    max_tries : int, optional
        the Reader will try max_tries times to unpack a bufr message.
        Some messages can not be read even if the array sizes are ok.
        Most of the time these files are corrupt.

    """

    def __init__(self, filename, kelem_guess=500, max_tries=10):
        self.bufr = raw_bufr_file.RawBUFRFile()
        self.bufr.open(filename, 'rb')
        self.nr_messages = self.bufr.get_num_bufr_msgs()
        self.max_tries = max_tries

        if 'BUFR_TABLES' not in os.environ:
            path = os.path.split(ecmwfbufr.__file__)[0]
            os.environ["BUFR_TABLES"] = os.path.join(
                path, 'ecmwf_bufrtables' + os.sep)
            # os.environ['PRINT_TABLE_NAMES'] = "false"

        self.size_ksup = ecmwfbufr_parameters.JSUP
        self.size_ksec0 = ecmwfbufr_parameters.JSEC0
        self.size_ksec1 = ecmwfbufr_parameters.JSEC1
        self.size_ksec2 = ecmwfbufr_parameters.JSEC2
        self.size_key = ecmwfbufr_parameters.JKEY
        self.size_ksec3 = ecmwfbufr_parameters.JSEC3
        self.size_ksec4 = ecmwfbufr_parameters.JSEC4

        self.kelem_guess = kelem_guess

    def messages(self):
        """
        Raises
        ------
        IOError:
            if a message cannot be unpacked after max_tries tries

        Returns
        -------
        data : yield results of messages
        """
        count = 0
        for i in np.arange(self.nr_messages) + 1:
            tries = 0

            ksup = np.zeros(self.size_ksup, dtype=np.int)
            ksec0 = np.zeros(self.size_ksec0, dtype=np.int)
            ksec1 = np.zeros(self.size_ksec1, dtype=np.int)
            ksec2 = np.zeros(self.size_ksec2, dtype=np.int)
            ksec3 = np.zeros(self.size_ksec3, dtype=np.int)
            ksec4 = np.zeros(self.size_ksec4, dtype=np.int)

            kerr = 0
            data = self.bufr.get_raw_bufr_msg(i)

            ecmwfbufr.bus012(data[0],  # input
                             ksup,  # output
                             ksec0,  # output
                             ksec1,  # output
                             ksec2,  # output
                             kerr)  # output

            kelem = self.kelem_guess
            ksup_first = ksup[5]
            kvals = ksup_first * kelem
            max_kelem = 500000
            self.init_values = np.zeros(kvals, dtype=np.float64)
            self.cvals = np.zeros((kvals, 80), dtype=np.character)
            # try to expand bufr message with the first guess for
            # kelem
            increment_arraysize = True
            while increment_arraysize:
                cnames = np.zeros((kelem, 64), dtype='|S1')
                cunits = np.zeros((kelem, 24), dtype='|S1')

                with warnings.catch_warnings():
                    warnings.filterwarnings(
                        "ignore", category=DeprecationWarning)
                    ecmwfbufr.bufrex(data[0],  # input
                                     ksup,  # output
                                     ksec0,  # output
                                     ksec1,  # output
                                     ksec2,  # output
                                     ksec3,  # output
                                     ksec4,  # output
                                     cnames,  # output
                                     cunits,  # output
                                     self.init_values,  # output
                                     self.cvals,  # output
                                     kerr)  # output
                # no error - stop loop
                if kerr == 0 and ksec4[0] != 0:
                    increment_arraysize = False
                # error increase array size and try to unpack again
                else:
                    tries += 1
                    if tries >= self.max_tries:
                        raise IOError('This file seems corrupt')
                    kelem = kelem * 5
                    kvals = ksup_first * kelem

                    if kelem > max_kelem:
                        kelem = kvals / 2
                        max_kelem = kvals

                    self.init_values = np.zeros(kvals, dtype=np.float64)
                    self.cvals = np.zeros((kvals, 80), dtype=np.character)

            decoded_values = ksup[4]
            # set kelem_guess to decoded values of last message
            # only increases reading speed if all messages are the same
            # not sure if this is the best option
            self.kelem_guess = decoded_values
            decoded_msg = ksup[5]
            # calculate first dimension of 2D array
            factor = int(kvals / kelem)

            # reshape and trim the array to the actual size of the data
            values = self.init_values.reshape((factor, kelem))
            values = values[:decoded_msg, :decoded_values]
            count += values.shape[0]

            yield values

    def __enter__(self):
        return self

    def __exit__(self, exc, val, trace):
        self.bufr.close()

def julday(month, day, year, hour=0, minute=0, second=0):
    """
    Julian date from month, day, and year (can be scalars or arrays)
    (function from pytesmo)
    Parameters
    ----------
    month : numpy.ndarray or int32
        Month.
    day : numpy.ndarray or int32
        Day.
    year : numpy.ndarray or int32
        Year.
    hour : numpy.ndarray or int32, optional
        Hour.
    minute : numpy.ndarray or int32, optional
        Minute.
    second : numpy.ndarray or int32, optional
        Second.
    Returns
    -------
    jul : numpy.ndarray or double
        Julian day.
    """
    month = np.array(month)
    day = np.array(day)
    inJanFeb = month <= 2
    jy = year - inJanFeb
    jm = month + 1 + inJanFeb * 12

    jul = np.int32(np.floor(365.25 * jy) +
                   np.floor(30.6001 * jm) + (day + 1720995.0))
    ja = np.int32(0.01 * jy)
    jul += 2 - ja + np.int32(0.25 * ja)

    jul = jul + hour / 24.0 - 0.5 + minute / 1440.0 + second / 86400.0

    return jul


# This Python file uses the following encoding: utf-8

from aiogram.types import InlineKeyboardMarkup,  InlineKeyboardButton

class button:
    def button_all(text_button: str, callback: str) -> InlineKeyboardMarkup:
        inline_btn = InlineKeyboardButton(text=text_button, callback_data=callback)
        inline_kb = InlineKeyboardMarkup().add(inline_btn)
        return inline_kb

    def proxy(list_proxy: list) -> InlineKeyboardMarkup:
        inline_kb = InlineKeyboardMarkup()
        for i in list_proxy:
            s: str = i.replace("socks5://", "")
            server, port = s.split(":")
            link_beta: str = f"https://t.me/proxy?server={server}&port={port}"
            inline_kb.add(InlineKeyboardButton(server, url=link_beta))
        return inline_kb

    def butoons(text: list, call_back: list)-> InlineKeyboardMarkup:
        inline_kb: InlineKeyboardMarkup = InlineKeyboardMarkup()
        for val, i in enumerate(text):
            inb = InlineKeyboardButton(text=i, callback_data=call_back[i])
            inline_kb.add(inb)
        return inline_kb


import torch
from torch.autograd import Variable
import torch.nn.functional as F

import pandas as pd
from sklearn.metrics import roc_curve, roc_auc_score
import numpy as np

class_num = 6

def plot_xy(x, y, x_axis="X", y_axis="Y", title="Plot"):
    df = pd.DataFrame({'x': x, "y": y})
    plot = df.plot(x='x', y='y')

    plot.grid(b=True, which='major')
    plot.grid(b=True, which='minor')

    plot.set_title(title)
    plot.set_ylabel(y_axis)
    plot.set_xlabel(x_axis)
    return plot

def plot_roc(y_true, y_score, title="Receciver Operating Characteristic"):
    fpr, tpr, thresholds = roc_curve(y_true, y_score)
    auc_score = roc_auc_score(y_true, y_score)
    plot = plot_xy(fpr, tpr, x_axis="False Positive Rate", y_axis="True Positive Rate", title=title)

    return auc_score, plot

def openset_softmax_confidence(dataloader, netC):
    openset_scores = []
    with torch.no_grad():
        for i, (images, labels) in enumerate(dataloader):
            images = Variable(images)
            images = images.cuda()
            net_y = netC(images)
            preds = F.softmax(net_y, dim=1)
            openset_scores.extend(preds.max(dim=1)[0].data.cpu().numpy())
    return -np.array(openset_scores)

def evalute_classifier(net, dataloader):
    net.eval()
    classification_closed_correct = 0
    classification_total = 0
    with torch.no_grad():
        for images, labels in dataloader:
            images = Variable(images)
            images = images.cuda()
            net_y = net(images)
            class_predictions = F.softmax(net_y, dim=1)
            labels = torch.argmax(labels, dim=1)
            _, predicted = class_predictions.max(1)
            classification_closed_correct += sum(predicted.data == labels)
            classification_total += len(labels)

    return float(classification_closed_correct) / classification_total

def evaluate_openset(net, dataloader_on, dataloader_off):
    net.eval()

    d_scores_on = openset_softmax_confidence(dataloader_on, net)
    d_scores_off = openset_softmax_confidence(dataloader_off, net)

    y_true = np.array([0] * len(d_scores_on) + [1] * len(d_scores_off))
    y_discriminator = np.concatenate([d_scores_on, d_scores_off])

    auc_d, plot_d = plot_roc(y_true, y_discriminator, 'Discriminator ROC vs {}'.format(dataloader_off.dsf.name))

    return auc_d


from flask import Flask,request,render_template

#Flask(import_name,template_folder="templates")
#模板就是包含了特殊语法的HTML
#Flask中所有的模板默认放在templates目录中 如果templates目录没有指定，需要手动创建
#自定义模板目录  在文件中修改模板目录后 记得在目录结构中创建相对应的目录
app = Flask(__name__,template_folder="temp")
#10:50~11:05
#127.0.0.1:5000/
@app.route('/')
def index_view():
    return render_template('review.html')

#9：50～10：05
#127.0.0.1:5000/server
@app.route('/server',methods=['get','post'])
def server():
    #request.method 保存当前请求的数据提交方式  "GET"/"POST"
    #request.args  接收get方式提交的数据
    #request.form  接收post方式提交的数据
    if request.method == 'GET':
        uname = request.args.get('uname')
        return '欢迎%s' % uname
    elif request.method == 'POST':
        uname = request.form.get('uname')
        return '欢迎%s' % uname

#http://127.0.0.1:5000/test/all  使用路由传参
#http://127.0.0.1:5000/test?level=all   使用get方式传参
@app.route('/test')
def test_view():
    level = request.args.get('level')
    if level == "all":
        return "全部难度的内容"
    elif level == "easy":
        return "初级难度的内容"
    elif level == "mid":
        return "中级难度的内容"
    elif level == "hard":
        return "高级难度的内容"
    else:
        return "获取数据失败"

if __name__ == "__main__":
    app.run(debug=True)#在开发阶段打开DEBUG  上线关闭

3. Python Web全栈式工程师/4. Ajax/day11/flask/run.py


from setuptools import setup, find_packages

version = '1.0.1'

setup(
    name="alerta-falcon",
    version=version,
    description='Alerta Webhook for Open-Falcon',
    url='https://github.com/cxjchocolate/alerta-contrib',
    license='MIT',
    author='Kevin Cai',
    author_email='kevin.cai@nio.com',
    packages=find_packages(),
    py_modules=['alerta_falcon'],
    install_requires=[],
    include_package_data=True,
    zip_safe=True,
    entry_points={
        'alerta.webhooks': [
            'falcon = alerta_falcon:FalconWebhook'
        ]
    }
)


# -*- coding: utf-8 -*-
"""
Created on Sun Mar 21  2021
This is the tool function for preprocess
@author: Shen Po-Heng
"""
import os
import pandas as pd
import numpy as np
from scipy.stats import iqr
from scipy import signal

class Create_Table:
    #combine all extraction data into one input
    def Create_Table(DataPath,savePath):
        Table=pd.DataFrame()#Declare a table for combine extracted data into one table
        print("\ncombine all users' extracted data....")
        for file in os.listdir(DataPath):
            #Read data
            print("Loading... %s"% str(file))
            data = pd.DataFrame(pd.read_csv(DataPath+str(file), index_col=0))
            Table=pd.concat([Table,data], ignore_index=False, sort=False)
            # Table.append(data) #another way to combine
        Table.rename(columns={str(len(Table.columns)-1):"label"}, inplace=True)
        # print(str(len(Table.columns)-2))
        Table.to_csv('%sexp_RawExtractionResult_Filtered_ALL_Gyro_nofilter.csv' % savePath,index=False)

class extraction_tool:
    #extract value from a window
    def extract_values(self,rowData,data):
        rowData.append(np.mean(data))# get Mean Value in a window
        rowData.append(abs(np.median(data)))#Median Value
        rowData.append(np.var(data))#Variance
        rowData.append(np.std(data))#deviation Value
        rowData.append(iqr(data))#IQR value
        rowData.append(np.max(data))#Max Value
        rowData.append(np.min(data))#Min Value
    #savedata
    def save_data(self,file,Data,savepath):
        TableData=pd.DataFrame(Data)
        TableData.to_csv('%sexp_RawExtractionResult_%s.csv' % (savepath ,str(file[14:16])))
        print('Save exp_RawExtractionResult_%s.csv to %s' % (str(file[14:16]),savepath))

class Preprocess_tool():
    @staticmethod
    def seperate_toxyz(data):
        x = np.array(data[0])
        y = np.array(data[1])
        z = np.array(data[2])
        return x,y,z
    #this is use lowpass filter to separate gravity and body part
    @staticmethod
    def Filter_toBodyGravity(Signal):
        LowFilter = signal.butter(3, 0.3, 'low', fs=50, output='sos') #low-pass filter(cutoff frequency=0.3Hz)
        Body_part = signal.sosfilt(LowFilter, Signal)
        Gra_part=Signal-Body_part
        return Body_part,Gra_part

class ThreeDim_Preprocess(Preprocess_tool):#especially for XYZ data
    #using the Filter_toBodyGravity function in 3 diffetent direction
    def Acc_toBodyGravity(self,Signal):#call Filter_toBodyGravity() 3 times
        Body,Gravity=np.zeros((3,len(Signal[0]))),np.zeros((3,len(Signal[0])))
        for i in range(3):
            Body[i],Gravity[i]=super().Filter_toBodyGravity(Signal[i])
        return Body,Gravity


# -*- coding: utf-8 -*-
from graphics import *
def convert(input):
    celsius=eval(input.getText())
    fahrenheit=9.0/5.0*celsius+32
    return fahrenheit
def colorchange(win,input):
    cnum=eval(input.getText())
    weight=cnum/100.0
    color=color_rgb(255*weight,66+150*(1-weight),255*(1-weight))
    win.setBackground(color)
def main():
    win=GraphWin("Celsius Converter",400,300)
    win.setCoords(0.0,0.0,3.0,4.0)
    Text(Point(1,3),'Celsius temperature:').draw(win)
    Text(Point(2,2.7),'Please enter 0.-100.0').draw(win)
    Text(Point(1,1),'Fahrenheit Temperature:').draw(win)
    input=Entry(Point(2,3),5)
    input.setText('0.0')
    input.draw(win)
    output=Text(Point(2,1),' ')
    output.draw(win)
    button=Text(Point(1.5,2.0),'Convert It')
    button.draw(win)
    rect=Rectangle(Point(1,1.5),Point(2,2.5))
    rect.draw(win)
    win.getMouse()
    result=convert(input)
    output.setText(result)
    colorchange(win,input)
    button.setText('Quit')
    win.getMouse()
    win.close()
if __name__=='__main__':
    main()


# -*- coding: utf-8 -*-
"""
Created on Sat Nov 25 13:40:33 2017

@author: mayes
"""

import numpy as np
import scipy.misc as msc
import scipy.ndimage as img
import matplotlib.pyplot as plt

FIG_COUNT = 0

def foreground2BinImg(f):
    d = img.filters.gaussian_filter(f, sigma=0.50, mode='reflect') - \
    img.filters.gaussian_filter(f, sigma=1.00, mode='reflect')
    d = np.abs(d)
    m = d.max()
    d[d< 0.1*m] = 0
    d[d>=0.1*m] = 1
    return img.morphology.binary_closing(d)

def countN(img, s):
    count = 0
    width, height = img.shape
    for i in range(1, int(1/s) + 1):
        for j in range(1, int(1/s) + 1):
            startRow = int((i - 1) * s * height)
            endRow = int((i * s * height))
            startColumn = int((j - 1) * s * width)
            endColumn = int((j * s * width))
            if(np.any(img[startRow : endRow , startColumn : endColumn])):
                count = count + 1
    return count

def fractalDimension(img, fig):
    f = msc.imread(imgName+'.png', flatten=True).astype(np.float)
    g = foreground2BinImg(f)
    plt.figure(fig)
    plt.imshow(g, cmap="binary")
    ni = []
    si = []
    for i in range(1,8):
        s = 1 / float(2**i)
        n = countN(g,s)
        ni.append(n)
        si.append(s)

    ni = np.asarray(ni)
    si = np.asarray(si)
    print(ni)
    print(si)

    y = np.log(ni)
    x = np.log(1/si)

    plt.figure(fig+1)
    plt.plot(x,y,'ks')

    X = np.vstack((x, np.ones((x.shape), dtype=x.dtype)))
    X = X.T
    w = np.dot(np.linalg.inv(np.dot(X.T, X)), np.dot(X.T,y))

    xl = np.linspace(0,5)
    yl = w[1] + (w[0] * xl)
    plt.plot(xl,yl)
    plt.show()

    return w[0]

imgName = 'lightning-3'
D1 = fractalDimension(imgName, 1)
print(D1)
imgName = 'tree-2'
D2 = fractalDimension(imgName, 3)
print(D2)
imgName = 'pattern'
D3 = fractalDimension(imgName, 5)
print(D3)
imgName = 'cat'
D4 = fractalDimension(imgName, 7)
print(D4)


def parse_cfg(cfg_path):

    all_lines = []

    with open(cfg_path) as f:
        for line in f:
            line = line.strip()
            if len(line) == 0 or line[0] == '' or line[0] == '#':
                continue
            else:
                all_lines.append(line.strip())

    def get_key_value(line):
        key, value = line.split('=')

        key = key.strip()
        value = value.strip()

        if ',' in value:
            value = value.split(',')

        return key, value

    cfg = {}

    cur_iter = 0
    while  cur_iter < len(all_lines):
        line = all_lines[cur_iter]

        if line[0] == '[':
            collection_name = line[1:-1]

            sub_cfg = {}

            cur_iter += 1

            while all_lines[cur_iter] != '[end]':
                key, value = get_key_value(all_lines[cur_iter])
                sub_cfg[key] = value
                cur_iter += 1

            cfg[collection_name] = sub_cfg
        else:
            key, value = get_key_value(line)
            cfg[key] = value

        cur_iter += 1
    return cfg


from . import FixtureTest

class EarlyStep(FixtureTest):
    def test_steps_with_regional_route(self):
        self.load_fixtures([
            'https://www.openstreetmap.org/way/24655593',
            'https://www.openstreetmap.org/relation/2260059',
        ], clip=self.tile_bbox(12, 653, 1582))

        self.assert_has_feature(
            12, 653, 1582, 'roads',
            {'kind_detail': 'steps'})

    def test_steps_without_route(self):
        self.load_fixtures([
            'https://www.openstreetmap.org/way/38060491',
        ])

        self.assert_has_feature(
            13, 1309, 3166, 'roads',
            {'kind_detail': 'steps'})

    def test_min_zoom(self):
        # way 25292070   highway=steps, no route, but has name (Esmeralda,
        # Bernal, SF)
        self.load_fixtures(['https://www.openstreetmap.org/way/25292070'])

        self.assert_no_matching_feature(
            13, 1310, 3167, 'roads',
            {'kind': 'path', 'kind_detail': 'steps',
             'name': 'Esmeralda Ave.'})

        self.assert_has_feature(
            14, 2620, 6334, 'roads',
            {'kind': 'path', 'kind_detail': 'steps',
             'name': 'Esmeralda Ave.'})


import requests
import logging

from typing import Dict, List
from functools import lru_cache

from flask import abort

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

_uri_pattern = 'http://biothings.io/explorer/api/v2/crawler?input_type={prefix}&input_value={local_id}'

@lru_cache(maxsize=128)
def crawl(curie:str) -> Dict[str, List[dict]]:
    """
    Retreives a dictionary mapping categories onto a list of associations that
    involve the given CURIE identifier.

    :param curie: a CURIE identifier of concept of interest
    """
    prefix, local_id = curie.split(':', 1)
    uri = _uri_pattern.format(prefix=prefix.lower(), local_id=local_id)

    response = requests.get(uri)

    if response.ok:
        data = response.json()
        return data['linkedData']
    else:
        abort(500, 'Could not connect to: {}'.format(uri))


#!/usr/bin/python

import json
import man_obj
import pos_arm
import random
import rec_obj
import rospy
import sns_obj
import wgh_obj
import geometry_msgs
import std_msgs
import dep_obj
import numpy

""" Definitions """

# bin ids

bin_a_id = "bin_A"
bin_b_id = "bin_B"
bin_c_id = "bin_C"
bin_d_id = "bin_D"
bin_e_id = "bin_E"
bin_f_id = "bin_F"
bin_g_id = "bin_G"
bin_h_id = "bin_H"
bin_i_id = "bin_I"

bin_ids =\
[\
    bin_a_id,\
    bin_b_id,\
    bin_c_id,\
    bin_d_id,\
    bin_e_id,\
    bin_f_id,\
    bin_g_id,\
    bin_h_id,\
    bin_i_id,\
]

# bin selection probabilities

bin_probs =\
{\
     0:   3.125,\
     1:   6.25 ,\
     2:  12.5  ,\
     3:  25.0  ,\
     4:  50.0  ,\
     5: 100.0  ,\
     6:  50.0  ,\
     7:  25.0  ,\
     8:  12.5  ,\
     9:   6.25 ,\
    10:   3.125,\
}

# bin poses

bin_a_pose = geometry_msgs.msg.Pose()
bin_a_pose.position.x = 0.30
bin_a_pose.position.y = -1.17
bin_a_pose.position.z = 1.62
bin_a_pose.orientation.w = 0.71
bin_a_pose.orientation.x = 0.00
bin_a_pose.orientation.y = 3.37
bin_a_pose.orientation.z = -0.71

bin_b_pose = geometry_msgs.msg.Pose()
bin_b_pose.position.x = -0.01
bin_b_pose.position.y = -1.17
bin_b_pose.position.z = 1.62
bin_b_pose.orientation.w = 0.71
bin_b_pose.orientation.x = 0.00
bin_b_pose.orientation.y = 5.96
bin_b_pose.orientation.z = -0.71

bin_c_pose = geometry_msgs.msg.Pose()
bin_c_pose.position.x = -0.28
bin_c_pose.position.y = -1.16
bin_c_pose.position.z = 1.62
bin_c_pose.orientation.w = 0.71
bin_c_pose.orientation.x = 0.00
bin_c_pose.orientation.y = 0.00
bin_c_pose.orientation.z = -0.71

bin_d_pose = geometry_msgs.msg.Pose()
bin_d_pose.position.x = 0.30
bin_d_pose.position.y = -1.17
bin_d_pose.position.z = 1.30
bin_d_pose.orientation.w = 0.71
bin_d_pose.orientation.x = 0.00
bin_d_pose.orientation.y = 0.00
bin_d_pose.orientation.z = -0.71

bin_e_pose = geometry_msgs.msg.Pose()
bin_e_pose.position.x = -0.02
bin_e_pose.position.y = -1.22
bin_e_pose.position.z = 1.29
bin_e_pose.orientation.w = 0.72
bin_e_pose.orientation.x = 0.00
bin_e_pose.orientation.y = 4.01
bin_e_pose.orientation.z = -0.69

bin_f_pose = geometry_msgs.msg.Pose()
bin_f_pose.position.x = -0.32
bin_f_pose.position.y = -1.17
bin_f_pose.position.z = 1.29
bin_f_pose.orientation.w = 0.71
bin_f_pose.orientation.x = 0.00
bin_f_pose.orientation.y = 0.00
bin_f_pose.orientation.z = -0.71

bin_g_pose = geometry_msgs.msg.Pose()
bin_g_pose.position.x = 0.30
bin_g_pose.position.y = -1.17
bin_g_pose.position.z = 0.97
bin_g_pose.orientation.w = 0.71
bin_g_pose.orientation.x = 0.00
bin_g_pose.orientation.y = 0.00
bin_g_pose.orientation.z = -0.71

bin_h_pose = geometry_msgs.msg.Pose()
bin_h_pose.position.x = 0.00
bin_h_pose.position.y = -1.17
bin_h_pose.position.z = 0.97
bin_h_pose.orientation.w = 0.71
bin_h_pose.orientation.x = 0.00
bin_h_pose.orientation.y = 0.00
bin_h_pose.orientation.z = 0.71

bin_i_pose = geometry_msgs.msg.Pose()
bin_i_pose.position.x = -0.31
bin_i_pose.position.y = -1.16
bin_i_pose.position.z = 0.97
bin_i_pose.orientation.w = 0.71
bin_i_pose.orientation.x = 0.00
bin_i_pose.orientation.y = 0.00
bin_i_pose.orientation.z = -0.71

bin_poses =\
{\
    bin_a_id: bin_a_pose,\
    bin_b_id: bin_b_pose,\
    bin_c_id: bin_c_pose,\
    bin_d_id: bin_d_pose,\
    bin_e_id: bin_e_pose,\
    bin_f_id: bin_f_pose,\
    bin_g_id: bin_g_pose,\
    bin_h_id: bin_h_pose,\
    bin_i_id: bin_i_pose,\
}

# observe pose

observe_obj_pose = geometry_msgs.msg.Pose()
observe_obj_pose.position.x = 0.79
observe_obj_pose.position.y = 0.11
observe_obj_pose.position.z = 0.75
observe_obj_pose.orientation.w = 0.71
observe_obj_pose.orientation.x = -0.01
observe_obj_pose.orientation.y = 0.70
observe_obj_pose.orientation.z = 0.01

# pick pose

pick_obj_pose = geometry_msgs.msg.Pose()
pick_obj_pose.position.x = 0.80
pick_obj_pose.position.y = 0.11
pick_obj_pose.position.z = 0.15
pick_obj_pose.orientation.w = 0.71
pick_obj_pose.orientation.x = -0.01
pick_obj_pose.orientation.y = 0.70
pick_obj_pose.orientation.z = 0.01

# recognise pose

rec_obj_pose = geometry_msgs.msg.Pose()
rec_obj_pose.position.x = 0.26
rec_obj_pose.position.y = -0.30
rec_obj_pose.position.z = 1.16
rec_obj_pose.orientation.w = 0.57
rec_obj_pose.orientation.x = 0.51
rec_obj_pose.orientation.y = 0.58
rec_obj_pose.orientation.z = -0.41

# weigh pose

weigh_obj_pose = geometry_msgs.msg.Pose()
weigh_obj_pose.position.x = 0.83
weigh_obj_pose.position.y = 0.10
weigh_obj_pose.position.z = 1.50
weigh_obj_pose.orientation.w = 1.00
weigh_obj_pose.orientation.x = 0.00
weigh_obj_pose.orientation.y = 0.00
weigh_obj_pose.orientation.z = 0.02

# object ids

obj_a_id = "dove_beauty_bar"
obj_b_id = "one_with_nature_soap_dead_sea_mud"
obj_c_id = "champion_copper_plus_spark_plug"
obj_d_id = "crayola_64_ct"
obj_e_id = "dr_browns_bottle_brush"
obj_f_id = "elmers_washable_no_run_school_glue"
obj_g_id = "expo_dry_erase_board_eraser"
obj_h_id = "feline_greenies_dental_treats"
obj_i_id = "first_years_take_and_toss_straw_cup"
obj_j_id = "genuine_joe_plastic_stir_sticks"
obj_k_id = "highland_6539_self_stick_notes"
obj_l_id = "kong_air_dog_squeakair_tennis_ball"
obj_m_id = "kong_duck_dog_toy"
obj_n_id = "kong_sitting_frog_dog_toy"
obj_o_id = "kyjen_squeakin_eggs_plush_puppies"
obj_p_id = "laugh_out_loud_joke_book"
obj_q_id = "mark_twain_huckleberry_finn"
obj_r_id = "mead_index_cards"
obj_s_id = "mommys_helper_outlet_plugs"
obj_t_id = "munchkin_white_hot_duck_bath_toy"
obj_u_id = "oreo_mega_stuf"
obj_v_id = "paper_mate_12_count_mirado_black_warrior"
obj_w_id = "rolodex_jumbo_pencil_cup"
obj_x_id = "safety_works_safety_glasses"
obj_y_id = "sharpie_accent_tank_style_highlighters"
obj_z_id = "stanley_66_052"

obj_ids =\
[\
    obj_a_id,\
    obj_b_id,\
    obj_c_id,\
    obj_d_id,\
    obj_e_id,\
    obj_f_id,\
    obj_g_id,\
    obj_h_id,\
    obj_i_id,\
    obj_j_id,\
    obj_k_id,\
    obj_l_id,\
    obj_m_id,\
    obj_n_id,\
    obj_o_id,\
    obj_p_id,\
    obj_q_id,\
    obj_r_id,\
    obj_s_id,\
    obj_t_id,\
    obj_u_id,\
    obj_v_id,\
    obj_w_id,\
    obj_x_id,\
    obj_y_id,\
    obj_z_id,\
]

# object weights

obj_weights =\
{\
    obj_a_id: 473,\
    obj_b_id: 208,\
    obj_c_id:  54,\
    obj_d_id: 374,\
    obj_e_id:  75,\
    obj_f_id: 136,\
    obj_g_id:  19,\
    obj_h_id: 160,\
    obj_i_id: 109,\
    obj_j_id: 279,\
    obj_k_id: 183,\
    obj_l_id:  94,\
    obj_m_id:  22,\
    obj_n_id:  29,\
    obj_o_id:  47,\
    obj_p_id:  82,\
    obj_q_id: 162,\
    obj_r_id: 141,\
    obj_s_id:  80,\
    obj_t_id:  75,\
    obj_u_id: 387,\
    obj_v_id:  72,\
    obj_w_id:  92,\
    obj_x_id:  46,\
    obj_y_id: 107,\
    obj_z_id:  71,\
}

# file dictionary keys

tote_obj_ids_key = "work_order"
bin_obj_ids_key = "bin_contents"

""" Functions """

def get_bin_obj_ids(data, bin_id):
    return data[bin_obj_ids_key][bin_id]

def get_tote_obj_ids(data):
    tote_obj_ids = data[tote_obj_ids_key]

    # remove spark plug as lost!
    if obj_c_id in tote_obj_ids:
        del tote_obj_ids[obj_c_id]

    return tote_obj_ids

def init():
    dep_obj.init("dep_obj_srv")
    man_obj.init("man_obj_srv")
    rec_obj.init("rec_obj_srv")
    pos_arm.init("pos_arm_srv")
    sns_obj.init("sns_obj_srv")
    wgh_obj.init("wgh_obj_srv")

    rospy.init_node("tsk_pln_node")

def pick_obj():
    pos_arm.set_pose(observe_obj_pose)
    obj_pos = dep_obj.get_obj_pos()

    temp_observe_obj_pose = geometry_msgs.msg.Pose()
    temp_observe_obj_pose.position.x = observe_obj_pose.position.x
    temp_observe_obj_pose.position.y = observe_obj_pose.position.y
    temp_observe_obj_pose.position.z = observe_obj_pose.position.z
    temp_observe_obj_pose.orientation.w = observe_obj_pose.orientation.w
    temp_observe_obj_pose.orientation.x = observe_obj_pose.orientation.x
    temp_observe_obj_pose.orientation.y = observe_obj_pose.orientation.y
    temp_observe_obj_pose.orientation.z = observe_obj_pose.orientation.z

    temp_x = 0.0;
    temp_y = 0.0;
    temp_angle = 0.0;

    while obj_pos == "":
        # move arm to slightly different position
        temp_x = temp_x + 0.05 * numpy.cos(temp_angle) - 1.0
        temp_y = temp_y + 0.05 * numpy.sin(temp_angle)
        temp_observe_obj_pose.position.x = observe_obj_pose.position.x + temp_x
        temp_observe_obj_pose.position.y = observe_obj_pose.position.y + temp_y
        temp_angle = temp_angle + 0.52
        pos_arm.set_pose(temp_observe_obj_pose)
        obj_pos = dep_obj.get_obj_pos()

    coords = obj_pos.split(",")

    # move arm over object
    new_pick_obj_pose = geometry_msgs.msg.Pose()
    new_pick_obj_pose.position.x = temp_observe_obj_pose.position.x - 0.055 * ( float(coords[1]) - 215 )
    new_pick_obj_pose.position.y = temp_observe_obj_pose.position.y - 0.055 * ( float(coords[0]) - 240 )
    new_pick_obj_pose.position.z = temp_observe_obj_pose.position.z
    new_pick_obj_pose.orientation.w = temp_observe_obj_pose.orientation.w
    new_pick_obj_pose.orientation.x = temp_observe_obj_pose.orientation.x
    new_pick_obj_pose.orientation.y = temp_observe_obj_pose.orientation.y
    new_pick_obj_pose.orientation.z = temp_observe_obj_pose.orientation.z
    pos_arm.set_pose(new_pick_obj_pose)

    while sns_obj.is_obj_sns() == False:
        # move arm towards object
        new_pick_obj_pose.position.z = new_pick_obj_pose.position.z - 0.01
        pos_arm.set_pose(new_pick_obj_pose)
        continue;

    man_obj.grab_obj()

def read_input_file(input_file_path):
    json_data = open(input_file_path)
    return json.load(json_data)

def id_obj(obj_ids):
    pos_arm.set_pose(weigh_obj_pose);
    weight = wgh_obj.get_obj_weight()

    # filter obj ids by weight
    filt_obj_ids = [ ]

    for i in range(0, len(obj_ids)):
        obj_id = obj_ids[i]
        obj_weight = obj_weights[obj_id]

        if obj_weight > (weight * 0.8) and obj_weight < (weight * 1.2):
            filt_obj_ids.append(obj_id)
            rospy.loginfo(obj_id)

    if len(filt_obj_ids) == 0:
        return ""

    pos_arm.set_pose(rec_obj_pose)
    return rec_obj.get_obj_id(filt_obj_ids)

def sel_bin(data):
    bins = { }

    tot_bin_prob = 0;

    for i in range(0, len(bin_ids)):
        bin_id = bin_ids[i]
        bin_obj_ids = get_bin_obj_ids(data, bin_id)
        len_bin_obj_ids = len(bin_obj_ids)
        bin_prob = 0
        if len_bin_obj_ids in bin_probs:
            bin_prob = bin_probs[len_bin_obj_ids]
        else:
            bin_prob = 0

        bins[bin_id] = bin_prob
        tot_bin_prob = tot_bin_prob + bin_prob

    # normalize bin selection probabilities

    for i in range(0, len(bin_ids)):
        bin_id = bin_ids[i]
        bins[bin_id] = bins[bin_id] / tot_bin_prob

    # use roulette wheel to select bin

    x = random.random()

    for i in range(0, len(bin_ids)):
        bin_id = bin_ids[i]
        x = x - bins[bin_id]
        if x < 0:
           return bin_id

    # should never get here
    raise Exception("roulette wheel selection")

def stow_obj(bin_id):
    pos_arm.set_pose(bin_poses[bin_id])
    man_obj.drop_obj()

def write_output_file(output_file_path, data):
    with open(output_file_path, 'w') as outfile:
        json.dump(data, outfile)

""" Entry Point """

if __name__ == "__main__":
    init()

    input_file_path = rospy.get_param("input_file_path", "stow.json")
    output_file_path = rospy.get_param("output_file_path", "task.json")

    data = read_input_file(input_file_path)
    tote_obj_ids = get_tote_obj_ids(data)

    # stow items in tote
    while len(tote_obj_ids) > 0:
        pick_obj()

        obj_id = id_obj(tote_obj_ids)

        rospy.loginfo("object recognised: " + obj_id)

        if obj_id == "":
            # failed to detect object, put it back
            pos_arm.set_pose(pick_obj_pose)
            man_obj.drop_obj()
        else:

            # successful detected object
            bin_id = sel_bin(data)
            stow_obj(bin_id)

            # update data

            bin_obj_ids = get_bin_obj_ids(data, bin_id)
            bin_obj_ids.append(obj_id)
            tote_obj_ids.remove(obj_id)

    write_output_file(output_file_path, data)

# alternative sel_bin implementation, seeks to maximise points scored
"""
# better to specify a limit i.e. 5
def sel_bin(data):
    bins = { }
    any_bin_has_5_or_more_items = False

    for i in range(0, len(bin_ids)):
        bin_id = bin_ids[i]
        bin_cont = getBinContents(data, bin_id)
        len_bin_cont = len(bin_cont)
        bins[bin_id] = len_bin_cont
        if len_bin_cont > 4:
            any_bin_has_5_or_more_items = True

    print bins
    print any_bin_has_5_or_more_items

    if any_bin_has_5_or_more_items:
        # return least populated bin with 5 or more items

        for i in range(0, len(bin_ids)):
            bin_id = bin_ids[i]
            if bins[bin_id] < 5:
               # bin contains fewer than 5 items, exclude it from the search
               bins[bin_id] = 999

        min_len_bin_id = bin_ids[0]

        for i in range(1, len(bin_ids)):
            bin_id = bin_ids[i]
            if bins[bin_id] < bins[min_len_bin_id]:
                min_len_bin_id = bin_id

        return min_len_bin_id
    else:
        # return most populated bin
        max_len_bin_id = bin_ids[0]

        for i in range(1, len(bin_ids)):
            bin_id = bin_ids[i]
            if bins[bin_id] > bins[max_len_bin_id]:
                max_len_bin_id = bin_id

        return max_len_bin_id
"""


from odoo import api, fields, models

class assign_followers_settings(models.Model):
    _name = 'assign.followers.settings'
    _description = 'Settings for Assign and Remove followers to a Record'

    name = fields.Char(string='Name', required=True)
    crm_team = fields.Many2one('mail.channel', string="Channel", copy=False)
    model_id = fields.Many2one('ir.model', 'Model')
    is_check = fields.Boolean('check', default=False, copy=False)

    @api.multi
    def create_action(self,id):
        crm_lead = self.env['crm.lead'].search([('id','=',id)])
        mail_follow_id = self.env['mail.followers']
        for wizard in self:
            new_channels = wizard.crm_team
        for crm in crm_lead:
            if self.crm_team:
                if not mail_follow_id.search([('res_model', '=', self.model_id.model), ('res_id', '=', crm.id), ('channel_id', '=', self.crm_team.id)]):
                    crm.message_subscribe([],new_channels.ids)
                    self.is_check = True


import discord
from discord.ext import commands
import random
import sys
import os
import random
from random import randint

class Roles(commands.Cog):
    def __init__(self,bot):
        self.bot = bot
    #ctx.send

    @commands.command(brief="Change role using `$changerole <role_name>`")
    async def changerole(self, ctx, *args):
        member = ctx.message.author
        if len(args) == 1:
            role_name = args[0]
            if role_name == 'Mod':
                await ctx.send('Ask admin')
            else:
                role = None
                roles = ctx.guild.roles
                for rol in roles:
                    if rol.name == role_name:
                        role = rol
                if role == None:
                    print(role)
                    await ctx.send('Enter a valid role name')
                else:
                    print(role)
                    await member.add_roles(role,reason='User prompt')
        else:
            await ctx.send('Enter one role')

    @commands.command(brief="Remomve role using `$rmrole <role_name>`")
    async def rmrole(self, ctx, *args):
        member = ctx.message.author
        if len(args) == 1:
            role_name = args[0]
            role = None
            roles = ctx.guild.roles
            for rol in roles:
                if rol.name == role_name:
                    role = rol
            if role == None:
                await ctx.send('Enter a valid role name')
            else:
                await member.remove_roles(role,reason='User prompt')
        else:
            await ctx.send('Enter one role')

def setup(bot):
    bot.add_cog(Roles(bot))


# -*- coding: utf-8 -*-
"""
@author zwczou@gmail.com
"""

from ..schema import Schema, And, Use, clear, split, Optional, match, length

valid_mobile = r'^1\d{10}$'

order_id_list_form = Schema({
    "order_id": And(Use(split()), [And(Use(int), lambda x: x > 0)], lambda x: 0 < len(x) <= 200),
})

order_id_form = Schema({
    "order_id": And(Use(int), lambda x: x > 0),
})

_order = ['', "created", "canceled", "paid", "shipped", "closed"]

user_order_form = Schema({
    Optional("status", default=0): And(Use(lambda x: _order.index(x)), lambda x: x > 0),
    Optional("offset", default=0): And(Use(int), lambda x: x >= 0),
    Optional("limit", default=10): And(Use(int), lambda x: x >= -1),
})

def split_item_id(a):
    return dict((int(k), int(v)) for k, v in [x.split(':') for x in a.split(',')])

create_order_form = Schema({
    "item_id": And(Use(clear()), Use(split_item_id), length(1, 200)),
    "nickname": And(Use(clear()), length(1, 20)),
    "mobile": And(match(valid_mobile)),
    Optional("zip_code", ""): And(Use(clear()), lambda x: len(x) == 6),
    "address": And(lambda x: 9 < len(x) < 100, Use(clear())),
    Optional("leave_message", ""): And(basestring, Use(clear(strip=False))),
})


file = open("super_villains.txt")

name_list = []
for line in file:
    line = line.strip()
    name_list.append(line)

file.close()

# # Linear search
# key = "Claude Avernus"
#
# i = 0
# while i < len(name_list) and name_list[i] != key:
#     i += 1
#
# if i < len(name_list):
#     print("The name is at position", i)
# else:
#     print("The name was not in the list.")
#
#
# class Alien:
#     """ Class that defines an alien """
#     def __init__(self, color, weight):
#         self.color = color
#         self.weight = weight
#
#
# def has_property(my_alien):
#     """ Check to see if an alien has a property. In this case, is the alien green?  """
#     if my_alien.color.upper() == "GREEN":
#         return True
#     else:
#         return False
#
# def check_if_one_item_has_property_v1(list):
#     """
#     :param list:
#     :return: True if at least one item has a property.
#     """
#     i = 0
#     while i < len(list) and not has_property(list[i]):
#         i += 1
#     if i < len(list):
#         return True
#     if i == len(list):
#         return False
#
# def check_if_one_item_has_property_v2(list):
#     """
#     :param list:
#     :return: True id at least one item has a property. Works the same as v1, but less code.
#     """
#     for item in list:
#         if has_property(item):
#             return True
#         return False
#
# def check_if_all_items_have_property(list):
#     """
#     :param list:
#     :return: True if at ALL items have a property.
#     """
#     for item in list:
#         if not has_property(item):
#             return False
#     return True
#
# def get_matching_items(list):
#     """
#     :param list:
#     :return: A brand new list that holds all the items that match our property.
#     """
#     matchinglist = []
#     for item in list:
#         if has_property(item):
#             matchinglist.append(item)
#     return len(matchinglist)
#
# alien_list = []
# alien_list.append(Alien("Green", 42))
# alien_list.append(Alien("Red", 40))
# alien_list.append(Alien("Blue", 41))
# alien_list.append(Alien("Purple", 40))
#
# result = check_if_one_item_has_property_v1(alien_list)
# print("Result of test check_if_one_item_has_property_v1: ", result)
# result = check_if_one_item_has_property_v2(alien_list)
# print("Result of test check_if_one_item_has_property_v2: ", result)
# result = check_if_all_items_have_property(alien_list)
# print("check_if_all_items_have_property: ", result)
# result = get_matching_items(alien_list)
# print("Number of items returned from test get_matching_items: ", result)

# Binary search
desired_element = "Brutus the Gruesome"

lower_bound = 0
upper_bound = len(name_list)-1
found = False

while lower_bound <= upper_bound and not found:
    middle_pos = (lower_bound + upper_bound) // 2
    if name_list[middle_pos] < desired_element:
        lower_bound = middle_pos + 1
    elif name_list[middle_pos] > desired_element:
        upper_bound = middle_pos
    else:
        found = True

if found:
    print("The name is found al position ", middle_pos)
else:
    print("The name was not found in the list.")


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@File       : logger_utils.py
@Author     : Yuka
@Time       : 2021/3/23 14:49
@Version    : 1.0.0
@Description:
"""
# log相关配置
import logging
import logging.config
import os
import time
from config import HOME

log_path = os.path.join(HOME, "log")
if not os.path.exists(log_path):
    os.mkdir(log_path)
LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        # 日志格式
        'standard': {
            'format': '[%(asctime)s] [%(filename)s:%(lineno)d] [%(module)s:%(funcName)s] '
                      '[%(levelname)s]- %(message)s'},
        'simple': {  # 简单格式
            'format': '[%(asctime)s] [%(levelname)s] %(message)s'
        },
    },
    # 过滤
    'filters': {
    },
    # 定义具体处理日志的方式
    'handlers': {
        # 默认记录所有日志
        'default': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(log_path, 'all-{}.log'.format(time.strftime('%Y-%m-%d'))),
            'maxBytes': 1024 * 1024 * 5,  # 文件大小
            'backupCount': 5,  # 备份数
            'formatter': 'simple',  # 输出格式
            'encoding': 'utf-8',  # 设置默认编码，否则打印出来汉字乱码
        },
        # 输出错误日志
        'error': {
            'level': 'ERROR',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(log_path, 'error-{}.log'.format(time.strftime('%Y-%m-%d'))),
            'maxBytes': 1024 * 1024 * 5,  # 文件大小
            'backupCount': 5,  # 备份数
            'formatter': 'simple',  # 输出格式
            'encoding': 'utf-8',  # 设置默认编码
        },
        # 控制台输出
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        # 输出info日志
        'info': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(log_path, 'info-{}.log'.format(time.strftime('%Y-%m-%d'))),
            'maxBytes': 1024 * 1024 * 5,
            'backupCount': 5,
            'formatter': 'simple',
            'encoding': 'utf-8',  # 设置默认编码
        },
    },
    # 配置用哪几种 handlers 来处理日志
    'loggers': {
        # 类型 为 django 处理所有类型的日志， 默认调用
        'django': {
            'handlers': ['default', 'console'],
            'level': 'INFO',
            'propagate': False
        },
        # logger_utils 调用时需要当作参数传入
        'api_logger': {
            'handlers': ['default', 'error', 'info', 'console'],
            'level': 'INFO',
            'propagate': False
        },
    }
}
logging.config.dictConfig(LOGGING)
logger = logging.getLogger('api_logger')
# logger.setLevel("info")


'''
Button付きのPopupを簡単に作れる

例えば[Yes][No]Buttonの付いたPopupを使って利用者に質問したい時は

yesno_popup = PopupWithButtons(
    content=Label(text='Do you like Kivy?'),
    button_texts=('Yes', 'No', ),
)
yesno_popup.bind(on_button_press=(
    lambda popup, button: print(button.text, 'was pressed.')
))
yesno_popup.open()

とすればいい。もし`auto_dismiss`をFalseにした場合は、Buttonを押しても自動でPopupが
閉じなくなるので、以下のようにCallback関数内で明示的に閉じる必要がある。

yesno_popup = PopupWithButtons(
    content=Label(text='Do you like Kivy?'),
    button_texts=('Yes', 'No', ),
    auto_dismiss=False,  # 相違点
)
yesno_popup.bind(on_button_press=(
    lambda popup, button: popup.dismiss()  # 相違点
))
yesno_popup.open()
'''

__all__ = ('PopupWithButtons', )

from kivy.lang import Builder
from kivy.factory import Factory
from kivy.properties import (
    ObjectProperty, ListProperty,
)

Builder.load_string(r"""
<PopupWithButtons>:
    _container: container
    _buttons_layout: buttons_layout
    BoxLayout:
        size: root.size
        orientation: 'vertical'
        padding: '5sp'
        spacing: '5sp'
        RelativeLayout:
            id: container
        BoxLayout:
            id: buttons_layout
            size_hint_y: 0.1
            padding: '5sp'
            spacing: '5sp'
""")

class PopupWithButtons(Factory.ModalView):

    button_class = ObjectProperty(Factory.Button)
    '''使用するButtonを任意の物に置き換えられる'''

    content = ObjectProperty(baseclass=Factory.Widget)
    '''PopupのMainとなる中身。このWidgetの下にButtonが並べられる'''

    button_texts = ListProperty(['OK', ])
    '''Buttonに表示する文字列のlist。このlistの要素数だけButtonが作られる。'''

    _container = ObjectProperty(baseclass=Factory.Widget)
    _buttons_layout = ObjectProperty(baseclass=Factory.Widget)

    __events__ = ('on_button_press', 'on_button_release', )

    def on_button_press(self, *args):
        if self.auto_dismiss:
            self.dismiss()

    def on_button_release(self, *args):
        if self.auto_dismiss:
            self.dismiss()

    def _on_button_press(self, button):
        self.dispatch('on_button_press', button)

    def _on_button_release(self, button):
        self.dispatch('on_button_release', button)

    def on_content(self, __, widget):
        if self._container is not None:
            self._refresh_container()

    def on__container(self, __, container):
        if container is not None and self.content is not None:
            self._refresh_container()

    def _refresh_container(self):
        container = self._container
        container.clear_widgets()
        container.add_widget(self.content)

    def on_button_texts(self, __, button_texts):
        if self._buttons_layout is not None:
            self._refresh_buttons_layout()

    def on__buttons_layout(self, __, layout):
        if layout is not None:
            self._refresh_buttons_layout()

    def _refresh_buttons_layout(self):
        layout = self._buttons_layout
        layout.clear_widgets()
        layout_add_widget = layout.add_widget
        button_class = self.button_class
        for text in self.button_texts:
            button = button_class(text=str(text))
            button.bind(
                on_press=self._on_button_press,
                on_release=self._on_button_release,
            )
            layout_add_widget(button)

def _test():
    from kivy.base import runTouchApp

    yesno_popup = PopupWithButtons(
        content=Factory.Label(text='Do you like Kivy?'),
        button_texts='Yes No'.split(),
    )
    yesno_popup.bind(on_button_press=(
        lambda popup, button: print(button.text, 'was pressed.')
    ))

    ok_popup = PopupWithButtons(
        content=Factory.Label(text='"The armor I used to seal my all too '
                                   'powerful strength is now broken."'),
        button_texts=('OK', ),
    )

    pick_color_popup = PopupWithButtons(
        content=Factory.ColorPicker(),
        button_texts='OK Cancel'.split(),
        auto_dismiss=False,
    )
    def on_button_press(popup, button):
        if button.text == 'Cancel':
            print('Cancelled.')
        elif button.text == 'OK':
            print('Color', popup.content.color, 'was choosed.')
        popup.dismiss()
    pick_color_popup.bind(on_button_press=on_button_press)

    root = Builder.load_string(r"""
BoxLayout:
    orientation: 'vertical'
    spacing: '5sp'
    padding: '5sp'
    Button:
        id: yesno
        text: 'Yes No Popup'
    Button:
        id: ok
        text: 'OK Popup'
    Button:
        id: color
        text: 'Pick Color'
""")
    root.ids.yesno.bind(on_press=yesno_popup.open)
    root.ids.ok.bind(on_press=ok_popup.open)
    root.ids.color.bind(on_press=pick_color_popup.open)
    runTouchApp(root)

if __name__ == '__main__':
    _test()


"""
This module for define various command to this bot
"""

def get_help(admin=False):
    """For help command when the bot is being summoned on group chat"""
    if admin is True:
        return "\nDaftar command:" \
               "\n/help" \
               "\n/jadwal hari ini" \
               "\n/jadwal besok" \
               "\n/jadwal klub" \
               "\n/fact" \
               "\n/mcstatus" \
               "\n/mcplayers"
    return "\nDaftar command:" \
           "\n/solve: Mencari solusi persamaan kuadrat, contoh input:" \
           "\n/solve x^2 - y^2 + 1 = 0"


from core.snake.snake import Snake

class Player:
    def __init__(self, game, number):
        self.game = game
        self.number = number

        self.snake = Snake(self, [])
        # self.controller = Controller.getController(self.game.controllersConfig["TYPE"][self.number], self.game.controllersConfig["GAMEPAD_NUMBERS"][self.number], self.game.actionButton)


"""Decipher a message encrypted with the route transposition cipher used by
the Union/North during the American Civil War.

Required input: a message string, number of columns in cipher, number of rows
in cipher, a string containing the route/key.

Designed for whole-word transposition ciphers with variable rows and columns.
Assumes encryption began at either top or bottom of a column.
Route (key) indicates the order to read columns and the direction to traverse.
Negative column numbers mean start at bottom and read upward. Positive column
numbers mean start at top and read downward.

EXAMPLE
4x4 matrix; route is -1 2 -3 4
"0" is forbidden
Arrows show encryption route.

  1   2   3   4
 ___ ___ ___ ___
| ^ | | | ^ | | |
|_|_|_v_|_|_|_v_|
| ^ | | | ^ | | |
|_|_|_v_|_|_|_v_|
| ^ | | | ^ | | |
|_|_|_v_|_|_|_v_|
| ^ | | | ^ | | |
|_|_|_v_|_|_|_v_|
START        END

The message is written across each row. Last row is filled with dummy words.
"""

from sys import exit

# USER INPUT
# Type or paste the encrypted string between the triple-quotes
ciphertext = """16 12 8 4 0 1 5 9 13 17 18 14 10 6 2 3 7 11 15 19"""
COLS = 4  # Enter the number of columns in the cipher
ROWS = 5  # Enter the number of rows in the cipher
# Type or paste the route/key between the quotes; put spaces between numbers
route = "-1 2 -3 4"

# !!!! DO NOT EDIT BELOW !!!!

def main():
    """Controls the flow of the program. Ends by displaying the decrypted
    plaintext."""
    print(f"Ciphertext: {ciphertext}\nTrying {COLS} columns and {ROWS} rows."
          f"\nTrying route: {route}")

    ciphertext_list = list(ciphertext.split())
    validate_col_row(ciphertext_list)
    route_int = route_to_int(route)
    translation_matrix = build_matrix(route_int, ciphertext_list)
    plaintext = decrypt(translation_matrix)

    print(f"Plaintext: {plaintext}")

def build_matrix(route_list, list_of_ciphertext):
    """Turn every n items in a list into a new item in a list of lists."""
    translation_matrix = [None] * COLS  # Initialize an index for each column
    start = 0
    stop = ROWS

    for key in route_list:
        if key < 0:
            # Read bottom to top
            col = list_of_ciphertext[start:stop]
        elif key > 0:
            # Read top to bottom
            col = list(reversed(list_of_ciphertext[start:stop]))
        translation_matrix[abs(key) - 1] = col
        start += ROWS
        stop += ROWS

    return translation_matrix

def decrypt(translation_matrix):
    """Loop through nested lists within a translation matrix, popping off last
    item and appending to a string (with a space)."""
    plaintext = ""
    for _ in range(ROWS):
        for col in translation_matrix:
            word = str(col.pop())
            plaintext += word + " "

    return plaintext

def validate_col_row(list_of_ciphertext):
    """Checks that input columns and rows are valid for the input message."""
    factors = []
    len_ciphertext = len(list_of_ciphertext)
    for i in range(2, len_ciphertext):  # Excludes 1-column ciphers
        if len_ciphertext % i == 0:
            factors.append(i)
    print(f"Length of cipher: {len_ciphertext} words\nAcceptable column/row "
          f"values include: {factors}")

    if ROWS * COLS != len_ciphertext:
        print("ERROR - Input columns and rows are not factors of length of"
              "ciphertext. Terminating...")
        exit(1)

def route_to_int(route):
    """Turn route into list of integers, and check validity."""
    route_int = [int(num) for num in route.split()]
    if (len(route_int) != COLS
        or min(route_int) < -COLS
        or max(route_int) > COLS
        or 0 in route_int):
        print("ERROR - Invalid route given. Terminating...")
        exit(1)
    else:
        return route_int

if __name__ == "__main__":
    main()