import itertools
import json
import logging
import os
import sys
import warnings
import time as tm
from collections import OrderedDict
from collections.abc import Iterable
from getpass import getuser

import numpy as np
import pandas as pd
import xarray as xr

import dask
from dask.diagnostics import ProgressBar
from netCDF4 import Dataset
from cftime import date2num
from datetime import datetime

from xarray.backends.locks import get_write_lock, combine_locks, NETCDFC_LOCK

import metsim.constants as cnst
from metsim import io
from metsim.datetime import date_range
from metsim.disaggregate import disaggregate
from metsim.methods import mtclim, passthrough
from metsim.physics import solar_geom
from metsim.units import converters

NO_SLICE = {}
DASK_CORE_SCHEDULERS = ['multiprocessing', 'threading', 'synchronous',
                        'processes', 'threads', 'single-threaded', 'sync']

references = '''Thornton, P.E., and S.W. Running, 1999. An improved algorithm for estimating incident daily solar radiation from measurements of temperature, humidity, and precipitation. Agricultural and Forest Meteorology, 93:211-228.
Kimball, J.S., S.W. Running, and R. Nemani, 1997. An improved method for estimating surface humidity from daily minimum temperature. Agricultural and Forest Meteorology, 85:87-98.
Bohn, T. J., B. Livneh, J. W. Oyler, S. W. Running, B. Nijssen, and D. P. Lettenmaier, 2013a: Global evaluation of MT-CLIM and related algorithms for forcing of ecological and hydrological models, Agr. Forest. Meteorol., 176, 38-49, doi:10.1016/j.agrformet.2013.03.003.'''

DEFAULT_TIME_UNITS = 'minutes since 2000-01-01 00:00:00.0'

now = tm.ctime(tm.time())
user = getuser()

attrs = {'pet': {'units': 'mm timestep-1',
                 'long_name': 'potential evaporation',
                 'standard_name': 'water_potential_evaporation_flux',
                 'missing_value': np.nan, 'fill_value': np.nan},
         'prec': {'units': 'mm timestep-1', 'long_name': 'precipitation',
                  'standard_name': 'precipitation_flux',
                  'missing_value': np.nan, 'fill_value': np.nan},
         'shortwave': {'units': 'W m-2', 'long_name': 'shortwave radiation',
                       'standard_name': 'surface_downwelling_shortwave_flux',
                       'missing_value': np.nan, 'fill_value': np.nan},
         'longwave': {'units': 'W m-2', 'long_name': 'longwave radiation',
                      'standard_name': 'surface_downwelling_longwave_flux',
                      'missing_value': np.nan, 'fill_value': np.nan},
         't_max': {'units': 'C', 'long_name': 'maximum daily air temperature',
                   'standard_name': 'daily_maximum_air_temperature',
                   'missing_value': np.nan, 'fill_value': np.nan},
         't_min': {'units': 'C', 'long_name': 'minimum daily air temperature',
                   'standard_name': 'daily_minimum_air_temperature',
                   'missing_value': np.nan, 'fill_value': np.nan},
         'temp': {'units': 'C', 'long_name': 'air temperature',
                  'standard_name': 'air_temperature',
                  'missing_value': np.nan, 'fill_value': np.nan},
         'vapor_pressure': {'units': 'Pa', 'long_name': 'vapor pressure',
                            'standard_name': 'vapor_pressure',
                            'missing_value': np.nan, 'fill_value': np.nan},
         'air_pressure': {'units': 'kPa', 'long_name': 'air pressure',
                          'standard_name': 'air_pressure',
                          'missing_value': np.nan, 'fill_value': np.nan},
         'tskc': {'units': 'fraction', 'long_name': 'cloud fraction',
                  'standard_name': 'cloud_fraction',
                  'missing_value': np.nan, 'fill_value': np.nan},
         'rel_humid': {'units': '%', 'long_name': 'relative humidity',
                       'standard_name': 'relative_humidity',
                       'missing_value': np.nan, 'fill_value': np.nan},
         'daylength': {'units': 's', 'long_name': 'daylength',
                       'standard_name': 'length of day',
                       'missing_value': np.nan, 'fill_value': np.nan},
         'spec_humid': {'units': 'g g-1', 'long_name': 'specific humidity',
                        'standard_name': 'specific_humidity',
                        'missing_value': np.nan, 'fill_value': np.nan},
         'wind': {'units': 'm s-1', 'long_name': 'wind speed',
                  'standard_name': 'wind_speed',
                  'missing_value': np.nan, 'fill_value': np.nan},
         '_global': {'conventions': '1.6', 'title': 'Output from MetSim',
                     'institution': 'University of Washington',
                     'source': 'metsim.py',
                     'history': 'Created: {0} by {1}'.format(now, user),
                     'comment': 'no comment at this time'}}

attrs = {k: OrderedDict(v) for k, v in attrs.items()}

available_outputs = {n : {'out_name': n, 'units': attrs[n]['units']}
                     for n in attrs if n != '_global'}
default_outputs = ['temp', 'prec', 'shortwave', 'longwave',
                     'vapor_pressure', 'rel_humid']

class MetSim(object):
    """
    MetSim handles the distribution of jobs that write to a common file
    by launching muliple processes and queueing up their writeback so that
    work can be done while IO is happening.
    """

    # Class variables
    methods = {'mtclim': mtclim, 'passthrough': passthrough}
    params = {
        "period_ending": False,
        "is_worker": False,
        "method": 'mtclim',
        "domain": '',
        "state": '',
        "out_dir": '',
        "out_prefix": 'forcing',
        "start": 'forcing',
        "stop": 'forcing',
        "forcing_fmt": 'netcdf',
        "time_step": -1,
        "calendar": 'standard',
        "prec_type": 'uniform',
        "out_precision": 'f4',
        "verbose": 0,
        "sw_prec_thresh": 0.0,
        "utc_offset": False,
        "lw_cloud": 'cloud_deardorff',
        "lw_type": 'prata',
        "prec_type": 'uniform',
        "tdew_tol": 1e-6,
        "tmax_daylength_fraction": 0.67,
        "rain_scalar": 0.75,
        "tday_coef": 0.45,
        "lapse_rate": 0.0065,
        "out_vars": {n: available_outputs[n] for n in default_outputs},
        "out_freq": None,
        "chunks": NO_SLICE,
        "scheduler": 'distributed',
        "num_workers": 1,
    }

    def __init__(self, params: dict, domain_slice=NO_SLICE):
        """
        Constructor
        """
        self._domain = None
        self._met_data = None
        self._state = None
        self._client = None
        self._domain_slice = domain_slice
        self.progress_bar = ProgressBar()
        self.params.update(params)
        logging.captureWarnings(True)
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(self.params['verbose'])

        formatter = logging.Formatter(' - '.join(
            ['%asctime)s', '%(name)s', '%(levelname)s', '%(message)s']))
        ch = logging.StreamHandler(sys.stdout)
        ch.setFormatter(formatter)
        ch.setLevel(self.params['verbose'])
        # set global dask scheduler
        if domain_slice is NO_SLICE:
            if self.params['scheduler'] in DASK_CORE_SCHEDULERS:
                dask.config.set(scheduler=self.params['scheduler'])
            else:
                from distributed import Client, progress
                if 'distributed' == self.params['scheduler']:
                    self._client = Client(
                        n_workers=self.params['num_workers'],
                        threads_per_worker=1)
                    if self.params['verbose'] == logging.DEBUG:
                        self.progress_bar = progress
                elif os.path.isfile(self.params['scheduler']):
                    self._client = Client(
                        scheduler_file=self.params['scheduler'])
                else:
                    self._client = Client(self.params['scheduler'])
        else:
            dask.config.set(scheduler=self.params['scheduler'])

        # Set up logging
        # If in verbose mode set up the progress bar
        if self.params['verbose'] == logging.DEBUG:
            if 'distributed' != self.params['scheduler']:
                self.progress_bar.register()
                self.progress_bar = lambda x: x
        else:
            # If not in verbose mode, create a dummy function
            self.progress_bar = lambda x: x
        # Create time vector(s)
        self._times = self._get_output_times(
            freq=self.params['out_freq'],
            period_ending=self.params['period_ending'])

        self._update_unit_attrs(self.params['out_vars'])

    def _update_unit_attrs(self, out_vars):
        for k, v in out_vars.items():
            if 'units' in v.keys():
                if v['units'] in converters[k].keys():
                    attrs[k]['units'] = v['units']
                else:
                    self.logger.warn(
                        f'Could not find unit conversion for {k} to {v["units"]}!'
                        f' We will use the default units of'
                        f' {available_outputs[k]["units"]} instead.' )
                    v['units'] = available_outputs[k]['units']
            else:
                v['units'] = available_outputs[k]['units']

    def _validate_force_times(self, force_times):
        for p, i in [('start', 0), ('stop', -1)]:
            # infer times from force_times
            if isinstance(self.params[p], str):
                if self.params[p] == 'forcing':
                    self.params[p] = pd.Timestamp(
                        force_times.values[i]).to_pydatetime()
                elif '/' in self.params[p]:
                    year, month, day = map(int, self.params[p].split('/'))
                    self.params[p] = datetime(year, month, day)
                else:
                    self.params[p] = pd.to_datetime(self.params[p])

        # update calendar from input data (fall back to params version)
        self.params['calendar'] = self.met_data['time'].encoding.get(
            'calendar', self.params['calendar'])

        assert self.params['start'] >= pd.Timestamp(
            force_times.values[0]).to_pydatetime()
        assert self.params['stop'] <= pd.Timestamp(
            force_times.values[-1]).to_pydatetime()

        self.params['state_start'] = (self.params['start'] -
                                      pd.Timedelta("90 days"))
        self.params['state_stop'] = (self.params['start'] -
                                     pd.Timedelta("1 days"))
        if self.params['utc_offset']:
            attrs['time'] = {'units': DEFAULT_TIME_UNITS,
                             'long_name': 'UTC time',
                             'standard_name': 'utc_time'}
        else:
            attrs['time'] = {'units': DEFAULT_TIME_UNITS,
                             'long_name': 'local time at grid location',
                             'standard_name': 'local_time'}

    def convert_monthly_param(self, name):
        self.met_data[name] = self.met_data['prec'].copy()
        months = self.met_data['time'].dt.month
        for m in range(12):
            param = self.domain[name].sel(month=m)
            locations = {'time': self.met_data['time'].isel(time=months == m)}
            self.met_data[name].loc[locations] = param

    @property
    def domain(self):
        if self._domain is None:
            self._domain = io.read_domain(self.params).isel(
                **self._domain_slice)
        return self._domain

    @property
    def met_data(self):
        if self._met_data is None:
            self._met_data = io.read_met_data(self.params, self.domain)
            self._met_data['elev'] = self.domain['elev']
            self._met_data['lat'] = self.domain['lat']
            self._met_data['lon'] = self.domain['lon']

            # process constant_vars
            constant_vars = self.params.get('constant_vars', None)
            if constant_vars:
                da_template = self._met_data[list(self._met_data)[0]]
                for var in constant_vars.keys():
                    self._met_data[var] = xr.full_like(da_template,
                                                       float(constant_vars[var]))

            self._validate_force_times(force_times=self._met_data['time'])
        return self._met_data

    @property
    def state(self):
        if self._state is None:
            self._state = io.read_state(self.params, self.domain)
            self._aggregate_state()
        return self._state

    @property
    def slices(self):
        if not self.params['chunks']:
            return [{d: slice(None) for d in self.domain[['mask']].dims}]

        return chunk_domain(self.params['chunks'], self.domain[['mask']].dims)

    def open_output(self):
        filenames = [self._get_output_filename(times) for times in self._times]
        return xr.open_mfdataset(filenames)

    def run(self):
        self._validate_setup()
        write_locks = {}
        for times in self._times:
            filename = self._get_output_filename(times)
            self.setup_netcdf_output(filename, times)
            write_locks[filename] = combine_locks([NETCDFC_LOCK, get_write_lock(filename)])
        self.logger.info('Starting {} chunks...'.format(len(self.slices)))

        delayed_objs = [wrap_run_slice(self.params, write_locks, dslice)
                        for dslice in self.slices]
        persisted = dask.persist(delayed_objs, num_workers=self.params['num_workers'])
        self.progress_bar(persisted)
        dask.compute(persisted)
        self.logger.info('Cleaning up...')
        try:
            self._client.cluster.close()
            self._client.close()
            if self.params['verbose'] == logging.DEBUG:
                print()
                print('closed dask cluster/client')
        except Exception:
            pass

    def load_inputs(self, close=True):
        self._domain = self.domain.load()
        self._met_data = self.met_data.load()
        self._state = self.state.load()
        if close:
            self._domain.close()
            self._met_data.close()
            self._state.close()

    def setup_netcdf_output(self, filename, times):
        '''setup a single netcdf file'''
        with Dataset(filename, mode="w") as ncout:
            # dims
            dim_sizes = (None, ) + self.domain['mask'].shape
            var_dims = ('time', ) + self.domain['mask'].dims
            chunksizes = [len(times)]
            for d, s in zip(var_dims[1:], dim_sizes[1:]):
                c = int(self.params['chunks'].get(d, s))
                if c <= s:
                    chunksizes.append(c)
                else:
                    chunksizes.append(s)
            create_kwargs = {'chunksizes': chunksizes}
            for d, size in zip(var_dims, dim_sizes):
                ncout.createDimension(d, size)
            # vars
            for varname, varconf in self.params['out_vars'].items():
                ncout.createVariable(
                    varconf['out_name'], self.params['out_precision'], var_dims,
                    **create_kwargs)

            # add metadata and coordinate variables (time/lat/lon)
            time_var = ncout.createVariable('time', 'i4', ('time', ))
            time_var.calendar = self.params['calendar']
            time_var[:] = date2num(
                times.to_pydatetime(),
                units=attrs['time'].get('units', DEFAULT_TIME_UNITS),
                calendar=time_var.calendar)

            dtype_map = {'float64': 'f8', 'float32': 'f4',
                         'int64': 'i8', 'int32': 'i4'}
            for dim in self.domain['mask'].dims:
                dim_vals = self.domain[dim].values
                dim_dtype = dtype_map.get(
                    str(dim_vals.dtype), self.params['out_precision'])
                dim_var = ncout.createVariable(dim, dim_dtype, (dim, ))
                dim_var[:] = dim_vals

            # parameters to not record in the metadata
            skip_params = ['elev', 'lat', 'lon', 'is_worker', 'out_vars',
                           'forcing_vars', 'domain_vars', 'state_vars',
                           'constant_vars', 'references', 'verbose',
                           'num_workers',]
            for k, v in self.params.items():
                if k in skip_params:
                    continue
                # Need to convert some parameters to strings
                if k in ['start', 'stop', 'utc_offset', 'period_ending']:
                    v = str(v)
                elif k in ['state_start', 'state_stop', 'out_freq']:
                    # skip
                    continue
                # Don't include complex types
                if isinstance(v, dict):
                    v = json.dumps(v)
                elif not isinstance(v, str) and isinstance(v, Iterable):
                    v = ', '.join(v)

                if isinstance(v, str):
                    v = v.replace("'", "").replace('"', "")
                attrs['_global'][k] = v

            # set global attrs
            for key, val in attrs['_global'].items():
                setattr(ncout, key, val)

            # set variable attrs
            for key, value in attrs.get('time', {}).items():
                setattr(ncout.variables['time'], key, value)
            for varname, varconf  in self.params['out_vars'].items():
                outname = varconf['out_name']
                for key, val in attrs.get(varname, {}).items():
                    setattr(ncout.variables[outname], key, val)

    def write_chunk(self, locks=None):
        '''write data from a single chunk'''
        if not len(self.params['out_vars']):
            return
        for times in self._times:
            filename = self._get_output_filename(times)
            lock = locks.get(filename, DummyLock())
            time_slice = slice(times[0], times[-1])
            with lock:
                with Dataset(filename, mode="r+") as ncout:
                    for varname, varconf in self.params['out_vars'].items():
                        outname = varconf['out_name']
                        dims = ncout.variables[outname].dimensions[1:]
                        write_slice = ((slice(None), ) + tuple(
                            self._domain_slice[d] for d in dims))
                        ncout.variables[outname][write_slice] = (
                            self.output[varname].sel(time=time_slice).values)

    def run_slice(self):
        """
        Run a single slice of
        """
        self._validate_setup()
        self.disagg = int(self.params['time_step']) < cnst.MIN_PER_DAY
        self.method = MetSim.methods[self.params['method']]
        self.setup_output()
        times = self.met_data['time']
        params = self.params.copy()
        # transform input parameters to floating point values
        params['sw_prec_thresh'] = float(params['sw_prec_thresh'])
        params['rain_scalar'] = float(params['rain_scalar'])
        params['tdew_tol'] = float(params['tdew_tol'])
        params['tmax_daylength_fraction'] = float(params['tmax_daylength_fraction'])
        params['tday_coef'] = float(params['tday_coef'])
        params['tmax_daylength_fraction'] = float(params['tmax_daylength_fraction'])
        params['lapse_rate'] = float(params['lapse_rate'])
        if self.params['prec_type'].upper() in ['TRIANGLE', 'MIX']:
            self.convert_monthly_param('dur')
            self.convert_monthly_param('t_pk')
        for index, mask_val in np.ndenumerate(self.domain['mask'].values):
            if mask_val > 0:
                locs = {d: i for d, i in zip(self.domain['mask'].dims, index)}
            else:
                continue
            df, state = wrap_run_cell(self.method.run, params,
                                      self.met_data.isel(**locs),
                                      self.state.isel(**locs),
                                      self.disagg, times)

            # Cut the returned data down to the correct time index
            # and do any required unit conversions
            for varname in self.params['out_vars']:
                desired_units = self.params['out_vars'][varname]['units']
                out_vals = converters[varname][desired_units](
                     df[varname].values, int(self.params['time_step']))
                self.output[varname][locs] = out_vals

    def _unpack_state(self, result: pd.DataFrame, locs: dict):
        """Put restart values in the state dataset"""
        # We concatenate with the old state values in case we don't
        # have 90 new days to use
        tmin = np.concatenate((self.state['t_min'].isel(**locs).values[:],
                               result['t_min'].values))
        tmax = np.concatenate((self.state['t_max'].isel(**locs).values[:],
                               result['t_max'].values))
        prec = np.concatenate((self.state['prec'].isel(**locs).values[:],
                               result['prec'].values))
        self.state['t_min'].isel(**locs).values[:] = tmin[-90:]
        self.state['t_max'].isel(**locs).values[:] = tmax[-90:]
        self.state['prec'].isel(**locs).values[:] = prec[-90:]
        state_start = result.index[-1] - pd.Timedelta('89 days')
        self.state['time'].values = date_range(
            state_start, result.index[-1], calendar=self.params['calendar'])

    def _get_output_times(self, freq=None, period_ending=False):
        """
        Generate chunked time vectors

        Parameters
        ----------
        freq:
            Output frequency. Given as a Pandas timegrouper string.
            If not given, the entire timeseries will be used.
        period_ending:
            Flag to specify if output timesteps should be period-
            ending. Default is period-beginning

        Returns
        -------
        times:
            A list of timeseries which represent each of times that
            output files will be created for.
        """
        prototype = self.met_data
        self.disagg = int(self.params['time_step']) < cnst.MIN_PER_DAY

        if self.disagg:
            delta = pd.Timedelta('1 days') - pd.Timedelta(
                '{} minutes'.format(self.params['time_step']))
        else:
            delta = pd.Timedelta('0 days')
        if period_ending:
            offset = pd.Timedelta(
                '{} minutes'.format(self.params['time_step']))
        else:
            offset = pd.Timedelta('0 minutes')

        start = pd.Timestamp(prototype['time'].values[0]).to_pydatetime()
        stop = pd.Timestamp(prototype['time'].values[-1]).to_pydatetime()
        times = date_range(start + offset, stop + offset + delta,
                           freq="{}T".format(self.params['time_step']),
                           calendar=self.params['calendar'])

        if freq is None or freq == '':
            times = [times]
        else:
            dummy = pd.Series(np.arange(len(times)), index=times)
            grouper = pd.Grouper(freq=freq)
            times = [t.index for k, t in dummy.groupby(grouper)]
        return times

    def _get_output_filename(self, times):
        suffix = self.get_nc_output_suffix(times)
        fname = '{}_{}.nc'.format(self.params['out_prefix'], suffix)
        output_filename = os.path.join(
            os.path.abspath(self.params['out_dir']), fname)
        return output_filename

    def setup_output(self):

        # output times
        times = self._get_output_times(
            freq=None, period_ending=self.params['period_ending'])[0]

        # Number of timesteps
        n_ts = len(times)

        shape = (n_ts, ) + self.domain['mask'].shape
        dims = ('time', ) + self.domain['mask'].dims
        coords = {'time': times, **self.domain['mask'].coords}
        self.output = xr.Dataset(coords=coords)
        self.output['time'].encoding['calendar'] = self.params['calendar']

        dtype = self.params['out_precision']
        for varname in self.params['out_vars']:
            self.output[varname] = xr.DataArray(
                data=np.full(shape, np.nan, dtype=dtype),
                coords=coords, dims=dims,
                name=varname, attrs=attrs.get(varname, {}))
        self.output['time'].attrs.update(attrs['time'])

    def _aggregate_state(self):
        """Aggregate data out of the state file and load it into `met_data`"""
        # Precipitation record

        assert self.state.dims['time'] == 90, self.state['time']
        record_dates = date_range(self.params['state_start'],
                                  self.params['state_stop'],
                                  calendar=self.params['calendar'])
        trailing = self.state['prec']
        trailing['time'] = record_dates
        total_precip = xr.concat([trailing, self.met_data['prec']],
                                 dim='time').load()
        total_precip = (cnst.DAYS_PER_YEAR * total_precip.rolling(
            time=90).mean().sel(time=slice(self.params['start'],
                                           self.params['stop'])))

        self.met_data['seasonal_prec'] = total_precip

        # Smoothed daily temperature range
        trailing = self.state['t_max'] - self.state['t_min']

        trailing['time'] = record_dates
        dtr = self.met_data['t_max'] - self.met_data['t_min']
        if (dtr < 0).any():
            raise ValueError("Daily maximum temperature lower"
                             " than daily minimum temperature!")
        sm_dtr = xr.concat([trailing, dtr], dim='time').load()
        sm_dtr = sm_dtr.rolling(time=30).mean().drop(record_dates, dim='time')
        self.met_data['dtr'] = dtr
        self.met_data['smoothed_dtr'] = sm_dtr

    def _validate_setup(self):
        """Updates the global parameters dictionary"""
        errs = [""]

        # Make sure there's some input
        if not len(self.params.get('forcing', [])):
            errs.append("Requires input forcings to be specified")

        # Make sure there is at least one forcing_var
        # They cannot all be constant since we use one as a template
        # for the others
        if not len(self.params.get('forcing_vars', [])):
            errs.append("Requires at least one non-constant forcing")

        # Parameters that can't be empty strings or None
        non_empty = ['out_dir', 'time_step', 'forcing_fmt']
        for each in non_empty:
            if self.params.get(each, None) is None or self.params[each] == '':
                errs.append("Cannot have empty value for {}".format(each))

        # Make sure time step divides evenly into a day
        if (cnst.MIN_PER_DAY % int(self.params.get('time_step', -1)) or
                (int(self.params['time_step']) > (6 * cnst.MIN_PER_HOUR) and
                 int(self.params['time_step']) != cnst.MIN_PER_DAY)):
            errs.append("Time step must be evenly divisible into 1440 "
                        "minutes (24 hours) and less than 360 minutes "
                        "(6 hours). Got {}.".format(self.params['time_step']))

        # Check for required input variable specification
        if self.met_data is not None:
            required_in = ['t_min', 't_max', 'prec']
            for each in required_in:
                if each not in self.met_data.variables:
                    errs.append("Input requires {}".format(each))

        # Make sure that we are going to write out some data
        if not len(self.params.get('out_vars', [])):
            errs.append("Output variable list must not be empty")

        # Check output variables are valid
        daily_out_vars = ['t_min', 't_max', 't_day', 'prec', 'vapor_pressure',
                          'shortwave', 'tskc', 'pet', 'wind', 'daylength']
        out_var_check = ['temp', 'prec', 'shortwave', 'vapor_pressure',
                         'air_pressure', 'rel_humid', 'spec_humid',
                         'longwave', 'tskc', 'wind']
        if int(self.params.get('time_step', -1)) == 1440:
            out_var_check = daily_out_vars
        for var in self.params.get('out_vars', []):
            if var not in out_var_check:
                errs.append('Cannot output variable {} at timestep {}'.format(
                    var, self.params['time_step']))

        # Check that the parameters specified are available
        opts = {'out_precision': ['f4', 'f8'],
                'lw_cloud': ['default', 'cloud_deardorff'],
                'lw_type': ['default', 'tva', 'anderson',
                            'brutsaert', 'satterlund',
                            'idso', 'prata']}
        for k, v in opts.items():
            if not self.params.get(k, None) in v:
                errs.append("Invalid option given for {}".format(k))

        # If any errors, raise and give a summary
        if len(errs) > 1:
            raise Exception("\n  ".join(errs))

    def get_nc_output_suffix(self, times):
        s, e = times[[0, -1]]
        template = '{:04d}{:02d}{:02d}-{:04d}{:02d}{:02d}'
        return template.format(s.year, s.month, s.day,
                               e.year, e.month, e.day,)

def wrap_run_cell(func: callable, params: dict,
                  ds: xr.Dataset, state: xr.Dataset, disagg: bool,
                  out_times: pd.DatetimeIndex):
    """
    Iterate over a chunk of the domain. This is wrapped
    so we can return a tuple of locs and df.

    Parameters
    ----------
    func: callable
        The function to call to do the work
    params: dict
        Parameters from a MetSim object
    ds: xr.Dataset
        Input forcings and domain
    state: xr.Dataset
        State variables at the point of interest
    disagg: bool
        Whether or not we should run a disagg routine
    out_times: pd.DatetimeIndex
        Times to return (should be trimmed 1 day at
        each end from the given index)

    Returns
    -------
    df_complete: pd.DataFrame
        A dataframe with the disaggregated data in it
    df_base: pd.DataFrame
        A dataframe with the state data in it
    """
    lat = ds['lat'].values.flatten()[0]
    lon = ds['lon'].values.flatten()[0]
    elev = ds['elev'].values.flatten()[0]
    params['elev'] = elev
    params['lat'] = lat
    params['lon'] = lon
    df = ds.to_dataframe()

    # solar_geom returns a tuple due to restrictions of numba
    # for clarity we convert it to a dictionary here
    sg = solar_geom(elev, lat, params['lapse_rate'])
    sg = {'tiny_rad_fract': sg[0], 'daylength': sg[1],
          'potrad': sg[2], 'tt_max0': sg[3]}
    yday = df.index.dayofyear - 1
    df['daylength'] = sg['daylength'][yday]
    df['potrad'] = sg['potrad'][yday]
    df['tt_max'] = sg['tt_max0'][yday]

    # Generate the daily values - these are saved
    # so that we can use a subset of them to write
    # out the state file later
    df_base = func(df, params)

    if disagg:
        # Get some values for padding the time list,
        # so that when interpolating in the disaggregation
        # functions we can match endpoints with adjoining
        # chunks - if no data is available, just repeat some
        # default values (this case is used at the very
        # beginning and end of the record)
        t_begin = [state['t_min'].values[-1],
                   state['t_max'].values[-1]]
        try:
            nextday = out_times[-1] + pd.Timedelta('1 days')
            t_end = [ds['t_min'].sel(time=nextday),
                     ds['t_max'].sel(time=nextday)]
        except (KeyError, ValueError):
            # None so that we don't extend the record
            t_end = None

        # Disaggregate to subdaily values
        df_complete = disaggregate(df, params, sg, t_begin, t_end)

        # Calculate the times that we want to get out by chopping
        # off the endpoints that were added on previously
        start = out_times.values[0]
        stop = (out_times.values[-1] + pd.Timedelta('1 days') -
                pd.Timedelta("{} minutes".format(params['time_step'])))

        if params['period_ending']:
            start += pd.Timedelta('{} minutes'.format(params['time_step']))
            stop += pd.Timedelta('{} minutes'.format(params['time_step']))

        new_times = date_range(
            start, stop, freq='{}T'.format(params['time_step']),
            calendar=params['calendar'])
    else:
        # convert srad to daily average flux from daytime flux
        df_base['shortwave'] *= df_base['daylength'] / cnst.SEC_PER_DAY
        # If we're outputting daily values, we dont' need to
        # change the output dates - see inside of `if` condition
        # above for more explanation
        start = out_times.values[0]
        stop = (out_times.values[-1] + pd.Timedelta('1 days') -
                pd.Timedelta("{} minutes".format(params['time_step'])))
        new_times = date_range(
            start, stop, freq='{}T'.format(params['time_step']),
            calendar=params['calendar'])
        df_base.index = new_times
        df_complete = df_base

    # Cut the returned data down to the correct time index
    df_complete = df_complete.loc[new_times[0]:new_times[-1]]
    return df_complete, df_base

@dask.delayed()
def wrap_run_slice(params, write_locks, domain_slice=NO_SLICE):
    params['is_worker'] = True
    ms = MetSim(params, domain_slice=domain_slice)
    ms.load_inputs()
    ms.run_slice()
    ms.write_chunk(locks=write_locks)

def chunk_domain(chunks, dims):
    '''
    Return a dictionary of chunk slices that can be used to decompose a grid
    '''
    def left(chunk, dim):
        return np.arange(0, dim, chunk)

    def right(chunk, dim):
        nums = np.arange(chunk, dim + chunk, chunk)
        nums[-1] = dim + 1
        return nums

    slices = [[slice(*a) for a in (zip(left(int(chunks[dim]), dims[dim]),
                                       right(int(chunks[dim]), dims[dim])))]
              for dim in chunks.keys()]

    return [dict(zip(chunks.keys(), p)) for p in itertools.product(*slices)]

class DummyLock(object):
    """DummyLock provides the lock API without any actual locking."""
    # This will be available in xarray in the next major version
    def acquire(self, *args):
        pass

    def release(self, *args):
        pass

    def __enter__(self):
        pass

    def __exit__(self, *args):
        pass

    @property
    def locked(self):
        return False

{'Client': 'distributed.Client', 'progress': 'distributed.progress'}.methods


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

import os, glob
import numpy as np
import pandas as pd
import torch
from torch.utils import data as utdata
from PIL import Image
from torchvision import transforms

rootPath = ''
splitPath = '/home/cheny/DataSet/miniImagenet/splits'
miniImagePath = 'C:/Users/Tream/Desktop/DataSets/Few-shot/miniImagenet/{}/'

resz = transforms.Resize(84)
centcrop = transforms.CenterCrop(84)
totensor = transforms.ToTensor()
norml = transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                 std=[0.229, 0.224, 0.225])

cps = transforms.Compose([
            resz,
            centcrop,
            totensor,
            norml
        ])

def load_image(path):
    img = Image.open(path)
    img = cps(img)
    return img

class miniImageNetDS(utdata.Dataset):
    def __init__(self, req_st='train', num_tasks=32, n_way=5, n_sq=(5, 15)):
        super(miniImageNetDS, self).__init__()
        self.req_st = req_st
        self.num_all_calss = len(glob.glob(miniImagePath.format(req_st)+'*'))  # require里面的类的个数
        self.images_path = glob.glob(miniImagePath.format(req_st)+'*')  # require类里每类的路径
        self.num_each_class = 600
        self.num_tasks = num_tasks
        self.n_way = n_way
        self.n_sq = sum(n_sq)

    def __len__(self):
        return self.num_all_calss

    def __getitem__(self, idx):
        idxclass_path = self.images_path[idx] + '/*'
        idxclass_all_img_path = glob.glob(idxclass_path)
        sq_idx = torch.randperm(self.num_each_class)[:self.n_sq]
        idxclass_img_path = [idxclass_all_img_path[i] for i in sq_idx]
        idxclass_img = torch.stack(list(map(load_image, idxclass_img_path)), 0)
        y = torch.tensor(idx).expand(self.n_sq)
        return idxclass_img, y

class tasksSampler(object):
    def __init__(self, num_all_class, n_tasks=32, n_way=5, n_sq=(5, 15)):
        self.num_all_class = num_all_class
        self.n_tasks = n_tasks  # n_episode
        self.n_way = n_way
        self.n_sq = sum(n_sq)  # num of support and query

    def __len__(self):
        return self.n_tasks

    def __iter__(self):
        for i in range(self.n_tasks):
            select_class = np.random.permutation(self.num_all_class)[:self.n_way]  # 随机选择n_way个类
            yield select_class
            # 返回n_way个类的索引

def loadDL(req_st='train', num_tasks=32, n_way=5, n_sq=(5, 15)):
    ds = miniImageNetDS(req_st, num_tasks, n_way, n_sq)
    num_all_class = ds.num_all_calss
    sampler = tasksSampler(num_all_class, num_tasks, n_way, n_sq)
    ld = utdata.DataLoader(ds, batch_sampler=sampler)
    return ld

def rangeto0_1withMinMax(imgts):
    min = imgts.view(3, -1).min(-1)[0]
    range_ = imgts.view(3, -1).max(-1)[0] - min
    imgts.sub_(min[:, None, None]).div_(range_[:, None, None])
    return imgts

if __name__ == '__main__':
    ld = loadDL(num_tasks=2, n_way=3, n_sq=(1, 2))
    print(len(ld))
    # batch0X, batch0y = next(iter(ld))
    # print(batch0X.shape, batch0y.shape)
    # from torchvision.utils import make_grid
    # from matplotlib import pyplot as plt
    #
    # # print(batch0[0].view(3, -1).max(-1)[0], batch0[0].view(3, -1).min(-1)[0])
    # # batch0[0] = rangeto0_1withMeanStd(batch0[0])
    # # print(batch0[0].view(3, -1).max(-1)[0], batch0[0].view(3, -1).min(-1)[0])
    # batch0X = batch0X.view(-1, 3, 84, 84)
    # print(batch0y)
    # for i in range(len(batch0X)):
    #     batch0X[i] = rangeto0_1withMinMax(batch0X[i])
    #
    # grids = make_grid(batch0X)
    # grids = grids.numpy().transpose(1,2,0)
    # plt.imshow(grids)
    # plt.axis('off')
    # plt.show()

    batch = list(iter(ld))
    batchX, batchY = list(zip(*batch))
    batchX, batchY = [torch.stack(i) for i in (batchX,batchY)]
    print(batchX.shape)
    print(batchY.shape)


"""
Advent of code 2020 day 6
Sample input:
abc

a
b
c

ab
ac

a
a
a
a

b

Sample output
PART I:
3 + 3 + 3 + 1 + 1 = 11

PART II:
3 + 0 + 1 + 1 + 1 = 6
"""
from collections import defaultdict
from typing import List

class CustomCustoms:
    part1_total_count = 0
    part2_total_count = 0

    def run(self, path):
        custom_forms = []
        count = 0
        with open(path, 'r') as reader:
            try:
                line = True
                while line:
                    line = reader.readline()
                    if line != "\n":
                        custom_forms.extend(list(filter(lambda x: (x != "\n"), list(line))))
                        count += 1
                    else:
                        self.update_group_details_part1(custom_forms)
                        self.update_group_details_part2(custom_forms, count)
                        custom_forms = []
                        count = 0

                print("part1 total : ", self.part1_total_count)
                print("part2 total : ", self.part2_total_count)
            except Exception as e:
                print(e)

    def update_group_details_part2(self, custom_forms: List, count: int):
        """ filter list to avoid '\n' and create a set and get count not efficient enough but clean"""
        temp_count = 0
        my_dict = defaultdict(list)
        for item in custom_forms:
            my_dict[item].append("yes")

        for item, val in my_dict.items():
            if len(val) == count:
                temp_count += 1

        self.part2_total_count += temp_count

    def update_group_details_part1(self, custom_forms: List):
        """ filter list to avoid '\n' and create a set and get count not efficient enough but clean"""
        my_set = len(set(custom_forms))
        self.part1_total_count += my_set

if __name__ == "__main__":
    obj = CustomCustoms()
    obj.run("input/day06_input.txt")


from typing import List, Optional

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def buildTree(
            self,
            inorder: List[int],
            postorder: List[int]
    ) -> Optional[TreeNode]:
        if len(postorder) == 0:
            return None
        if len(postorder) == 1:
            return TreeNode(val=postorder[0])

        root_val = postorder[-1]
        inorder_root_ind = inorder.index(root_val)

        return TreeNode(
            val=root_val,
            left=self.buildTree(
                inorder[0: inorder_root_ind],
                postorder[0: inorder_root_ind]
            ),
            right=self.buildTree(
                inorder[inorder_root_ind + 1:],
                postorder[inorder_root_ind: -1]
            )
        )


"""Sent state column

Revision ID: 1da0e1b89f2a
Revises: b9ffb7fde6f7
Create Date: 2023-07-06 22:08:36.541604

"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = '1da0e1b89f2a'
down_revision = 'b9ffb7fde6f7'

email_recipient = sa.table(
    'email_recipient',
    sa.column('rendered_text', sa.Unicode()),
    sa.column('is_sent', sa.Boolean()),
)

def upgrade() -> None:
    op.add_column(
        'email_recipient',
        sa.Column('is_sent', sa.Boolean(), server_default=sa.false(), nullable=True),
    )
    op.execute(
        email_recipient.update()
        .values(is_sent=True)
        .where(email_recipient.c.rendered_text.isnot(None))
    )
    op.alter_column('email_recipient', 'is_sent', nullable=False, server_default=None)

def downgrade() -> None:
    op.drop_column('email_recipient', 'is_sent')


# Approach 2: Depth First Search (DFS)

# Algorithm:
# DFS is very similar to BFS. Instead of using a queue and working iteratively, we'll use recursion. Our dfs
# method will be called for every reachable cell.
# Note: we could also work iteratively with DFS, in which case we would simply use a stack instead of a queue like
# in the Approach 1 code, with mostly everything else being identical to the BFS approach.

from typing import List

class Solution:
    def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
        # Check if input is empty
        if not heights or not heights[0]:
            return []

        # Initialize variables, including sets used to keep track of visited cells
        num_rows, num_cols = len(heights), len(heights[0])
        pacific_reachable = set()
        atlantic_reachable = set()

        def dfs(row, col, reachable):
            # This cell is reachable, so mark it
            reachable.add((row, col))
            for (x, y) in [(1, 0), (0, 1), (-1, 0), (0, -1)]: # Check all 4 directions
                new_row, new_col = row + x, col + y
                # Check if the new cell is within bounds
                if new_row < 0 or new_row >= num_rows or new_col < 0 or new_col >= num_cols:
                    continue
                # Check that the new cell hasn't already been visited
                if (new_row, new_col) in reachable:
                    continue
                # Check that the new cell has a higher or equal height,
                # So that water can flow from the new cell to the old cell
                if heights[new_row][new_col] < heights[row][col]:
                    continue
                # If we've gotten this far, that means the new cell is reachable
                dfs(new_row, new_col, reachable)

        # Loop through each cell adjacent to the oceans and start a DFS
        for i in range(num_rows):
            dfs(i, 0, pacific_reachable)
            dfs(i, num_cols - 1, atlantic_reachable)
        for i in range(num_cols):
            dfs(0, i, pacific_reachable)
            dfs(num_rows - 1, i, atlantic_reachable)

        # Find all cells that can reach both oceans, and convert to list
        return list(pacific_reachable.intersection(atlantic_reachable))

heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]

obj = Solution()
print(obj.pacificAtlantic(heights))

# Complexity Analysis:
# Time Complexity: O(M⋅N), where M is the number of rows and N is the number of columns.
# Similar to approach 1. The dfs function runs exactly once for each cell accessible from an ocean.
# Space Complexity: O(M⋅N), where M is the number of rows and N is the number of columns.
# Similar to approach 1. Space that was used by our queues is now occupied by dfs calls on the recursion stack.


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

# In[2]:

from scipy.special import airy
from scipy.optimize import root

# In[7]:

def Airy(x, index):
    """
    Airy function. Returns Ai, Ai', Bi, Bi'
    """
    ans = airy(x)
    return ans[index]

# In[18]:

z_sol = root(Airy, -1, args=1)
print(f'{z_sol.x[0]:.10f}')

# In[9]:

airy(-1.019)

# In[ ]:


""" Url config file for services app """
from django.urls import path
from .views import SingleServiceDetail, AllServicesView

APP_NAME = 'services'
urlpatterns = [
    path('', AllServicesView.as_view(), name='services'),
    path('services_detail/<int:pk>/', SingleServiceDetail.as_view(),
         name='services_detail'),
]


import torch

dropout = 0.7

class DQNModel(torch.nn.Module):
    def __init__(self, options={}):
        super().__init__()
        self.dropout1 = torch.nn.Dropout(dropout)
        self.dropout2 = torch.nn.Dropout(dropout)
        self.dropout3 = torch.nn.Dropout(dropout)
        self.linear1 = torch.nn.Linear(10241,4096)
        self.linear2 = torch.nn.Linear(4096,2048)
        self.linear3 = torch.nn.Linear(2048,1024)
        self.linear4 = torch.nn.Linear(1024,1)
        self.activation = torch.nn.ReLU()

    def forward(self, input):
        x = self.linear1(input)
        x = self.activation(x)
        x = self.dropout1(x)
        x = self.linear2(x)
        x = self.activation(x)
        x = self.dropout2(x)
        x = self.linear3(x)
        x = self.activation(x)
        x = self.dropout3(x)
        x = self.linear4(x)
        return x


from functools import cache
from typing import List

class Solution:
    def PredictTheWinner(self, nums: List[int]) -> bool:
        n = len(nums)
        pref = n * [0]
        pref[0] = nums[0]
        for i in range(1, n):
            pref[i] = pref[i-1] + nums[i]

        def get_sum(l, r):
            return pref[r] if l == 0 else pref[r] - pref[l-1]

        dp = [[-1 for x in range(n)] for j in range(n)]
        for i in range(n-1, -1, -1):
            for j in range(i, n):
                if i == j:
                    dp[i][j] = nums[i]
                    continue

                dp[i][j] = max(nums[i] + get_sum(i+1, j) - dp[i+1][j], nums[j] + get_sum(i, j-1) - dp[i][j-1])

        return 2 * dp[0][n-1] >= pref[-1]

print(Solution().PredictTheWinner(nums = [1,5,2]))
print(Solution().PredictTheWinner(nums = [1,5,233,7]))


import sys
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.axes as ax

up_file = sys.argv[1]
down_file = sys.argv[2]
onto_type = sys.argv[3]

ontology = pd.read_csv("go_slim_mapping.tab", sep='\t', header=None)
ontology = ontology[[0, 3, 4]]
ontology.columns = ['gene', 'o_type', 'onto']

# combine ontology and ge set and get mean log2fold change values
def get_onto_groups(gene_file):
    genes = pd.read_csv(gene_file, dtype=str, sep='\t')
    genes = genes.set_index('gene')
    gene_ont = pd.merge(genes, ontology, how='inner', on='gene')
    gene_ont = gene_ont.loc[gene_ont['o_type'] == onto_type]
    gene_ont["log2FoldChange"] = pd.to_numeric(gene_ont["log2FoldChange"])
    l2fc = gene_ont.groupby('onto')['log2FoldChange'].mean()

    # get gene ontology categories with highest representation
    gene_counts = gene_ont["onto"].value_counts()
    category_counts = ontology["onto"].value_counts()
    proportions = pd.merge(gene_counts, category_counts, how='inner', left_index=True, right_index=True)
    proportions['prop'] = proportions['onto_x']/proportions['onto_y']
    proportions = pd.merge(proportions, l2fc,how='inner', left_index=True, right_index=True)
    proportions = proportions.sort_values('prop', ascending=False)[:10]

    return proportions

# plotting code
up_proportions = get_onto_groups(up_file)
down_proportions = get_onto_groups(down_file)
proportions = pd.concat([up_proportions, down_proportions])

plt.figure(0, figsize=(10,11))
plt.grid(True)
plt.scatter(proportions.index.values, proportions['log2FoldChange'], s = proportions['prop']*5000)
plt.xticks(rotation=90)
vars = up_file.split(".")[0].split("/")[1].split("_")
if len(vars) == 3:
    plt.title("Differentially expressed genes with " + vars[1]  + ": " + vars[2], fontsize=20)
else:
    plt.title("Differentially expressed genes with " + vars[1], fontsize=20)
plt.xlabel("Gene ontology", fontsize=20)
plt.ylabel("Log2fc value", fontsize=20)
plt.tight_layout()
if len(vars) == 3:
    plt.savefig(vars[1] + "_" + vars[2] +  "_" + onto_type + ".png")
else:
    plt.savefig(vars[-1] + "_" + onto_type + ".png")


"""
    Модуль "db_func.py" содержит функции для работы с Базой данных.

    Состав модуля:

        1. Функция create_engine - создает "движок" для открытия рабочей сессии с базой данных.

        2. Функция create_session - открывает сессию работы с базой данных.

        3. Функция add_search_results - выполняет запись одной строки в таблицу БД "search_results"

        4. Функция get_next - выполняет SELECT запрос к таблице БД "search_results", получает одну запись,
           соответсвующую текущему значению параметра "result_id".

        5. Функция add_to_favorite_profiles - выполняет INSERT запрос к таблице БД 'favorite_profiles',
           "Избранные профили", и добавляет в неё одну запись.

        6. Функция add_to_blocked_profiles - выполняет INSERT запрос к таблице БД 'blocked_profiles',
           ("Игнорируемые профили") и добавляет в неё одну запись.

        7. Функция show_favorite_profiles - выполняет SELECT запрос к таблице БД 'favorite_profiles',
           получает из неё все данные, соответствующие параметрам запроса, и возвращает их для последующей отправки
           пользователю ВК.

        8. Функция clean_search_results - удаляет из таблицы БД "search_results" все результаты текущей сессии поиска
           для пользователя ВК соответствующему параметру user_id.

        9. Функция check_in_favorite_profiles - выполняет SELECT запрос к таблице БД 'favorite_profiles',
           "Избранные профили" и проверяет наличие в ней записи, соответствующей условиям запроса.

        10. Функция check_in_blocked_profiles - выполняет SELECT запрос к таблице БД 'blocked_profiles',
            "Заблокированные профили" и проверяет наличие в ней записи, соответствующей условиям запроса.

    Ознакомится сразу со всей документацией функций можно запустив этот модуль.

"""

import os

import sqlalchemy
from sqlalchemy.orm import sessionmaker
from dotenv import find_dotenv, load_dotenv

def create_engine():

    """
        Функция создает "движок" для открытия рабочей сессии с базой данных.

        Returns: engine
    """

    load_dotenv(find_dotenv())

    _DNS_ = f"postgresql://{os.getenv('db_username')}:{os.getenv('db_user_password')}" \
            f"@{os.getenv('db_hostname')}:{os.getenv('db_port')}/{os.getenv('db_name')}"

    engine = sqlalchemy.create_engine(_DNS_)

    return engine

def create_session():

    """
        Функция открывает сессию работы с базой данных.

        Returns: session
    """

    engine = create_engine()

    _Session_ = sessionmaker(bind=engine)

    session = _Session_()

    return session

def add_search_results(user_id: int, work_dict: dict, SearchResults):

    """
        Функция выполняет запись одной строки в таблицу БД "search_results" ("Результаты поиска").

        Args:
            user_id: str - id пользователя ВК, который общается с ботом.

            work_dict: dict - словарь, содержащий данные для записи в БД.

            SearchResults: ссылка на класс SearchResults, создающий модель таблицы "search_results",
                           импортируется из модуля db_models.py

    """

    session = create_session()

    session.add(SearchResults(
                user_id=user_id,
                result_id=work_dict['result_id'],
                vk_profile_href=work_dict["vk_profile_href"],
                first_name=work_dict["first_name"],
                last_name=work_dict["last_name"],
                photo_href_1=work_dict["photo_href_1"],
                photo_href_2=work_dict["photo_href_2"],
                photo_href_3=work_dict["photo_href_3"])
                )

    session.commit()

    session.close()

    return

def get_next(user_id: int, result_id: int, SearchResults) -> list[dict]:

    """
        Функция выполняет SELECT запрос к таблице БД "search_results", получает одну запись,
        соответсвующую текущему значению параметра "result_id".

        Args:
            user_id: int - id пользователя ВК, который общается с ботом.

            result_id: int - идентификатор, присваиваемый каждой записи в таблице БД "search_results".

            SearchResults: ссылка на класс SearchResults, создающий модель таблицы "search_results",
                           импортируется из модуля db_models.py

        Returns: list[dict]  - список из трех словарей.
                               Первые два элемента списка с индексами 0 и 1 используются для формирования и отправки
                               очередного результата поиска пользователю ВК бота.
                               Третий элемент списка с индексом 2 содержит все необходимые данные
                               в установленном формате, для записи текущего результата поиска
                               в таблицы 'favorite_profiles' и 'blocked_profiles', "Избранные" и "Игнорируемые"
                               пользователи соответственно.
    """

    session = create_session()

    for c in session.query(SearchResults).filter(SearchResults.user_id == f'{user_id}')\
            .filter(SearchResults.result_id == f'{result_id}'):

        result = [
            {'1': f'{c.first_name} {c.last_name} \n{c.vk_profile_href}'},
            {'2': f'{c.photo_href_1},{c.photo_href_2},{c.photo_href_3}'},
            {'user_id': f'{user_id}', "vk_profile_href": f'{c.vk_profile_href}', "first_name": f'{c.first_name}',
             "last_name": f'{c.last_name}', "photo_href_1": f'{c.photo_href_1}', "photo_href_2": f'{c.photo_href_2}',
             "photo_href_3": f'{c.photo_href_3}'}
        ]

        return result

def add_to_favorite_profiles(work_list: list[dict], FavoriteProfiles) -> str:

    """
        Функция выполняет INSERT запрос к таблице БД 'favorite_profiles', "Избранные профили",
        и добавляет в неё одну запись.

        Args:
            work_list: list[dict] - список словарей, элемент списка с индексом 2 содержит все необходимые для записи в БД данные.

            FavoriteProfiles: ссылка на класс FavoriteProfiles, создающий модель таблицы 'favorite_profiles',
                              импортируется из модуля db_models.py

        Returns: str - res_text переменную, содержащую текстовые данные, для ответа пользователю ВК.

    """

    session = create_session()

    session.add(FavoriteProfiles(
        user_id=work_list[2]["user_id"],
        vk_profile_href=work_list[2]["vk_profile_href"],
        first_name=work_list[2]["first_name"],
        last_name=work_list[2]["last_name"],
        photo_href_1=work_list[2]["photo_href_1"],
        photo_href_2=work_list[2]["photo_href_2"],
        photo_href_3=work_list[2]["photo_href_3"])
    )

    session.commit()

    session.close()

    res_text = 'Профиль добавлен в "Избранное"!'

    return res_text

def add_to_blocked_profiles(work_list: list[dict], BlockedProfiles) -> str:

    """
        Функция выполняет INSERT запрос к таблице БД 'blocked_profiles', "Игнорируемые профили"
        и добавляет в неё одну запись.

        Args:
            work_list: list[dict] - список словарей, элемент списка с индексом 2 содержит все необходимые для записи
            в БД данные.

            BlockedProfiles: ссылка на класс BlockedProfiles, создающий модель таблицы 'blocked_profiles',
                             импортируется из модуля db_models.py

        Returns: str - переменную res_text, содержащую текстовые данные, для ответа пользователю ВК.

    """

    session = create_session()

    session.add(BlockedProfiles(
        user_id=work_list[2]["user_id"],
        vk_profile_href=work_list[2]["vk_profile_href"])
    )

    session.commit()

    session.close()

    res_text = f'Пользователь {work_list[2]["first_name"]} {work_list[2]["last_name"]} добавлен в "Черный список"!'

    return res_text

def show_favorite_profiles(user_id: int, FavoriteProfiles) -> list[list[dict]]:

    """
        Функция выполняет SELECT запрос к таблице БД 'favorite_profiles', получает из неё все данные,
        соответствующие параметрам запроса, и возвращает их для последующей отправки пользователю ВК.

        Args:
            user_id: int - id пользователя ВК, который общается с ботом.

            FavoriteProfiles: ссылка на класс FavoriteProfiles, создающий модель таблицы 'favorite_profiles',
                              импортируется из модуля db_models.py

        Returns: list[list[dict] - список, элементами которого являются списки со вложенными словарями.
                 Данные используются для отправки ответа пользователю ВК.

    """

    session = create_session()

    sql_resp = session.query(FavoriteProfiles).filter(FavoriteProfiles.user_id == user_id)

    work_list = []

    for row in sql_resp:

        work_list.append([{'1': f'{row.first_name} {row.last_name} \n{row.vk_profile_href}'},
                         {'2': f'{row.photo_href_1},{row.photo_href_2},{row.photo_href_3}'}])

    return work_list

def cleans_search_results(user_id: int, SearchResults) -> str:

    """

        Функция удаляет из таблицы БД "search_results" все результаты текущей сессии поиска для пользователя ВК
        соответствующему параметру user_id.

        Args:
            user_id: int - id пользователя ВК, который общается с ботом.

            SearchResults: ссылка на класс SearchResults, создающий модель таблицы "search_results",
                           импортируется из модуля db_models.py

        Returns: str - переменную res_text, содержащую текстовые данные, для ответа пользователю ВК.

    """

    session = create_session()

    session.query(SearchResults).filter(SearchResults.user_id == user_id).delete()

    session.commit()

    session.close()

    res_text = "Поиск завершен."

    return res_text

def check_in_favorite_profiles(user_id: int, work_list: list[dict], FavoriteProfiles) -> bool:

    """

        Функция выполняет SELECT запрос к таблице БД 'favorite_profiles', "Избранные профили" и проверяет наличие в ней
        записи, соответствующей условиям запроса.

        Args:
            user_id: int - id пользователя ВК, который общается с ботом.

            work_list: list[dict] - список словарей, элемент списка с индексом 2 содержит все необходимые данные для проверки.

            FavoriteProfiles: ссылка на класс FavoriteProfiles, создающий модель таблицы 'favorite_profiles',
                              импортируется из модуля db_models.py

        Returns: Bool - True or False

    """

    session = create_session()

    sql_resp = session.query(FavoriteProfiles).filter(FavoriteProfiles.user_id == user_id)

    for result in sql_resp:

        if result.vk_profile_href == work_list[2]['vk_profile_href']:

            return True

    return False

def check_in_blocked_profiles(user_id: int, work_list: list[dict], BlockedProfiles) -> bool:

    """

        Функция выполняет SELECT запрос к таблице БД 'blocked_profiles', "Игнорируемые профили" и проверяет наличие
        в ней записи, соответствующей условиям запроса.

        Args:
            user_id: int - id пользователя ВК, который общается с ботом.

            work_list: list[dict] - список словарей, элемент списка с индексом 2 содержит все необходимые данные для проверки.

            BlockedProfiles: ссылка на класс BlockedProfiles, создающий модель таблицы 'blocked_profiles',
                             импортируется из модуля db_models.py

        Returns: Bool - True or False

    """

    session = create_session()

    sql_resp = session.query(BlockedProfiles).filter(BlockedProfiles.user_id == user_id)

    for result in sql_resp:

        if result.vk_profile_href == work_list[2]['vk_profile_href']:

            return True

    return False

if __name__ == "__main__":

    print('*' * 225)
    help(create_engine)
    print('*' * 225)
    help(create_session)
    print('*' * 225)
    help(add_search_results)
    print('*' * 225)
    help(get_next)
    print('*' * 225)
    help(add_to_favorite_profiles)
    print('*' * 225)
    help(add_to_blocked_profiles)
    print('*' * 225)
    help(show_favorite_profiles)
    print('*' * 225)
    help(cleans_search_results)
    print('*' * 225)
    help(check_in_favorite_profiles)
    print('*' * 225)
    help(check_in_blocked_profiles)
    print('*' * 225)


# -*- coding:utf-8 -*-
# Author: ssdcxy
# Date: 2020-02-21 09:14:00
# LastEditTime: 2020-02-21 09:48:14
# LastEditors: ssdcxy
# Description: 最长上升子序列
# FilePath: /arithmetic_oj/LeetCode/P0300.py

import json
from typing import List

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [0] * n
        res = 0
        for num in nums:
            i, j = 0, res
            while i < j:
                m = (i + j) // 2
                if dp[m] < num: i = m + 1
                else: j = m
            dp[i] = num
            if j == res: res += 1
        return res

def stringToIntegerList(input):
    return json.loads(input)

def main():
    import sys
    import io
    def readlines():
        for line in io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8'):
            yield line.strip('\n')

    lines = readlines()
    while True:
        try:
            line = next(lines)
            nums = stringToIntegerList(line);

            ret = Solution().lengthOfLIS(nums)

            out = str(ret);
            print(out)
        except StopIteration:
            break

if __name__ == '__main__':
    main()


import boto3
from decimal import Decimal
from stockapi.exceptions import StockNotFoundException
from stockapi.stock.share_api import ShareApi

class StockService(object):

    table_name = 'mystock-shares'

    def get_share(self, symbol):
        table = boto3.resource('dynamodb').Table(self.table_name)
        item = self.__get_item_from_table(table, symbol)
        return item

    def insert_share(self, symbol):
        dynamodb = boto3.client('dynamodb')
        dynamodb.put_item(
            TableName=self.table_name, Item={'symbol': {
                'S': symbol
            }})

    def update_share(self, symbol):
        table = boto3.resource('dynamodb').Table(self.table_name)
        item = self.__get_item_from_table(table, symbol)

        self.__update_item_by_name(symbol, item)

        table.put_item(Item=item)

    def get_all_shares(self):
        table = boto3.resource('dynamodb').Table(self.table_name)
        response = table.scan()
        return response['Items']

    def delete_share(self, symbol):
        table = boto3.resource('dynamodb').Table(self.table_name)
        table.delete_item(
            Key={
                'symbol': symbol
            }
        )

    def update_inderes(self, symbol, instruction, amount,
                       target_price, purchase_price, currency):
        table = boto3.resource('dynamodb').Table(self.table_name)
        item = self.__get_item_from_table(table, symbol)

        item['inderesInstruction'] = instruction
        item['amountOfStocks'] = amount
        if target_price == '-':
            item['inderesTargetPrice'] = '-'
        else:
            item['inderesTargetPrice'] = Decimal(str(target_price))
        item['purchasePrice'] = Decimal(str(purchase_price))
        item['currency'] = currency

        table.put_item(Item=item)

    def __get_item_from_table(self, table, symbol):
        response = table.get_item(
            Key={
                'symbol': symbol
            }
        )
        if 'Item' not in response:
            raise StockNotFoundException(
                'Cannot find a share with symbol %s.' % symbol)

        return response['Item']

    def __update_item_by_name(self, name, item):

        api = ShareApi(name)

        # Kurssi
        item['price'] = api.get_price()
        # Osinkotuotto
        item['dividendYield'] = api.get_divident_yield()
        # Tulos/Poma
        item['returnOnEquity'] = api.get_return_on_equity()
        # P/E
        item['priceToEarnings'] = api.get_price_to_earnings()
        # P/S
        item['priceToSales'] = api.get_price_to_sales()
        # P/B
        item['priceToBook'] = api.get_price_to_book()

        for k, v in item.iteritems():
            if type(v).__name__ == 'float':
                item[k] = Decimal(str(v))
            if type(v).__name__ == 'NoneType':
                item[k] = '-'


import json
import os

import numpy as np
from sklearn.metrics import confusion_matrix

from helper.utils import create_recursive_folder

# LIBLINEAR_PATH = os.path.join(os.path.dirname(__file__), "liblinear/python")
# print(LIBLINEAR_PATH)
# sys.path.append(os.path.abspath(LIBLINEAR_PATH))

class Minitagger(object):
    """
    Represents the Minitagger model and can be used to train a classifier and make predictions.
    Also it includes the active learning feature
    """

    def __init__(self):
        # feature extractor that is used (it is a SequenceDataFeatureExtractor object)
        self.feature_extractor = None
        # flag in order to print more/less log messages
        self.quiet = False
        # path to output directory for active learning
        self.active_output_path = ""
        # store predictions
        self.debug = False
        # path to output the predictions
        self.prediction_path = ""
        # path to output the predictions
        self.model_path = ""
        # path of the project
        self.project_dir = "."
        # language of the model
        self.language = ""
        # wikiner dataset
        self.wikiner = ""

        # Parameters:
        self.epsilon = 0.1

    def equip_feature_extractor(self, feature_extractor):
        """
        Equips the Minitagger with a feature extractor
        @type feature_extractor: SequenceDataFeatureExtractor
        @param feature_extractor: contains the feature extraction object
        """
        self.feature_extractor = feature_extractor

    def set_is_training(self, is_training):
        self.feature_extractor.is_training = is_training

    def set_prediction_path(self, model_name):
        create_recursive_folder([self.project_dir, "predictions", self.language, model_name])
        self.prediction_path = os.path.join(self.project_dir, "predictions", self.language, model_name)

    def set_model_path(self, model_path):
        create_recursive_folder([self.project_dir, "models_path", self.language, model_path])
        self.model_path = os.path.join(self.project_dir, "models_path", self.language, model_path)
        print("Model path:", self.model_path)

    @staticmethod
    def display_results(title, dict):
        print("==================", title, "==================")
        print("  F1-Score: ", dict["f1score"])
        print("  Precision: ", dict["precision"])
        print("  Recall: ", dict["recall"])
        print()

    @staticmethod
    def display_mean_result(title, fscore, precision, recall):
        print("==================", title, "==================")
        print("  F1-Score: ", fscore)
        print("  F1-score Mean: {:.3f}".format(np.mean(fscore)))
        print("  F1-score Standard deviation: {:.3f}".format(np.std(fscore)))
        print("  ---")
        print("  Precision: ", precision)
        print("  Precision Mean: {:.3f}".format(np.mean(precision)))
        print("  Precision Standard deviation: {:.3f}".format(np.std(precision)))
        print("  ---")
        print("  Recall: ", recall)
        print("  Recall Mean: {:.3f}".format(np.mean(recall)))
        print("  Recall Standard deviation: {:.3f}".format(np.std(recall)))
        print()

    def save_results(self, conll, exact, inexact, id=""):
        with open(os.path.join(self.model_path, "results_" + str(id) + ".json"), "w") as file:
            result = dict()
            result["ConllEval"] = conll
            result["Exact"] = exact
            result["Inexact"] = inexact
            json.dump(result, file, indent=2, separators=(',', ': '))

    def __debug(self, data_test, pred_labels):
        """
		Creates log files useful for debugging and prints a confusion matrix

		@type data_test: SequenceData object
		@param data_test: contains the testing data set
		@type pred_labels: list
		@param pred_labels: contains the prediction labels as they result from the classifier
		"""

        true_labels = self.__save_prediction_to_file(data_test, pred_labels)
        print()
        # find number of each label in the test set
        max_count = 0
        labels_list = list(data_test.label_count.keys())
        for label in labels_list:  # ["B", "I", "O"]:
            count = (np.array(true_labels) == label).sum()
            print("Number of " + label + " in the test set:", count)
            # find most frequent class in the test data set
            if count > max_count:
                max_count = count
                l = label
        print()
        # print accuracy of a naive baseline mode
        baseline_accuracy = "{0:.3f}".format(float(max_count) / len(true_labels) * 100)
        print("A naive model could predict always \'" + l + "\' with an accuracy of " + baseline_accuracy + "%")
        print()
        # create confusion matrix
        cm = confusion_matrix(true_labels, pred_labels, labels_list)
        print(labels_list)
        for row in cm:
            print(row)
            # plot_confusion_matrix(cm, labels_list)

    def __save_prediction_to_file(self, data_test, pred_labels):
        # file to print all predictions
        file_name = os.path.join(self.prediction_path, "predictions.txt")
        f1 = open(file_name, "w", encoding='utf-8')
        # file to print only sentences that contain at least one wrong label after classification
        file_name = os.path.join(self.prediction_path, "predictions_wrong.txt")
        f2 = open(file_name, "w", encoding='utf-8')
        # file to print only sentences whose labels are predicted 100% correctly
        file_name = os.path.join(self.prediction_path, "predictions_correct.txt")
        f3 = open(file_name, "w", encoding='utf-8')
        # index for prediction label
        pred_idx = 0
        # list to store all true labels
        true_labels = []
        true_pos_tags = []
        # iterate through the test set
        # labels_pos: [[labels]. [pos tag]] => labels = labels_pos[0] / pos_tag = labels_pos[1]
        for words, *labels_pos in data_test.sequence_pairs:
            # prediction sequence for each sentence
            pred_sequence = []
            for i in range(len(words)):
                # append label to the prediction sequence
                pred_sequence = pred_labels[pred_idx]
                # append label to the list of true labels
                true_labels.append(labels_pos[0][i])
                # append pos tag (if exist) to the list of true pos tag
                if len(labels_pos) == 2: true_pos_tags.append(labels_pos[1][i])
                # create line to print in the file
                line = words[i] + " " + labels_pos[0][i] + " " + pred_sequence[i] + "\n"
                # write to file
                f1.write(line)

            pred_idx += 1
            # separate sentences with empty lines
            f1.write("\n")
            # check if classification error occurred
            if labels_pos[0] != pred_sequence:
                for i in range(len(labels_pos[0])):
                    # create line to print to file
                    line = words[i] + " " + labels_pos[0][i] + " " + pred_sequence[i] + "\n"
                    f2.write(line)
                # separate sentences with empty lines
                f2.write("\n")
            else:
                for i in range(len(labels_pos[0])):
                    # create line to print to file
                    line = words[i] + " " + labels_pos[0][i] + " " + pred_sequence[i] + "\n"
                    f3.write(line)
                # separate sentences with empty lines
                f3.write("\n")
        # close files
        f1.close()
        f2.close()
        f3.close()
        return true_labels


from __future__ import absolute_import

from traits.api import HasTraits, Str, Any, Property, cached_property
from traitsui.api import UItem, Item, HGroup, VGroup, EnumEditor, TabularEditor
from traitsui.tabular_adapter import TabularAdapter

from pychron.core.helpers.traitsui_shortcuts import okcancel_view
from pychron.core.ui.combobox_editor import ComboboxEditor

class SessionAdapter(TabularAdapter):
    columns = [("Worker", "worker_name"), ("Session", "name"), ("Date", "start_date")]

class SampleLocator(HasTraits):
    principal_investigator = Str
    principal_investigators = Property

    project = Str
    projects = Property(depends_on="principal_investigator")

    sample = Str
    samples = Property(depends_on="project")

    session = Any
    sessions = Property(depends_on="sample")

    @cached_property
    def _get_principal_investigators(self):
        return self.dvc.get_principal_investigator_names()

    @cached_property
    def _get_sessions(self):
        if self.sample:
            return self.dvc.get_sample_prep_sessions(self.sample)
        else:
            return []

    @cached_property
    def _get_projects(self):
        if self.principal_investigator:
            return [
                p.name for p in self.dvc.get_projects((self.principal_investigator,))
            ]
        else:
            return []

    @cached_property
    def _get_samples(self):
        if self.project:
            return [si.name for si in self.dvc.get_samples(projects=self.project)]
        else:
            return []

    def traits_view(self):
        agrp = HGroup(
            Item(
                "principal_investigator",
                label="PI",
                editor=EnumEditor(name="principal_investigators"),
            ),
            Item("project", label="Project", editor=ComboboxEditor(name="projects")),
            Item("sample", label="Sample", editor=ComboboxEditor(name="samples")),
        )
        bgrp = VGroup(
            UItem(
                "sessions",
                editor=TabularEditor(adapter=SessionAdapter(), selected="session"),
            )
        )
        v = okcancel_view(VGroup(agrp, bgrp), title="Locate Sample")
        return v

# ============= EOF =============================================


from django.db import models

from django.contrib.auth.models import User

from math import floor

class Image(models.Model):
    name = models.CharField(max_length=200)
    path = models.CharField(max_length=200)
    height = models.IntegerField()
    width = models.IntegerField()

    def __str__(self):
        return self.name

class Background(models.Model):
    gm = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=20)
    image = models.ForeignKey(Image, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

class Token(models.Model):
    user = models.ForeignKey(User, related_name='gm', on_delete=models.CASCADE, null=True)
    title = models.CharField(max_length=20, null=True)
    image = models.ForeignKey(Image, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

class Game(models.Model):
    title = models.CharField(max_length=20)
    gm = models.ForeignKey(User, related_name='game', on_delete=models.CASCADE)

    def __str__(self):
        return self.title

class Trainer(models.Model):
    name = models.CharField(max_length=20)
    game = models.ForeignKey(Game, related_name='trainer', on_delete=models.CASCADE)
    user = models.ForeignKey(User, related_name='trainer', on_delete=models.CASCADE)
    token = models.ForeignKey(Token, related_name='trainer', on_delete=models.CASCADE, null=True, blank=True)
    level = models.IntegerField(default=1)
    money = models.IntegerField(default=5000)
    acrobatics = models.IntegerField(default=2)
    athletics = models.IntegerField(default=2)
    combat = models.IntegerField(default=2)
    intimidate = models.IntegerField(default=2)
    stealth = models.IntegerField(default=2)
    survival = models.IntegerField(default=2)
    gen_education = models.IntegerField(default=2)
    med_education = models.IntegerField(default=2)
    occ_education = models.IntegerField(default=2)
    pok_education = models.IntegerField(default=2)
    tec_education = models.IntegerField(default=2)
    guile = models.IntegerField(default=2)
    perception = models.IntegerField(default=2)
    charm = models.IntegerField(default=2)
    command = models.IntegerField(default=2)
    focus = models.IntegerField(default=2)
    intuition = models.IntegerField(default=2)
    constitution = models.IntegerField(default=10)
    attack = models.IntegerField(default=5)
    defense = models.IntegerField(default=5)
    special_attack = models.IntegerField(default=5)
    special_defense = models.IntegerField(default=5)
    speed = models.IntegerField(default=5)
    attack_cs = models.IntegerField(default=0)
    defense_cs = models.IntegerField(default=0)
    special_attack_cs = models.IntegerField(default=0)
    special_defense_cs = models.IntegerField(default=0)
    speed_cs = models.IntegerField(default=0)
    current_hp = models.IntegerField(default=0)
    current_ap = models.IntegerField(default=5)

    def __str__(self):
        return self.name

    def total_stat(self, stat, cs):
        if stat > 0:
            modifier = (cs + 2) / 2
        elif stat < 0:
            modifier = 2 / (2 - cs)
        else:
            modifier = 1
        return stat * modifier

    @property
    def total_attack(self):
        return self.total_stat(self.attack, self.attack_cs)

    @property
    def total_defense(self):
        return self.total_stat(self.defense, self.defense_cs)

    @property
    def total_special_attack(self):
        return self.total_stat(self.special_attack, self.special_attack_cs)

    @property
    def total_special_defense(self):
        return self.total_stat(self.special_defense, self.special_defense_cs)

    @property
    def total_speed(self):
        return self.total_stat(self.speed, self.speed_cs)

class Note(models.Model):
    note = models.TextField(max_length=400)
    trainer = models.ForeignKey(Trainer, related_name='note', on_delete=models.CASCADE)

    def __str__(self):
        return f'{str(self.trainer)} Note: {self.id}'

class Item(models.Model):
    name = models.CharField(max_length=50)
    price = models.IntegerField(default=0)
    roll_text = models.CharField(max_length=50, null=True, blank=True)

    def __str__(self):
        return str(self.name)

class TrainerItem(models.Model):
    trainer = models.ForeignKey(Trainer, related_name='item', on_delete=models.CASCADE)
    item = models.ForeignKey(Item, on_delete=models.CASCADE, null=True, blank=True)
    item_name = models.CharField(max_length=50, null=True, blank=True)
    number = models.IntegerField(default=0)

    def __str__(self):
        if self.item is None:
            return f'{str(self.trainer)} {str(self.item_name)}'
        else:
            return f'{str(self.trainer)} {str(self.item.name)}'

class Type(models.Model):
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name

class TypeEffectiveness(models.Model):
    attack = models.ForeignKey(Type, on_delete=models.CASCADE, null=True, blank=True, related_name='attack_type')
    defend = models.ForeignKey(Type, on_delete=models.CASCADE, null=True, blank=True, related_name='defend_type')
    value = models.FloatField(default=1)

    def __str__(self):
        return f'{self.attack} > {self.defend}'

class Species(models.Model):
    name = models.CharField(max_length=20)
    dex_num = models.IntegerField(unique=True)
    type_1 = models.ForeignKey(Type, on_delete=models.CASCADE, null=True, blank=True, related_name='type_1')
    type_2 = models.ForeignKey(Type, on_delete=models.CASCADE, null=True, blank=True, related_name='type_2')
    base_constitution = models.IntegerField()
    base_attack = models.IntegerField()
    base_defense = models.IntegerField()
    base_special_attack = models.IntegerField()
    base_special_defense = models.IntegerField()
    base_speed = models.IntegerField()
    acrobatics = models.CharField(max_length=10, blank=True)
    athletics = models.CharField(max_length=10, blank=True)
    combat = models.CharField(max_length=10, blank=True)
    intimidate = models.CharField(max_length=10, blank=True)
    stealth = models.CharField(max_length=10, blank=True)
    survival = models.CharField(max_length=10, blank=True)
    gen_education = models.CharField(max_length=10, blank=True)
    med_education = models.CharField(max_length=10, blank=True)
    occ_education = models.CharField(max_length=10, blank=True)
    pok_education = models.CharField(max_length=10, blank=True)
    tec_education = models.CharField(max_length=10, blank=True)
    guile = models.CharField(max_length=10, blank=True)
    perception = models.CharField(max_length=10, blank=True)
    charm = models.CharField(max_length=10, blank=True)
    command = models.CharField(max_length=10, blank=True)
    focus = models.CharField(max_length=10, blank=True)
    intuition = models.CharField(max_length=10, blank=True)
    overland = models.CharField(max_length=10, blank=True)
    swimming = models.CharField(max_length=10, blank=True)
    burrow = models.CharField(max_length=10, blank=True)
    sky = models.CharField(max_length=10, blank=True)
    levitate = models.CharField(max_length=10, blank=True)
    teleport = models.CharField(max_length=10, blank=True)
    tm_moves = models.TextField(blank=True, null=True)
    egg_moves = models.TextField(blank=True, null=True)
    tutor_moves = models.TextField(blank=True, null=True)
    basic_abilities = models.CharField(max_length=200, blank=True, null=True)
    adv_abilities = models.CharField(max_length=200, blank=True, null=True)
    high_ability = models.CharField(max_length=100, blank=True, null=True)
    size = models.CharField(max_length=50)
    weight = models.IntegerField()
    egg_groups = models.CharField(max_length=200, blank=True, null=True)
    capture_rate = models.IntegerField(default=255)

    def __str__(self):
        return self.name

class Evolution(models.Model):
    base = models.ForeignKey(Species, related_name="base", on_delete=models.CASCADE)
    evolved = models.ForeignKey(Species, related_name="evolved", on_delete=models.CASCADE)
    requirements = models.CharField(max_length=200)

    def __str__(self):
        return self.evolved.name

class DamageBase(models.Model):
    num_die = models.IntegerField(default=1)
    die_num = models.IntegerField(default=1)
    add = models.IntegerField(default=0)

    def __str__(self):
        return f'Damage Base {self.id}'

class Attack(models.Model):
    name = models.CharField(max_length=20)
    type = models.ForeignKey(Type, on_delete=models.CASCADE, null=True, blank=True, related_name='attack')
    frequency = models.CharField(max_length=20)
    ac = models.IntegerField()
    damage_base = models.ForeignKey(DamageBase, on_delete=models.CASCADE, null=True, blank=True)
    attack_class = models.CharField(max_length=20)
    range = models.CharField(max_length=60)
    effect = models.TextField(max_length=400)
    game = models.ForeignKey(Game, on_delete=models.CASCADE, null=True, blank=True)

    def __str__(self):
        return self.name

class SpeciesAttack(models.Model):
    species = models.ForeignKey(Species, related_name='species_attack', on_delete=models.CASCADE)
    attack = models.ForeignKey(Attack, on_delete=models.CASCADE)
    level = models.IntegerField()

    def __str__(self):
        return f'{str(self.species)} {str(self.attack)}'

class Nature(models.Model):
    name = models.CharField(max_length=20)
    constitution = models.IntegerField()
    attack = models.IntegerField()
    defense = models.IntegerField()
    special_attack = models.IntegerField()
    special_defense = models.IntegerField()
    speed = models.IntegerField()

    def __str__(self):
        return self.name

class Pokemon(models.Model):
    name = models.CharField(max_length=20)
    trainer = models.ForeignKey(Trainer, null=True, blank=True, related_name='pokemon', on_delete=models.CASCADE)
    game = models.ForeignKey(Game, null=True, related_name='pokemon', on_delete=models.CASCADE)
    species = models.ForeignKey(Species, on_delete=models.CASCADE)
    nature = models.ForeignKey(Nature, on_delete=models.CASCADE)
    level = models.IntegerField(default=1)
    experience = models.IntegerField(default=0)
    in_party = models.BooleanField(default=False)
    constitution = models.IntegerField()
    attack = models.IntegerField()
    defense = models.IntegerField()
    special_attack = models.IntegerField()
    special_defense = models.IntegerField()
    speed = models.IntegerField()
    attack_cs = models.IntegerField(default=0)
    defense_cs = models.IntegerField(default=0)
    special_attack_cs = models.IntegerField(default=0)
    special_defense_cs = models.IntegerField(default=0)
    speed_cs = models.IntegerField(default=0)
    current_hp = models.IntegerField(default=0)
    ability = models.CharField(max_length=40, null=True, blank=True)
    token = models.ForeignKey(Token, on_delete=models.CASCADE, null=True, blank=True)
    burned = models.BooleanField(default=False)
    frozen = models.BooleanField(default=False)
    paralyzed = models.BooleanField(default=False)
    poisoned = models.BooleanField(default=False)
    asleep = models.BooleanField(default=False)
    confused = models.BooleanField(default=False)
    cursed = models.BooleanField(default=False)
    disabled = models.BooleanField(default=False)
    enraged = models.BooleanField(default=False)
    flinched = models.BooleanField(default=False)
    infatuated = models.BooleanField(default=False)
    suppressed = models.BooleanField(default=False)
    blind = models.BooleanField(default=False)
    slowed = models.BooleanField(default=False)
    stuck = models.BooleanField(default=False)
    trapped = models.BooleanField(default=False)
    tripped = models.BooleanField(default=False)
    vulnerable = models.BooleanField(default=False)

    def __str__(self):
        return self.name

    def get_cs_mult(self, cs):
        if cs <= -6:
            return 0.25
        elif cs == -5:
            return 2.0/7.0
        elif cs == -4:
            return 2.0/6.0
        elif cs == -3:
            return 0.4
        elif cs == -2:
            return .5
        elif cs == -1:
            return 2.0/3.0
        elif cs == 0:
            return 1
        elif cs == 1:
            return 1.5
        elif cs == 2:
            return 2
        elif cs == 3:
            return 2.5
        elif cs == 4:
            return 3
        elif cs == 5:
            return 3.5
        elif cs >= 6:
            return 4

    @property
    def total_constitution(self):
        return self.constitution + self.species.base_constitution + self.nature.constitution

    @property
    def total_attack(self):
        cs_mult = self.get_cs_mult(self.attack_cs)
        return (self.attack + self.species.base_attack + self.nature.attack) * cs_mult

    @property
    def total_defense(self):
        cs_mult = self.get_cs_mult(self.defense_cs)
        return (self.defense + self.species.base_defense + self.nature.defense) * cs_mult

    @property
    def total_special_attack(self):
        cs_mult = self.get_cs_mult(self.special_attack_cs)
        return (self.special_attack + self.species.base_special_attack + self.nature.special_attack) * cs_mult

    @property
    def total_special_defense(self):
        cs_mult = self.get_cs_mult(self.special_defense_cs)
        return (self.special_defense + self.species.base_special_defense + self.nature.special_defense) * cs_mult

    @property
    def total_speed(self):
        cs_mult = self.get_cs_mult(self.speed_cs)
        return (self.speed + self.species.base_speed + self.nature.speed) * cs_mult

    def exp_to_level(self, level):
        factor = floor(level / 10)
        remainder = level % 10
        experience = 0
        for i in range(factor):
            experience += 100 * (2 ** i)
        for i in range(remainder):
            experience += 10 * (2 ** factor)
        return experience

class PokemonAttack(models.Model):
    pokemon = models.ForeignKey(Pokemon, related_name='pokemon_attack', on_delete=models.CASCADE)
    attack = models.ForeignKey(Attack, on_delete=models.CASCADE)

    def __str__(self):
        return f'{str(self.pokemon)} {str(self.attack)}'

class TrainerAttack(models.Model):
    trainer = models.ForeignKey(Trainer, related_name='trainer_attack', on_delete=models.CASCADE)
    attack = models.ForeignKey(Attack, on_delete=models.CASCADE)

    def __str__(self):
        return f'{str(self.trainer)} {str(self.attack)}'

class Edge(models.Model):
    name = models.TextField(max_length=200)
    notes = models.CharField(max_length=100, null=True)
    prerequisites = models.CharField(max_length=100, null=True)
    effect = models.CharField(max_length=400)

    def __str__(self):
        if self.notes is not None:
            return f'{self.name} {self.notes}'
        else:
            return self.name

class EdgeAttack(models.Model):
    edge = models.ForeignKey(Edge, on_delete=models.CASCADE)
    attack = models.ForeignKey(Attack, on_delete=models.CASCADE)

    def __str__(self):
        return f'{str(self.edge)} {str(self.attack)}'

class Feature(models.Model):
    name = models.TextField(max_length=100)
    notes = models.CharField(max_length=100, null=True, blank=True)
    tags = models.CharField(max_length=100, null=True, blank=True)
    prerequisites = models.CharField(max_length=100, null=True, blank=True)
    frequency = models.CharField(max_length=100, null=True, blank=True)
    trigger = models.TextField(max_length=200, null=True, blank=True)
    effect = models.TextField(max_length=1000)
    game = models.ForeignKey(Game, on_delete=models.CASCADE, null=True, blank=True)

    def __str__(self):
        if self.notes is not None:
            return f'{self.name} {self.notes}'
        else:
            return self.name

class FeatureAttack(models.Model):
    feature = models.ForeignKey(Feature, on_delete=models.CASCADE)
    attack = models.ForeignKey(Attack, on_delete=models.CASCADE)

    def __str__(self):
        return f'{str(self.edge)} {str(self.attack)}'

class TrainerEdge(models.Model):
    trainer = models.ForeignKey(Trainer, related_name='trainer_edge', on_delete=models.CASCADE)
    edge = models.ForeignKey(Edge, on_delete=models.CASCADE)

    def __str__(self):
        return f'{str(self.trainer)} {str(self.edge)}'

class TrainerFeature(models.Model):
    trainer = models.ForeignKey(Trainer, related_name='trainer_feature', on_delete=models.CASCADE)
    feature = models.ForeignKey(Feature, on_delete=models.CASCADE)

    def __str__(self):
        return f'{str(self.trainer)} {str(self.feature)}'

class Message(models.Model):
    game = models.ForeignKey(Game, on_delete=models.CASCADE)
    message = models.CharField(max_length=200)
    display_name = models.CharField(max_length=50)

    def __str__(self):
        return self.message


#Libraries
import cv2
import numpy as np
import math

#Universal constant
DEGRETORAD = 0.0174533

#CV2 SETUP
img = np.zeros((400,800,3), np.uint8)
cv2.namedWindow('image')

#floor
cv2.rectangle(img,(0,380),(800,500),(100,255,100),1)

#Initial constants
initialPosition = (39, 380)
gravity = 9.8 * 0.1
initalSpeed = 300 * 0.1
angles = [20, 30, 40, 45, 50, 60, 70, 90]
anglesId = 0

#Problem variables
counter = 0
canonPosition = [initialPosition[0], initialPosition[1]]
velocity = [0.0,0.0]
angle = 20

while(1):
    #Angle set
    canonAngle = (angle + 90)  * DEGRETORAD

    #Initial image clone (avoid trail)
    _img = np.array(img, copy=True)

    counter += 0.5

    #apply gravity
    velocity[1] += gravity

    #Apply speed
    canonPosition[0] += int(velocity[0])
    canonPosition[1] += int(velocity[1])

    #Collision with floor
    if canonPosition[1] >= 380:
        canonPosition[1] = 380
        velocity[0] = 0
        velocity[1] = 0

    for i in range(0, 100):
        x = initalSpeed * math.sin(canonAngle) * i + initialPosition[0]
        y = initalSpeed * math.cos(canonAngle) * i + 0.5 * gravity * (i ** 2) + initialPosition[1]

        cv2.circle(_img,(int(x), int(y)),1,(100,100,100), 5)

        if y > 380:
            break

    #
    _canonPosition = tuple(canonPosition)
    _canonPositionToDraw = (canonPosition[0] - initialPosition[0],
        (canonPosition[1] - initialPosition[1]) * -1)

    #Draw the canon ball
    cv2.circle(_img,_canonPosition,5,(0,0,255), 5)

    #LABELS
    text = "Angle: " + str(angle)
    cv2.putText(_img, text, (10,25),
        cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255))
    text = "Velocity: " + str(velocity)
    cv2.putText(_img, text, (10,50),
        cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255))
    text = "Position: " + str(_canonPositionToDraw)
    cv2.putText(_img, text, (10,75),
        cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255))
    text = "KeyArrows to change the angle"
    cv2.putText(_img, text, (10,90),
        cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255))
    text = "Q to reset"
    cv2.putText(_img, text, (10,115),
        cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255))
    text = "Spacebar to shoot"
    cv2.putText(_img, text, (10,140),
        cv2.FONT_HERSHEY_SIMPLEX, .5, (255, 255, 255))

    cv2.imshow('image',_img)

    #Key pressed
    key = cv2.waitKey(20)

    #Print it on console
    if key != -1:
        print(key)

    #Arrow
    if key == 0:
        anglesId += 1
        if anglesId >= len(angles):
            anglesId = 0
        angle = angles[anglesId]

    #Key A
    if key == 113:
        canonPosition = [initialPosition[0], initialPosition[1]]

    #Spacebar
    if key == 32:
        vectorPositionX = math.sin(canonAngle) * initalSpeed
        vectorPositionY = math.cos(canonAngle) * initalSpeed

        velocity = [vectorPositionX, vectorPositionY]
        #canonPosition[1] = 380

    #Escape
    if key == 27:
        break

cv2.destroyAllWindows()


# BOJ_2178_미로 탐색
# 난이도: 실버1
# 풀이 날짜: 23.10.8
# 풀이 시간: 1시간 30분

# How to
'''
    0,0 좌표부터 시작하여 너비우선탐색을 진행하였다.
    현재 위치에서 갈 수 있는 사방면을 살펴보고, 지금까지 걸린 횟수를 더한다.
    최종적으로 M,N의 좌표에는 해당 위치까지 도달하는데 걸린 횟수가 저장되게된다.
'''

# Comment
'''
    좀 많이 헤멨다.
    헤메다가 상하좌우를 살피는 부분은 책을 참고하여 작성하였다.

    제일 오래걸렸던 이유는, matrix 라는 2차원 리스트에 저장할 때,
    (x,y)좌표가 matrix[y][x]에 저장되어있다는 것이였다...

    현재는 삭제했지만, 일일히 매 반복 시의 matrix를 프린트하고 new_x와 new_y를 모니터링하며 오류를 발견하였다.

'''

# Code
import sys
input = sys.stdin.readline
from collections import deque

N, M = map(int, input().split())

matrix = [[] for _ in range(N)]
visited = [ [False] * M for _ in range(N)]

for i in range(N):
    line = input().strip()
    for ch in line:
        matrix[i].append(int(ch))

queue = deque()

queue.append((0,0))
visited[0][0] = True

while queue:
    # up, down, left, right
    x_list = [0, 0, -1, 1]
    y_list = [-1, 1, 0, 0]
    x, y = queue.popleft()

    for i in range(4):
        new_x = x + x_list[i]
        new_y = y + y_list[i]

        if new_x < 0 or new_y < 0 or new_x >= M or new_y >= N:
            continue

        if not visited[new_y][new_x] and matrix[new_y][new_x] > 0:

            matrix[new_y][new_x] += matrix[y][x]
            visited[new_y][new_x] = True
            queue.append((new_x,new_y))

print(matrix[N-1][M-1])

## 메모리: 34140 KB, 시간: 84 ms