import numpy as np
from collections import defaultdict
import os.path
import fractions as frac

import operator
from ..utils import *

class PhaseError(Exception):
    pass

class PhaseDataError(Exception):
    pass

class PhaseData(object):
    """
    A PhaseData object is a container for storing and organizing phase data.
    Most importantly used when doing a large number of thermodynamic analyses
    and it is undesirable to access the database for every space you want to
    consider.
    """
    def __init__(self):
        self.clear()

    def __str__(self):
        return '%d Phases' % len(self.phases)

    @property
    def phases(self):
        """
        List of all phases.
        """
        return self._phases

    @phases.setter
    def phases(self, phases):
        self.clear()
        for phase in phases:
            self.add_phase(phase)

    def clear(self):
        self._phases = []
        self.phases_by_elt = defaultdict(set)
        self.phases_by_dim = defaultdict(set)
        self.phase_dict = {}
        self.space = set()

    def add_phase(self, phase):
        """
        Add a phase to the PhaseData collection. Updates the
        PhaseData.phase_dict and PhaseData.phases_by_elt dictionaries
        appropriately to enable quick access.
        Examples::
            >>> pd = PhaseData()
            >>> pd.add_phase(Phase(composition='Fe2O3', energy=-3))
            >>> pd.add_phase(Phase(composition='Fe2O3', energy=-4))
            >>> pd.add_phase(Phase(composition='Fe2O3', energy=-5))
            >>> pd.phase_dict
            {'Fe2O3': <Phase Fe2O3 : -5}
            >>> pd.phases_by_elt['Fe']
            [<Phase Fe2O3 : -3>, <Phase Fe2O3 : -4>, <Phase Fe2O3 : -5>]
        """

        if not phase.name in self.phase_dict:
            self.phase_dict[phase.name] = phase
        else:
            if phase.energy < self.phase_dict[phase.name].energy:
                self.phase_dict[phase.name] = phase
        self._phases.append(phase)
        phase.index = len(self._phases)

        for elt in phase.comp:
            self.phases_by_elt[elt].add(phase)
        self.phases_by_dim[len(phase.comp)].add(phase)

        self.space |= set(phase.comp.keys())

    def add_phases(self, phases):
        """
        Loops over a sequence of phases, and applies `add_phase` to each.
        Equivalent to::
            >>> pd = PhaseData()
            >>> for p in phases:
            >>>     pd.add_phase(p)
        """
        for phase in phases:
            self.add_phase(phase)

    def read_api_data(self, jsondata, per_atom=True):
        if jsondata.get('data', []) == []:
            print("No data found")
            return
        for d in jsondata['data']:
            if 'name' not in d or 'delta_e' not in d:
                continue
            phase = Phase(composition=d['name'],
                          energy=float(d['delta_e']),
                          per_atom=per_atom)
            self.add_phase(phase)

    def get_phase_data(self, space):
        if not space:
            return self

        phases = set(self.phases)
        others = set(self.phases_by_elt.keys()) - set(space)
        for elt in others:
            phases -= self.phases_by_elt[elt]
        pd = PhaseData()
        pd.phases = phases
        return pd

class Phase(object):
    """
    A Phase object is a point in composition-energy space.
    Examples::
        >>> p1 = Phase('Fe2O3', -1.64, per_atom=True)
        >>> p2 = Phase('Fe2O3', -8.2, per_atom=False)
        >>> p3 = Phase({'Fe':0.4, 'O':0.6}, -1.64)
        >>> p4 = Phase({'Fe':6, 'O':9}, -24.6, per_atom=False)
        >>> p1 == p2
        True
        >>> p2 == p3
        True
        >>> p3 == p4
        True
    """

    id = None
    use = True
    show_label = True
    custom_name = None
    phase_dict = {}
    def __init__(self,
            composition=None,
            energy=None,
            description='',
            per_atom=True,
            stability=None,
            total=False,
            name=''):

        if composition is None or energy is None:
            raise PhaseError("Composition and/or energy missing.")
        if isinstance(composition, str):
            composition = parse_comp(composition)

        self.description = description
        self.comp = defaultdict(float, composition)
        self.stability = stability
        if name:
            self.custom_name = name

        if not per_atom:
            self.total_energy = energy
        else:
            self.energy = energy

    @staticmethod
    def from_phases(phase_dict):
        """
        Generate a Phase object from a dictionary of Phase objects. Returns a
        composite phase of unit composition.
        """
        if len(phase_dict) == 1:
            return phase_dict.keys()[0]

        pkeys = sorted(phase_dict.keys(), key=lambda x: x.name)
        energy = sum([ amt*p.energy for p, amt in phase_dict.items() ])

        comp = defaultdict(float)
        for p, factor in phase_dict.items():
            for e, amt in p.unit_comp.items():
                comp[e] += amt*factor

        phase = Phase(
                composition=comp,
                energy=energy,
                per_atom=False)
        phase.phase_dict = phase_dict
        return phase

    @property
    def natoms(self):
        return sum(self.nom_comp.values())

    def __str__(self):
        if self.description:
            return '{name} ({description}): {energy:0.3g}'.format(
                    name=self.name, energy=self.energy, description=self.description)
        else:
            return '{name} : {energy:0.3g}'.format(
                    name=self.name, energy=self.energy)

    def __repr__(self):
        return '<Phase %s>' % self

    def __hash__(self):
        return hash((self.name, self.energy))

    def __eq__(self, other):
        """
        Phases are defined to be equal if they have the same composition and an
        energy within 1e-6 eV/atom.
        """
        if set(self.comp) != set(other.comp):
            return False
        if abs(self.energy - other.energy) > 1e-6:
           return False
        for key in self.comp:
           if abs(self.unit_comp[key]-other.unit_comp[key]) > 1e-6:
                return False
        return True

    @property
    def label(self):
        return '%s: %0.3f eV/atom' % (self.name, self.energy)

    @property
    def name(self):
        if self.custom_name:
            return self.custom_name
        if self.phase_dict:
            name_dict = dict((p, v/p.natoms) for p, v in
                    self.phase_dict.items())
            return ' + '.join('%.3g %s' % (v, p.name) for p, v in name_dict.items())
        return format_comp(self.nom_comp)

    @property
    def space(self):
        """
        Set of elements in the phase.
        """
        return set([ k for k, v in self.unit_comp.items()
            if abs(v) > 1e-6 ])

    @property
    def n(self):
        """
        Number of atoms in the total composition.
        """
        return sum(self._comp.values())

    @property
    def comp(self):
        """
        Total composition.
        """
        return self._comp

    @comp.setter
    def comp(self, composition):
        self._comp = composition
        self._unit_comp = unit_comp(composition)
        self._nom_comp = reduce_comp(composition)

    @property
    def unit_comp(self):
        """
        Unit composition.
        """
        return self._unit_comp

    @property
    def nom_comp(self):
        """
        Composition divided by the GCD. e.g. Fe4O6 becomes Fe2O3.
        """
        return self._nom_comp

    @property
    def energy(self):
        """
        Energy per atom in eV.
        """
        return self._energy

    @energy.setter
    def energy(self, energy):
        self._energy = energy
        self._total_energy = energy * sum(self.comp.values())
        self._energy_pfu = energy / sum(self.nom_comp.values())

    @property
    def total_energy(self):
        """
        Total energy for the composition as supplied (in eV).
        """
        return self._total_energy

    @total_energy.setter
    def total_energy(self, energy):
        self._total_energy = energy
        self._energy = energy/sum(self.comp.values())
        self._energy_pfu = energy / sum(self.nom_comp.values())

    @property
    def energy_pfu(self):
        """
        Energy per nominal composition. i.e. energy per Fe2O3, not Fe4O6.
        """
        return self._energy_pfu

    @energy_pfu.setter
    def energy_pfu(self, energy):
        self._energy_pfu = energy

    def amt(self, comp):
        """
        Returns a composition dictionary with the specified composition pulled
        out as 'var'.
        Examples::
            >>> phase = Phase(composition={'Fe':1, 'Li':5, 'O':8}, energy=-1)
            >>> phase.amt('Li2O')
            defaultdict(<type 'float'>, {'var': 2.5, 'Fe': 1, 'O': 5.5, 'Li': 0.0})
        """
        if isinstance(comp, Phase):
            comp = comp.comp
        elif isinstance(comp, str):
            comp = parse_comp(comp)
        residual = defaultdict(float, self.comp)
        tot = sum(residual.values())
        for c, amt in dict(comp).items():
            pres = residual[c]/amt
            for c2, amt2 in comp.items():
                residual[c2] -= pres*amt2
        residual['var'] = (tot - sum(residual.values()))
        residual['var'] /= float(sum(comp.values()))
        return residual

    def fraction(self, comp):
        """
        Returns a composition dictionary with the specified composition pulled
        out as 'var'.
        Examples::
            >>> phase = Phase(composition={'Fe':1, 'Li':5, 'O':8}, energy=-1)
            >>> phase.fraction('Li2O')
            defaultdict(<type 'float'>, {'var': 0.5357142857142858, 'Fe':
                0.07142857142857142, 'O': 0.3928571428571428, 'Li': 0.0})
        """
        if isinstance(comp, Phase):
            comp = comp.unit_comp
        elif isinstance(comp, str):
            comp = unit_comp(parse_comp(comp))
        residual = defaultdict(float, self.unit_comp)
        tot = sum(residual.values())
        for c, amt in dict(comp).items():
            pres = residual[c]/amt
            for c2, amt2 in comp.items():
                residual[c2] -= pres*amt2
        residual['var'] = (tot - sum(residual.values()))
        residual['var'] /= float(sum(comp.values()))
        return residual


import math
from typing import Sequence, Tuple

import torch
from torch import nn
from torch.nn import functional as F
from tensorfn.config import config_model
from pydantic import StrictInt, StrictFloat

from .layer import DropPath, tuple2, PositionwiseFeedForward

LayerNorm = lambda x: nn.LayerNorm(x, eps=1e-6)

def patchify(input, size):
    batch, height, width, dim = input.shape

    return (
        input.view(batch, height // size, size, width // size, size, dim)
        .permute(0, 1, 3, 2, 4, 5)
        .reshape(batch, height // size, width // size, -1)
    )

class PositionalEncodingGenerator(nn.Module):
    def __init__(self, dim):
        super().__init__()

        self.proj = nn.Conv2d(dim, dim, 3, padding=1, bias=False, groups=dim)

    def forward(self, input):
        out = input.permute(0, 3, 1, 2)
        out = self.proj(out) + out
        out = out.permute(0, 2, 3, 1)

        return out

class MultiHeadedAttention(nn.Module):
    def __init__(self, dim, n_head, reduction=1, dropout=0):
        super().__init__()

        self.dim_head = dim // n_head
        self.n_head = n_head

        self.linear_q = nn.Linear(dim, dim, bias=False)
        self.linear_kv = nn.Linear(dim, dim * 2, bias=False)

        self.linear = nn.Linear(dim, dim)
        self.dropout = dropout
        self.reduction = reduction

        if self.reduction > 1:
            self.reduce_conv = nn.Conv2d(
                dim, dim, self.reduction, stride=self.reduction
            )

    def forward(self, input):
        batch_size, height, width, _ = input.shape

        def reshape(input):
            return input.reshape(batch_size, -1, self.n_head, self.dim_head).transpose(
                1, 2
            )

        query = reshape(self.linear_q(input))

        if self.reduction > 1:
            dim = input.shape[-1]
            reduc = input.transpose(1, 2).reshape(batch_size, dim, height, width)
            reduc = self.reduce_conv(reduc).reshape(batch_size, dim, -1).transpose(1, 2)
            kv = reduc

        else:
            kv = input

        key, value = self.linear_kv(kv).chunk(2, dim=2)
        key = reshape(key).transpose(2, 3)
        value = reshape(value)

        score = query @ key / math.sqrt(self.dim_head)

        attn = F.softmax(score, 3)
        attn = F.dropout(attn, self.dropout, training=self.training)

        out = attn @ value

        out = out.transpose(1, 2).reshape(
            batch_size, height, width, self.dim_head * self.n_head
        )
        out = self.linear(out)

        return out

class MultiHeadedLocalAttention(nn.Module):
    def __init__(self, dim, n_head, dim_head, window_size, dropout=0):
        super().__init__()

        self.dim_head = dim_head
        self.n_head = n_head

        self.weight = nn.Linear(dim, n_head * dim_head * 3, bias=True)
        self.linear = nn.Linear(n_head * dim_head, dim)

        self.window_size = window_size
        self.dropout = dropout

    def forward(self, input):
        batch, height, width, dim = input.shape
        h_stride = height // self.window_size
        w_stride = width // self.window_size
        window = self.window_size

        def reshape(input):
            return (
                input.reshape(
                    batch,
                    h_stride,
                    window,
                    w_stride,
                    window,
                    self.n_head,
                    self.dim_head,
                )
                .permute(0, 1, 3, 5, 2, 4, 6)
                .reshape(batch, -1, self.n_head, window * window, self.dim_head)
            )

        query, key, value = self.weight(input).chunk(3, dim=-1)  # B, S, H, W^2, D

        query = reshape(query)
        key = reshape(key).transpose(-2, -1)
        value = reshape(value)

        score = query @ key / math.sqrt(self.dim_head)  # B, S, H, W^2, W^2

        attn = F.softmax(score, -1)
        attn = F.dropout(attn, self.dropout, training=self.training)

        out = attn @ value  # B, S, H, W^2, D

        out = (
            out.view(
                batch, h_stride, w_stride, self.n_head, window, window, self.dim_head
            )
            .permute(0, 1, 4, 2, 5, 3, 6)
            .reshape(batch, height, width, self.n_head * self.dim_head)
        )
        out = self.linear(out)

        return out

class TransformerLayer(nn.Module):
    def __init__(
        self,
        dim,
        n_head,
        dim_head,
        dim_ff,
        window_size,
        activation=nn.SiLU,
        drop_ff=0,
        drop_attn=0,
        drop_path=0,
    ):
        super().__init__()

        self.norm_attn_local = LayerNorm(dim)
        self.attn_local = MultiHeadedLocalAttention(
            dim, n_head, dim_head, window_size, drop_attn
        )
        self.norm_ff_local = LayerNorm(dim)
        self.ff_local = PositionwiseFeedForward(
            dim, dim_ff, activation=activation, dropout=drop_ff
        )

        self.norm_attn_global = LayerNorm(dim)
        self.attn_global = MultiHeadedAttention(dim, n_head, window_size, drop_attn)
        self.norm_ff_global = LayerNorm(dim)
        self.ff_global = PositionwiseFeedForward(
            dim, dim_ff, activation=activation, dropout=drop_ff
        )

        self.drop_path = DropPath(drop_path)

    def set_drop_path(self, p):
        self.drop_path.p = p

    def forward(self, input):
        out = input + self.drop_path(self.attn_local(self.norm_attn_local(input)))
        out = out + self.drop_path(self.ff_local(self.norm_ff_local(out)))
        out = out + self.drop_path(self.attn_global(self.norm_attn_global(out)))
        out = out + self.drop_path(self.ff_global(self.norm_ff_global(out)))

        return out

class PatchEmbedding(nn.Module):
    def __init__(self, in_dim, out_dim, window_size):
        super().__init__()

        self.window_size = window_size
        self.linear = nn.Linear(in_dim * window_size * window_size, out_dim)
        self.norm = nn.LayerNorm(out_dim)

    def forward(self, input):
        out = patchify(input, self.window_size)
        out = self.linear(out)
        out = self.norm(out)

        return out

def reduce_size(size, reduction):
    return (size[0] // reduction, size[1] // reduction)

@config_model(name="twins_svt", namespace="model", use_type=True)
class TwinsSVT(nn.Module):
    def __init__(
        self,
        n_class: StrictInt,
        depths: Tuple[StrictInt, StrictInt, StrictInt, StrictInt],
        dims: Tuple[StrictInt, StrictInt, StrictInt, StrictInt],
        dim_head: StrictInt,
        n_heads: Tuple[StrictInt, StrictInt, StrictInt, StrictInt],
        dim_ffs: Tuple[StrictInt, StrictInt, StrictInt, StrictInt],
        window_size: StrictInt,
        drop_ff: StrictFloat = 0.0,
        drop_attn: StrictFloat = 0.0,
        drop_path: StrictFloat = 0.0,
    ):
        super().__init__()

        self.depths = depths

        def make_block(i, in_dim, reduction):
            return self.make_block(
                depths[i],
                in_dim,
                dims[i],
                n_heads[i],
                dim_head,
                dim_ffs[i],
                window_size,
                reduction,
                drop_ff,
                drop_attn,
            )

        self.block1 = make_block(0, 3, 4)
        self.block2 = make_block(1, dims[0], 2)
        self.block3 = make_block(2, dims[1], 2)
        self.block4 = make_block(3, dims[2], 2)

        self.final_linear = nn.Sequential(nn.LayerNorm(dims[-1]))
        linear = nn.Linear(dims[-1], n_class)
        nn.init.normal_(linear.weight, std=0.02)
        nn.init.zeros_(linear.bias)
        self.classifier = nn.Sequential(nn.AdaptiveAvgPool2d(1), nn.Flatten(1), linear)

        self.apply(self.init_weights)
        self.set_dropout(None, drop_path)

    def set_dropout(self, dropout, drop_path):
        n_blocks = sum(self.depths)
        dp_rate = [drop_path * float(i) / n_blocks for i in range(n_blocks)]

        i = 0
        for block in self.block1:
            try:
                block.set_drop_path(dp_rate[i])
                i += 1

            except:
                continue

        for block in self.block2:
            try:
                block.set_drop_path(dp_rate[i])
                i += 1

            except:
                continue

        for block in self.block3:
            try:
                block.set_drop_path(dp_rate[i])
                i += 1

            except:
                continue

        for block in self.block4:
            try:
                block.set_drop_path(dp_rate[i])
                i += 1

            except:
                continue

    def init_weights(self, module):
        if isinstance(module, nn.Linear):
            nn.init.normal_(module.weight, std=0.02)

            if module.bias is not None:
                nn.init.zeros_(module.bias)

        elif isinstance(module, nn.LayerNorm):
            nn.init.ones_(module.weight)
            nn.init.zeros_(module.bias)

    def make_block(
        self,
        depth,
        in_dim,
        dim,
        n_head,
        dim_head,
        dim_ff,
        window_size,
        reduction,
        drop_ff,
        drop_attn,
    ):
        block = [PatchEmbedding(in_dim, dim, reduction)]

        for i in range(depth):
            block.append(
                TransformerLayer(
                    dim,
                    n_head,
                    dim_head,
                    dim_ff,
                    window_size,
                    drop_ff=drop_ff,
                    drop_attn=drop_attn,
                )
            )

            if i == 0:
                block.append(PositionalEncodingGenerator(dim))

        return nn.Sequential(*block)

    def forward(self, input):
        out = self.block1(input.permute(0, 2, 3, 1))
        out = self.block2(out)
        out = self.block3(out)
        out = self.block4(out)
        out = self.final_linear(out).permute(0, 3, 1, 2)
        out = self.classifier(out)

        return out


"""etm tables

Revision ID: 9586837afe25
Revises: bd7e69e0e71c, 7338c2929edc, d1472478ce8a
Create Date: 2022-12-05 14:13:26.587715

"""
from alembic import op
import sqlalchemy as sa
import rdr_service.model.utils
from sqlalchemy.dialects import mysql

from rdr_service.participant_enums import PhysicalMeasurementsStatus, QuestionnaireStatus, OrderStatus
from rdr_service.participant_enums import WithdrawalStatus, WithdrawalReason, SuspensionStatus, QuestionnaireDefinitionStatus
from rdr_service.participant_enums import EnrollmentStatus, Race, SampleStatus, OrganizationType, BiobankOrderStatus
from rdr_service.participant_enums import OrderShipmentTrackingStatus, OrderShipmentStatus
from rdr_service.participant_enums import MetricSetType, MetricsKey, GenderIdentity
from rdr_service.model.base import add_table_history_table, drop_table_history_table
from rdr_service.model.code import CodeType
from rdr_service.model.etm import ExtensionType
from rdr_service.model.site_enums import SiteStatus, EnrollingStatus, DigitalSchedulingStatus, ObsoleteStatus

# revision identifiers, used by Alembic.
revision = '9586837afe25'
down_revision = ('bd7e69e0e71c', '7338c2929edc', 'd1472478ce8a')
branch_labels = None
depends_on = None

def upgrade(engine_name):
    globals()["upgrade_%s" % engine_name]()

def downgrade(engine_name):
    globals()["downgrade_%s" % engine_name]()

def upgrade_rdr():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('etm_questionnaire',
    sa.Column('etm_questionnaire_id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('created', rdr_service.model.utils.UTCDateTime(), nullable=True),
    sa.Column('modified', rdr_service.model.utils.UTCDateTime(), nullable=True),
    sa.Column('questionnaire_type', sa.String(length=100), nullable=True),
    sa.Column('version', sa.Integer(), nullable=True),
    sa.Column('semantic_version', sa.String(length=100), nullable=True),
    sa.Column('name', sa.String(length=100), nullable=True),
    sa.Column('title', sa.String(length=100), nullable=True),
    sa.Column('resource', sa.JSON(), nullable=True),
    sa.PrimaryKeyConstraint('etm_questionnaire_id')
    )
    op.create_table('etm_questionnaire_response',
    sa.Column('etm_questionnaire_response_id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('created', rdr_service.model.utils.UTCDateTime(), nullable=True),
    sa.Column('modified', rdr_service.model.utils.UTCDateTime(), nullable=True),
    sa.Column('authored', rdr_service.model.utils.UTCDateTime(), nullable=True),
    sa.Column('questionnaire_type', sa.String(length=100), nullable=True),
    sa.Column('status', rdr_service.model.utils.Enum(QuestionnaireStatus), nullable=True),
    sa.Column('participant_id', sa.Integer(), nullable=True),
    sa.Column('resource', sa.JSON(), nullable=True),
    sa.Column('version', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['participant_id'], ['participant.participant_id'], ),
    sa.PrimaryKeyConstraint('etm_questionnaire_response_id')
    )
    op.create_table('etm_questionnaire_response_answer',
    sa.Column('etm_questionnaire_response_answer_id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('etm_questionnaire_response_id', sa.Integer(), nullable=True),
    sa.Column('link_id', sa.String(length=40), nullable=True),
    sa.Column('answer_value', mysql.MEDIUMTEXT(), nullable=True),
    sa.ForeignKeyConstraint(['etm_questionnaire_response_id'], ['etm_questionnaire_response.etm_questionnaire_response_id'], ),
    sa.PrimaryKeyConstraint('etm_questionnaire_response_answer_id')
    )
    op.create_table('etm_questionnaire_response_metadata',
    sa.Column('etm_questionnaire_response_metadata_id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('etm_questionnaire_response_id', sa.Integer(), nullable=True),
    sa.Column('extension_type', rdr_service.model.utils.Enum(ExtensionType), nullable=True),
    sa.Column('key', sa.String(length=100), nullable=True),
    sa.Column('value_string', mysql.MEDIUMTEXT(), nullable=True),
    sa.Column('value_int', sa.BigInteger(), nullable=True),
    sa.Column('value_decimal', sa.Float(), nullable=True),
    sa.ForeignKeyConstraint(['etm_questionnaire_response_id'], ['etm_questionnaire_response.etm_questionnaire_response_id'], ),
    sa.PrimaryKeyConstraint('etm_questionnaire_response_metadata_id')
    )
    # ### end Alembic commands ###

def downgrade_rdr():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('etm_questionnaire_response_metadata')
    op.drop_table('etm_questionnaire_response_answer')
    op.drop_table('etm_questionnaire_response')
    op.drop_table('etm_questionnaire')
    # ### end Alembic commands ###

def upgrade_metrics():
    # ### commands auto generated by Alembic - please adjust! ###
    pass
    # ### end Alembic commands ###

def downgrade_metrics():
    # ### commands auto generated by Alembic - please adjust! ###
    pass
    # ### end Alembic commands ###


from django.contrib import admin
from .models import Category, Product

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name', 'slug', 'parents']
    list_filter = ('parents',)
    # automatic generation of values
    prepopulated_fields = {'slug': ('name',)}

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ['name', 'slug', 'price',
                    'quantity_available', 'available', 'created', 'updated']
    list_filter = ['available', 'created', 'updated']
    # used it in the administrative interface to specify
    # fields that can be edited directly in the list view
    list_editable = ['price', 'available']
    prepopulated_fields = {'slug': ('name',)}


#!/usr/bin/env python3
'''Scan all files with a given `suffix` in a given `path` recursively.
'''

import argparse
import pathlib
import sys

def scan_and_print(paths: list, suffix: str, old_word: str):
    '''Scan a word in all files with a given list of paths.
    '''
    word_count = 0
    file_count = 0
    for path in paths:
        old_name = str(path)
        word_count_in_file = 0
        with path.open() as old_file:
            for line in old_file:
                word_count_in_line = line.count(old_word)
                if word_count_in_line > 0:
                    word_count_in_file += word_count_in_line
        if word_count_in_file > 0:
            print(f'{word_count_in_file} "{old_word}"(s) found in {old_name}')
            word_count += word_count_in_file
            file_count += 1
    print(f'{word_count} "{old_word}"(s) found in {file_count} {suffix}',
        'file(s)')
    return word_count

def scan_and_delete(paths: list, old_word: str):
    '''Scan a word in all files in a given list of paths and delete the lines.
    '''
    for path in paths:
        old_name = str(path)
        new_name = old_name + '.temp'
        with path.open() as old_file:
            with open(new_name, 'w') as new_file:
                for line in old_file:
                    if old_word not in line:
                        new_file.write(line)
        pathlib.Path(new_name).replace(old_name)

def scan_and_replace(paths: list, old_word: str, new_word: str):
    '''Scan a word in all files in a given list of paths and replace the words.
    '''
    for path in paths:
        old_name = str(path)
        new_name = old_name + '.temp'
        with path.open() as old_file:
            with open(new_name, 'w') as new_file:
                for line in old_file:
                    new_file.write(line.replace(old_word, new_word))
        pathlib.Path(new_name).replace(old_name)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(prog = 'python3 scan.py')
    parser.add_argument('-p', '--path',
        default='.', type=str,
        help='path of the folder to be scanned')
    parser.add_argument('-s', '--suffix',
        default='py', type=str,
        help='suffix (type) of files to be scanned')
    parser.add_argument('-o', '--old_word',
        default='hello', type=str,
        help='the old string to be searched')
    parser.add_argument('-n', '--new_word',
        default='world', type=str,
        help='the new string to be substituted in')
    parser.add_argument('-d', '--delete',
        action='store_true',
        help='delete the lines containing the old string')
    parser.add_argument('-r', '--replace',
        action='store_true',
        help='replace the old string with the new one')
    args = parser.parse_args()
    print(args)
    root = pathlib.Path(args.path)
    paths = list(root.glob('**/*.' + args.suffix))
    if scan_and_print(paths, args.suffix, args.old_word) > 0:
        if args.delete:
            print('Are you sure to delete these lines? [y/n]',
                end='', flush=True)
            if sys.stdin.read(1) == 'y':
                scan_and_delete(paths, args.old_word)
        elif args.replace:
            print(f'Are you sure to replace these "{args.old_word}"s',
                f'to "{args.new_word}"s? [y/n]', end='', flush=True)
            if sys.stdin.read(1) == 'y':
                scan_and_replace(paths, args.old_word, args.new_word)


from . import utils
from aws_requests_auth.aws_auth import AWSRequestsAuth
from elasticsearch import Elasticsearch,RequestsHttpConnection

class Basics:
    """
    obs
    ---
    index and docType can be set
    by class or by each action method
    """

    _conn=None
    _index=None
    _docType=None

    @classmethod
    def setConn(cls, *args, **kwargs):
        """
        connection options
        ------------------
        elasticsearch documentation
        """
        try:
            cls._conn=Elasticsearch(*args, **kwargs)
        except Exception as e:
            print("[FAIL TO CONNECT TO ES] :: {}".format(e))
            exit(3)

    @classmethod
    def setAWsConn(cls, aws_key, aws_secret,
                   esEndPoint, esRegion, esService):
        """
        """
        try:
            auth=AWSRequestsAuth(aws_access_key=aws_key,
                       aws_secret_access_key=aws_secret,
                       aws_host=esEndPoint,
                       aws_region=esRegion,
                       aws_service=esService)
            cls._conn=Elasticsearch(
                            hosts=[{"host": esEndPoint, "port": 443}],
                            use_ssl=True,
                            verify_certs=True,
                            connection_class=RequestsHttpConnection,
                            http_auth=auth)
        except Exception as e:
            print("[FAIL TO CONNECT TO ES] :: {}".format(esEndPoint))
            print(e)
            exit(3)

    @classmethod
    def setIndex(cls, indexString):
        """
        """
        cls._index=indexString

    @classmethod
    def setDocType(cls, docTypeString):
        """
        """
        cls._docType=docTypeString

    @classmethod
    def createIndex(cls, *args, **kwargs):
        """
        """
        #ignore 400 :: if already exists
        if "ignore" not in kwargs:kwargs.update({"ignore":400})
        cls.indices.create(*args, **kwargs)

    @classmethod
    def save(cls, doc_id, doc, index=None, docType=None):
        """
        """
        if not index:
            index=cls._index
            if not index:raise Exception("[-] Setting index is required")
        if not docType:
            docType=cls._docType
        cls._conn.index(index=index,doc_type=docType,
                        id=doc_id,body=doc)
    @classmethod
    def search(cls, query, index=None, docType=None):
        """
        """
        return cls._conn.search(index=index, doc_type=docType, body=query)

    @classmethod
    def scroll(cls, query, index=None, docType=None, scrollTime=None):
        """
        """
        if not scrollTime:
            scrollTime="1m"
        if not index:
            index=cls._index
            if not index:raise Exception("[-] Setting index is required")
        if not docType:
            docType=cls._docType
        firstHit=cls._conn.search(index=index, doc_type=docType,
                                  body=query, scroll=scrollTime)
        scrollIt=firstHit["_scroll_id"]
        #send first hit
        for doc in firstHit["hits"]["hits"]:yield doc
        #iter the rest
        while True:
            try:
                manyHits=cls._conn.scroll(scroll_id=scrollIt,
                                           scroll=scrollTime)
                if not len(manyHits["hits"]["hits"]):break
                for doc in manyHits["hits"]["hits"]:yield doc
                scrollIt=manyHits["_scroll_id"]
            except Exception as e:
                print("[SCROLL FAIL] :: {}".format(e))
                break

    @classmethod
    def delete(cls, doc_id, index=None, docType=None):
        """
        """
        return cls._conn.delete(index=index, doc_type=docType, id=doc_id)

    @classmethod
    def deleteByMatch(cls, macthQuery, index=None, docType=None):
        """
        """
        return cls._conn.delete_by_query(index=index,
                                          doc_type=docType,
                                          body={'query':{"match":macthQuery}})

    @classmethod
    def deleteAllbyIndex(cls, index=None, docType=None):
        """
        """
        return cls._conn.delete_by_query(index=index, doc_type=docType,
                                          body={'query':{"match_all":{}}})

    @classmethod
    def getAllbyIndex(cls, index):
        """
        """
        cls.scroll({"query":{"match_all":{}}}, index=index)

    @classmethod
    def backupByIndex(cls, outputFile, index=None, docType=None,
                        scrollTime=None, bucketSize=100):
        """
        """
        _bucket=[]
        _bucket_count=1
        _docs_iterator=cls.scroll(
                    query={'query':{"match_all":{}}},
                    index=index, docType=docType,
                    scrollTime=scrollTime)
        for doc in _docs_iterator:
            _bucket.append(doc)
            if len(_bucket) > bucketSize:
                utils._save_bucket(outputFile+"_"+str(_bucket_count), _bucket)
                _bucket_count+=1
                _bucket=[]
        if not len(_bucket):return
        utils._save_bucket(outputFile+"_"+str(_bucket_count), _bucket)


from typing import List, Dict
from database import DataBase
from logging_ import ProjectLog
from .abc import Specification

class FileSpecification(Specification):

    def __init__(self, extensions: List[str], only_new_file: bool, project_log: ProjectLog, proc_dict: Dict):
        self.extensions: List[str] = extensions
        self.only_new_file: bool = only_new_file
        self.project_log = project_log
        self.proc_dict = proc_dict

    @staticmethod
    def delete_extension(filename: str) -> str:
        return filename.split('.')[-1]

    def is_satisfy(self, item: str):
        extension = self.delete_extension(item)
        if extension not in self.extensions:
            return False
        if self.only_new_file:
            with DataBase() as db:
                if db.is_processed(item):
                    self.project_log.info(f'{item} is processed')
                    self.proc_dict['status'] = f'{item} is processed'
                    return False
        return True

class DataBaseSpecification(Specification):

    def __init__(self, doc_status: List[str], record_id: int):
        self.doc_status: List[str] = doc_status
        self.id_ = record_id

    def is_satisfy(self, item) -> bool:
        pass

    def as_dict(self):
        return {
            'doc_status': self.doc_status,
            'id': self.id_,
        }

__all__ = ['FileSpecification', 'DataBaseSpecification']


import requests

def obter_localizacao_por_ip():
    url = 'https://ipinfo.io/json'
    response = requests.get(url)
    data = response.json()
    cidade = data.get('city', '')
    return cidade

def obter_clima(cidade):
    api_key = 'SUA_CHAVE_DE_API'
    url = f'http://api.openweathermap.org/data/2.5/weather?q={cidade}&appid={api_key}'

    response = requests.get(url)
    dados = response.json()

    if dados['cod'] == 200:
        descricao_clima = dados['weather'][0]['description']
        temperatura = dados['main']['temp']
        umidade = dados['main']['humidity']

        print(f"Clima em {cidade}: {descricao_clima}")
        print(f"Temperatura: {temperatura} K")
        print(f"Umidade: {umidade}%")
    else:
        print(f"Erro: {dados['message']}")


# -*- coding: utf-8 -*-
"""
Created on Wed Mar 29 16:29:22 2023

@author: iant

READ IN HDF5 FILE

get_gem_tpvmr(molecule, altitude, myear, ls, lat, lon, lst)
"""

import os
import h5py

from analysis.so_lno_2023.config import ROOT_DATA_DIR

def make_filepath(h5):

    year = h5[0:4]
    month = h5[4:6]
    day = h5[6:8]

    h5_filepath = os.path.join(ROOT_DATA_DIR, year, month, day, "%s.h5" %h5)
    return h5_filepath

def read_h5(h5):

    h5_filepath = make_filepath(h5)

    with h5py.File(h5_filepath, "r") as h5_f:

        y = h5_f["Science/Y"][...]
        y_raw = h5_f["Science/YUnmodified"][...]
        x = h5_f["Science/X"][0, :]
        order = h5_f["Channel/DiffractionOrder"][0]
        aotf_freq = h5_f["Channel/AOTFFrequency"][0]

        nomad_t = h5_f["Channel/MeasurementTemperature"][0][0]

        alts = h5_f["Geometry/Point0/TangentAltAreoid"][:, 0]
        lats = h5_f["Geometry/Point0/Lat"][:, 0]
        lons = h5_f["Geometry/Point0/Lon"][:, 0]

        myear = 36
        ls = h5_f["Geometry/LSubS"][0, 0] #first value
        lst = h5_f["Geometry/Point0/LST"][:, 0]

    return {"y":y, "y_raw":y_raw, "x":x, "order":order, "aotf_freq":aotf_freq, "nomad_t":nomad_t, "alts":alts, "lats":lats, "lons":lons, "myear":myear, "ls":ls, "lst":lst}


# wxy
import random
import h5py
import numpy as np
import torch
import sys

def Sort_en(filename1, filename2):
    with open(filename2, "w", encoding='utf-8') as file1:
        with open(filename1, "r", encoding='utf-8') as file:
            for line in file:
                temp = line.strip()
                n = '<sos> '
                m = ' <eos>'
                file1.write(''.join([n, temp, m]))
                file1.write('\n')
def Sort_target(filename1, filename2):
    with open(filename2, "w", encoding='utf-8') as file1:
        with open(filename1, "r", encoding='utf-8') as file:
            for line in file:
                temp = line.strip()
                m = ' <eos>'
                file1.write(''.join([temp, m]))
                file1.write('\n')
# 4、词频统计：收集数据集上的词典，建立单词到索引的一对一映射并保存结果，（0索引对应padding，其它单词的索引从1开始累加，单词顺序从高频到低频）；

def load(fileName):
    with open(fileName, "rb") as frd:
        tmp = frd.read().strip()  # 读文件并且去除
        rs = eval(tmp.decode("utf-8"))  # eval() 函数计算或执行参数 读文件使用decode
    return rs

# 5、根据词典索引，将排序后的数据切分为batch（每个batch包含的token数量尽可能多，但不要超过2560个token）并转换为tensor（为matrix，batch size * seql,
# batch size: 这个batch中包含几个句子，seql: 这些句子中最长句子的长度（短句使用padding，映射索引为0，填充到相同长度）），
# 查找每个batch的大小
def handle_2(file):
    batch_size = []
    tt = []
    lines = 0  # 目前矩阵有几行
    seql = 3
    bsize = int(2560 / 3)
    for line in file:
        temp = line.strip()
        if temp :
            temp = temp.split()
            l = len(temp) # 词长
            if lines < bsize:  # 找出当前batch的大小
                lines += 1  # 读取行数加一
                if l > seql:
                    if lines * l > 2560:
                        batch_size.append((lines-1, seql))  # 再加会超 则直接存为一个batch
                        lines = 1  # 将本行加入下一个batch
                    seql = l
                    bsize = int(2560 / seql)
            else:
                batch_size.append((bsize, seql))  # 当前batch读取完毕 存储
                lines = 1  # 重新读取下一个batch 并把当前行加入
                seql = l
                bsize = int(2560 / seql)
    if lines > 0:
        batch_size.append((lines, seql))
        tt.append([len(temp), l, lines, bsize, seql])
    with open('batch.txt', "w", encoding="utf-8") as file:
        for line in batch_size:
            file.write(str(line) + ' ')
    return batch_size

# 根据上次存储每个batch大小 存储下一个文件
def handle_3(words, file, batch_size):
    index = 0
    batch = []
    matrix_line = 0
    bsize = batch_size[0][0]  # 行数
    seql = batch_size[0][1]  # 词长
    for line in file:
        tmp = line.strip()
        if tmp:
            tmp = tmp.split()
            if matrix_line < bsize:
                batch.append([words.get(w, 1) for w in tmp] + [0 for _ in range(seql - len(tmp))])  # 把当前行的数据存入batch
                matrix_line += 1
            else:  # 当前batch已经存储好 开始寻找下一个batch
                yield batch,index
                index += 1
                bsize = batch_size[index][0]  # 行数
                seql = batch_size[index][1]  # 词长
                batch= []  # 创建一个batch
                batch.append([words.get(w, 1) for w in tmp] + [0 for _ in range(seql - len(tmp))])  # 把当前行的数据存入batch
                matrix_line = 1
    if batch:
        yield batch, index

# 按hdf5格式存入文件。需要学会yield语法（生成器）的使用。HDF5存储格式：src(一个group)/<k, v>：存转换后的数据，k为数据的索引，从0自增，v为具体数据张量；
# ndata:一个只有一个元素的向量，存src中数据的数量；nword:一个只有一个元素的向量，存第3步收集的词典大小。
def save(words_en, file_en, f5_en, batch_size):
    group_en = f5_en.create_group("group")
    index = 0
    for batch_en, index in handle_3(words_en, file_en, batch_size):
        matrix_array_en = np.array(batch_en, dtype=np.int32)  # 将batch转成numpy类型存储
        group_en.create_dataset(str(index), data=matrix_array_en)

    f5_en["ndata"] = np.array(index, dtype=np.int32)
    f5_en["nword"] = np.array(len(words_en), dtype=np.int32)

if __name__ == "__main__":
    Sort_en(sys.argv[1], sys.argv[2])  # BPE BPE_sort en
    Sort_target(sys.argv[5], sys.argv[6])
    print(1)
    words_en = load(sys.argv[3])  # BPE_sort dict  en
    print(2)
    with open(sys.argv[2], 'r', encoding="utf-8") as file_en:  # result
        batch_size = handle_2(file_en)
    print(3)
    with open(sys.argv[2], 'r', encoding="utf-8") as file_en,h5py.File(sys.argv[4], 'w') as f5_en:  # result
        save(words_en, file_en, f5_en, batch_size)
    print(4)

# python ..\GIT\pre_test_data.py test.tc.en test_sort_en.txt dict_en.txt result_en_test.hdf5
# python pre_test_data.py test.BPE.en test_sort_en.txt dict_en.txt result_en_test.hdf5 test.BPE.de test_sort_de.txt
# perl moses/mosesdecoder/scripts/generic/multi-bleu.perl test_sort_de.txt < result_test_de.txt
# perl moses/mosesdecoder/scripts/generic/mteval-v13a.pl -r test.BPE.de -s test.BPE.de -t test.BPE.de -c -b
# 　　调用行：


#--------------------------------
# Name:         build_static_files_arcpy.py
# Purpose:      Build static files for ET-Demands from zonal stats ETCells
# Python:       2.7
#--------------------------------

import argparse
from collections import defaultdict
import datetime as dt
import logging
import os
import re
import shutil
import sys

import arcpy

import _util as util

def main(ini_path, zone_type='huc8', area_threshold=10,
         beef_cuttings=4, dairy_cuttings=5,
         overwrite_flag=False, cleanup_flag=False):
    """Build static text files needed to run ET-Demands model

    Args:
        ini_path (str): file path of the project INI file
        zone_type (str): Zone type (huc8, huc10, county)
        area_threshold (float): CDL area threshold [acres]
        beef_cuttings (int): Initial number of beef hay cuttings
        dairy_cuttings (int): Initial number of dairy hay cuttings
        overwrite_flag (bool): If True, overwrite existing files
        cleanup_flag (bool): If True, remove temporary files

    Returns:
        None

    """
    logging.info('\nBuilding ET-Demands Static Files')

    # Input units
    station_elev_units = 'FEET'

    # Default values
    permeability = -999
    soil_depth = 60         # inches
    aridity = 50
    irrigation = 1
    crops = 86

    # Input paths
    # DEADBEEF - For now, get cropET folder from INI file
    # This function may eventually be moved into the main cropET code
    crop_et_sec = 'CROP_ET'
    config = util.read_ini(ini_path, section=crop_et_sec)

    try:
        project_ws = config.get(crop_et_sec, 'project_folder')
    except:
        logging.error('project_folder parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        gis_ws = config.get(crop_et_sec, 'gis_folder')
    except:
        logging.error('gis_folder parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        et_cells_path = config.get(crop_et_sec, 'cells_path')
    except:
        logging.error('cells_path parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        stations_path = config.get(crop_et_sec, 'stations_path')
    except:
        logging.error('stations_path parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        crop_et_ws = config.get(crop_et_sec, 'crop_et_folder')
    except:
        logging.error('crop_et_ws parameter must be set in the INI file, '
                      'exiting')
        return False
    try:
        template_ws = config.get(crop_et_sec, 'template_folder')
    except:
        template_ws = os.path.join(os.path.dirname(crop_et_ws), 'static')
        logging.info(
            '\nStatic text file "template_folder" parameter was not set '
            'in the INI\n  Defaulting to: {}'.format(template_ws))

    # Read data from geodatabase or shapefile
    # if '.gdb' in et_cells_path and not et_cells_path.endswith('.shp'):
    #     _flag = False
    #     _path = os.path.dirname(et_cells_path)
    #      gdb_path = r'D:\Projects\CAT_Basins\AltusOK\et-demands_py\et_demands.gdb'
    #     _cells_path = os.path.join(gdb_path, 'et_cells')

    # Output sub-folder names
    static_ws = os.path.join(project_ws, 'static')

    # Weather station shapefile field names
    station_id_field = 'STATION_ID'
    if zone_type == 'huc8':
        station_zone_field = 'HUC8'
    elif zone_type == 'huc10':
        station_zone_field = 'HUC10'
    elif zone_type == 'county':
        station_zone_field = 'COUNTYNAME'
    elif zone_type == 'gridmet':
        station_zone_field = 'GRIDMET_ID'
        station_id_field = 'GRIDMET_ID'
    station_lat_field = 'LAT'
    station_lon_field = 'LON'
    if station_elev_units.upper() in ['FT', 'FEET']:
        station_elev_field = 'ELEV_FT'
    elif station_elev_units.upper() in ['M', 'METERS']:
        station_elev_field = 'ELEV_M'
    # station_elev_field = 'ELEV_FT'

    # ET Cell field names
    cell_lat_field = 'LAT'
    cell_lon_field = 'LON'
    cell_id_field = 'CELL_ID'
    cell_name_field = 'CELL_NAME'
    cell_station_id_field = 'STATION_ID'
    # awc_field = 'AWC'
    clay_field = 'CLAY'
    sand_field = 'SAND'
    awc_in_ft_field = 'AWC_IN_FT'
    hydgrp_num_field = 'HYDGRP_NUM'
    hydgrp_field = 'HYDGRP'

    # huc_field = 'HUC{}'.format(huc)
    # permeability_field = 'PERMEABILITY'
    # soil_depth_field = 'SOIL_DEPTH'
    # aridity_field = 'ARIDITY'
    # dairy_cutting_field = 'DAIRY_CUTTINGS'
    # beef_cutting_field = 'BEEF_CUTTINGS'

    # Static file names
    cell_props_name = 'ETCellsProperties.txt'
    cell_crops_name = 'ETCellsCrops.txt'
    cell_cuttings_name = 'MeanCuttings.txt'
    crop_params_name = 'CropParams.txt'
    crop_coefs_name = 'CropCoefs.txt'
    crop_coefs_eto = 'CropCoefs_eto.txt'
    crop_coefs_etr = 'CropCoefs_etr.txt'
    eto_ratio_name = 'EToRatiosMon.txt'
    static_list = [crop_params_name, crop_coefs_name, crop_coefs_eto,
                   crop_coefs_etr, cell_props_name, cell_crops_name,
                   cell_cuttings_name, eto_ratio_name]

    # Check input folders
    if not os.path.isdir(crop_et_ws):
        logging.critical('\nERROR: The INI cropET folder does not exist'
                         '\n  {}'.format(crop_et_ws))
        sys.exit()
    elif not os.path.isdir(project_ws):
        logging.critical('\nERROR: The project folder does not exist'
                         '\n  {}'.format(project_ws))
        sys.exit()
    elif not os.path.isdir(gis_ws):
        logging.critical('\nERROR: The GIS folder does not exist'
                         '\n  {}'.format(gis_ws))
        sys.exit()
    logging.info('\nGIS Workspace:      {}'.format(gis_ws))
    logging.info('Project Workspace:  {}'.format(project_ws))
    logging.info('CropET Workspace:   {}'.format(crop_et_ws))
    logging.info('Template Workspace: {}'.format(template_ws))

    # Check input files
    if not arcpy.Exists(et_cells_path):
        logging.critical('\nERROR: The ET Cell shapefile does not exist'
                         '\n  {}'.format(et_cells_path))
        sys.exit()
    elif not arcpy.Exists(stations_path):
        logging.critical('\nERROR: The weather station shapefile does not exist'
                         '\n  {}'.format(stations_path))
        sys.exit()
    for static_name in static_list:
        if not os.path.isfile(os.path.join(template_ws, static_name)):
            logging.error(
                '\nERROR: The static template does not exist'
                '\n  {}'.format(os.path.join(template_ws, static_name)))
            sys.exit()
    logging.debug('ET Cells Path: {}'.format(et_cells_path))
    logging.debug('Stations Path: {}'.format(stations_path))

    # Check units
    if station_elev_units.upper() not in ['FEET', 'FT', 'METERS', 'M']:
        logging.error(
            '\nERROR: Station elevation units {} are invalid'
            '\n  Units must be METERS or FEET'.format(station_elev_units))
        sys.exit()

    # Build output table folder if necessary
    if not os.path.isdir(static_ws):
        os.makedirs(static_ws)

    # Read Weather station/cell data
    logging.info('\nReading station shapefile')
    logging.debug('  {}'.format(stations_path))
    fields = [station_zone_field, station_id_field, station_elev_field,
              station_lat_field, station_lon_field]
    logging.debug('  Fields: {}'.format(fields))
    station_data_dict = defaultdict(dict)
    with arcpy.da.SearchCursor(stations_path, fields) as s_cursor:
        for row in s_cursor:
            for field in fields[1:]:
                # Key/match on strings even if ID is an integer
                station_data_dict[str(row[0])][field] = row[fields.index(field)]
    for k, v in station_data_dict.items():
        logging.debug('  {}: {}'.format(k, v))

    # Read ET Cell zonal stats
    logging.info('\nReading ET Cell Zonal Stats')
    logging.debug('  {}'.format(et_cells_path))
    crop_field_list = sorted([
        f.name for f in arcpy.ListFields(et_cells_path)
        if re.match('CROP_\d{2}', f.name)])
    fields = [cell_id_field, cell_name_field, cell_lat_field,
              awc_in_ft_field, clay_field, sand_field,
              hydgrp_num_field, hydgrp_field]
    fields = fields + crop_field_list
    logging.debug('  Fields: {}'.format(fields))
    cell_data_dict = defaultdict(dict)
    with arcpy.da.SearchCursor(et_cells_path, fields) as s_cursor:
        for row in s_cursor:
            for field in fields[1:]:
                # Key/match on strings even if ID is an integer
                cell_data_dict[str(row[0])][field] = row[fields.index(field)]

    # Update ET Cell STATION_ID value
    fields = [cell_id_field, cell_station_id_field]
    with arcpy.da.UpdateCursor(et_cells_path, fields) as u_cursor:
        for row in u_cursor:
            try:
                row[1] = station_data_dict[row[0]][station_id_field]
                u_cursor.updateRow(row)
            except KeyError:
                pass

    # Convert elevation units if necessary
    if station_elev_units.upper() in ['METERS', 'M']:
        logging.debug('  Convert station elevation from meters to feet')
        for k in station_data_dict.keys():
            station_data_dict[k][station_elev_field] /= 0.3048

    logging.info('\nCopying template static files')
    for static_name in static_list:
        # if (overwrite_flag or
        #         os.path.isfile(os.path.join(static_ws, static_name))):
        logging.debug('  {}'.format(static_name))
        shutil.copy(os.path.join(template_ws, static_name), static_ws)
        # shutil.copyfile(
        #     .path.join(template_ws, static_name),
        #     .path.join(static_ws, crop_params_name))

    logging.info('\nWriting static text files')
    cell_props_path = os.path.join(static_ws, cell_props_name)
    cell_crops_path = os.path.join(static_ws, cell_crops_name)
    cell_cuttings_path = os.path.join(static_ws, cell_cuttings_name)
    # crop_params_path = os.path.join(static_ws, crop_params_name)
    # crop_coefs_path = os.path.join(static_ws, crop_coefs_name)
    eto_ratio_path = os.path.join(static_ws, eto_ratio_name)

    # Write cell properties
    logging.debug('  {}'.format(cell_props_path))
    with open(cell_props_path, 'a') as output_f:
        for cell_id, cell_data in sorted(cell_data_dict.items()):
            if cell_id in station_data_dict.keys():
                station_data = station_data_dict[cell_id]
                station_id = station_data[station_id_field]
                station_lat = '{:>9.4f}'.format(station_data[station_lat_field])
                station_lon = '{:>9.4f}'.format(station_data[station_lon_field])
                station_elev = '{:.2f}'.format(station_data[station_elev_field])
            else:
                logging.debug(
                    '    Cell_ID {} was not found in the '
                    'station data'.format(cell_id))
                station_id, station_lat, station_lon, station_elev = '', '', '', ''
            # There is an extra/unused column in the template and excel files
            output_list = [
                cell_id, cell_data[cell_name_field],
                station_id, station_lat, station_lon,
                station_elev, permeability,
                '{:.4f}'.format(cell_data[awc_in_ft_field]), soil_depth,
                cell_data[hydgrp_field], cell_data[hydgrp_num_field],
                aridity, '']
            output_f.write('\t'.join(map(str, output_list)) + '\n')
            del output_list
            del station_id, station_lat, station_lon, station_elev

    # Write cell crops
    logging.debug('  {}'.format(cell_crops_path))
    with open(cell_crops_path, 'a') as output_f:
        for cell_id, cell_data in sorted(cell_data_dict.items()):
            if cell_id in station_data_dict.keys():
                station_id = station_data_dict[cell_id][station_id_field]
            else:
                logging.debug(
                    '    Cell_ID {} was not found in the '
                    'station data'.format(cell_id))
                station_id = ''
            output_list = [
                cell_id, cell_data[cell_name_field], station_id, irrigation]
            crop_list = ['CROP_{:02d}'.format(i) for i in range(1, crops + 1)]
            crop_area_list = [
                cell_data[crop] if crop in cell_data.keys() else 0
                for crop in crop_list]
            crop_flag_list = [
                1 if area > area_threshold else 0 for area in crop_area_list]
            output_list = output_list + crop_flag_list
            output_f.write('\t'.join(map(str, output_list)) + '\n')
            del crop_list, crop_area_list, crop_flag_list, output_list

    # Write cell cuttings
    logging.debug('  {}'.format(cell_cuttings_path))
    with open(cell_cuttings_path, 'a') as output_f:
        for cell_id, cell_data in sorted(cell_data_dict.items()):
            output_list = [
                cell_id, cell_data[cell_name_field],
                '{:>9.4f}'.format(cell_data[cell_lat_field]),
                dairy_cuttings, beef_cuttings]
            output_f.write('\t'.join(map(str, output_list)) + '\n')
            del output_list

    # Write monthly ETo ratios
    logging.debug('  {}'.format(eto_ratio_path))
    with open(eto_ratio_path, 'a') as output_f:
        for cell_id, cell_data in sorted(cell_data_dict.items()):
            if cell_id in station_data_dict.keys():
                station_data = station_data_dict[cell_id]
                station_id = station_data[station_id_field]
                # station_lat = '{:>9.4f}'.format(station_data[station_lat_field])
                # station_lon = '{:>9.4f}'.format(station_data[station_lon_field])
                # station_elev = '{:.2f}'.format(station_data[station_elev_field])
            else:
                logging.debug(
                    '    Cell_ID {} was not found in the '
                    'station data'.format(cell_id))
                # station_id, station_lat, station_lon, station_elev = '', '', '', ''
                continue
            output_list = [station_id, ''] + [1.0] * 12
            output_f.write('\t'.join(map(str, output_list)) + '\n')
            del output_list

def arg_parse():
    """"""
    parser = argparse.ArgumentParser(
        description='ET-Demands Static Files',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        '-i', '--ini', metavar='PATH',
        type=lambda x: util.is_valid_file(parser, x), help='Input file')
    parser.add_argument(
        '--zone', default='huc8', metavar='STR', type=str,
        choices=('huc8', 'huc10', 'county', 'gridmet'),
        help='Zone type [{}]'.format(
            ', '.join(['huc8', 'huc10', 'county', 'gridmet'])))
    parser.add_argument(
        '--acres', default=10, type=float,
        help='Crop area threshold')
    parser.add_argument(
        '--beef', default=4, type=int,
        help='Number of beef hay cuttings')
    parser.add_argument(
        '--dairy', default=5, type=int,
        help='Number of dairy hay cuttings')
    parser.add_argument(
        '-o', '--overwrite', default=None, action='store_true',
        help='Overwrite existing file')
    parser.add_argument(
        '--clean', default=None, action='store_true',
        help='Remove temporary datasets')
    parser.add_argument(
        '--debug', default=logging.INFO, const=logging.DEBUG,
        help='Debug level logging', action="store_const", dest="loglevel")
    args = parser.parse_args()

    return args

if __name__ == '__main__':
    args = arg_parse()

    logging.basicConfig(level=args.loglevel, format='%(message)s')
    logging.info('\n{}'.format('#' * 80))
    logging.info('{0:<20s} {1}'.format(
        'Run Time Stamp:', dt.datetime.now().isoformat(' ')))
    logging.info('{0:<20s} {1}'.format('Current Directory:', os.getcwd()))
    logging.info('{0:<20s} {1}'.format(
        'Script:', os.path.basename(sys.argv[0])))

    main(ini_path=args.ini, area_threshold=args.acres, zone_type=args.zone,
         dairy_cuttings=args.dairy, beef_cuttings=args.beef,
         overwrite_flag=args.overwrite, cleanup_flag=args.clean)


from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from sqlalchemy.sql import func
from config import db, bcrypt

class Skill(db.Model):
    __tablename__ = "skills"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    languages = db.Column(db.String(255))
    python = db.Column(db.String(255), nullable="True")
    html = db.Column(db.String(255), nullable="True")
    bio = db.Column(db.Text)
    developer_id = db.Column(db.Integer, db.ForeignKey("developer.id"))
    developer = db.relationship('Developer', foreign_keys=[developer_id], backref="developer_skills", cascade='all')
    created_at = db.Column(db.DateTime, server_default=func.now())
    updated_at = db.Column(db.DateTime, server_default=func.now(), onupdate=func.now())

    @classmethod
    def skill_validation(cls, form):
        errors = []

        if len(form['bio']) < 10:
            errors.append("Please enter your bio!")

        return errors

    @classmethod
    def add_skill(cls, form, developer_id):
        skills = cls(
            languages=form['languages'],
            python = form['python'],
            html = form['html'],
            bio=form['bio'],
            developer_id = developer_id
        )
        db.session.add(skills)
        db.session.commit()

    @classmethod
    def get_all_skills(cls):
        return cls.query.all()

    @classmethod
    def get_user_skills(cls, user_id):
        get_user_all_skills = cls.query.filter_by(user_id = user_id).all()
        return get_user_all_skills

    @classmethod
    def get_skills(cls, skill_id):
        get_single_skill = cls.query.filter_by(id = skill_id).first()
        return get_single_skill

    @classmethod
    def delete_skills(cls, skill_id):
        get_single_skill = cls.query.get(skill_id)
        db.session.delete(get_single_skill)
        db.session.commit()

    @classmethod
    def edit_skills_info(cls, form, skill_id):
        skills_update = Skill.get(skill_id)
        skills_update.languages = form['languages']
        skills_update.bio = form['bio']
        db.session.commit()
        return skills_update


'''
Contains helper code related to logging.
'''
import logging

import requests

def get_easy_logger(name, level=None):
    ''' Create a logger with the given name and optionally a level. '''
    result = logging.getLogger(name)
    if level:
        result.setLevel(level)
    return result

class DiscordWebhookHandler(logging.Handler):
    ''' Logging handler that emits passed messages to a Discord webhook using
    JSON formatting. '''

    def __init__(self, webhook, *args, **kwargs):
        ''' Store parameters. '''
        self.webhook = webhook
        super().__init__(*args, **kwargs)

    level_emoji = {
        logging.DEBUG: ':bug:',
        logging.INFO: ':information_source:',
        logging.WARNING: ':warning:',
        logging.ERROR: ':no_entry_sign:',
        logging.CRITICAL: ':exclamation:'
    }
    ''' What emoji is used for what level. '''

    def mapLogRecord(self, record): # pylint: disable=invalid-name
        ''' Map log record to JSON dict to send, as in HTTPHandler. '''
        level = record.levelname
        emoji = self.level_emoji[record.levelno]
        exc_info = record.exc_info

        if not exc_info:
            logmsg = record.msg % record.args
            message = f'{emoji} {level}: {logmsg}'
        else:
            message = (f'{emoji} {level}:\n'
                       f'```\n{record.exc_text}\n```')
        return {'content': message}

    def emit(self, record):
        ''' Send data via POST-request. '''
        data = self.mapLogRecord(record)
        requests.post(self.webhook, json=data)


import json
from urllib import urlencode

from Products.CMFCore.utils import getToolByName
from Products.Five import BrowserView

from zope.component import getUtility

from org.bccvl.site.browser.ws import IALAService
from org.bccvl.site import defaults

class DatasetsImportView(BrowserView):
    """
    render the dataset import template
    """

    title = u"Discover Dataset"

    def parseRequest(self):
        params = {'action': None}
        if 'search' in self.request:
            params['action'] = 'search'
        if 'import' in self.request:
            params['action'] = 'import'
        for name in ('searchOccurrence_query',
                     'searchOccurrence_source',
                     'lsid', 'taxon', 'common'):
            if name in self.request:
                params[name] = self.request.get(name).decode('utf-8')
        return params

    def searchResults(self):
        if self.params.get('action') != 'search':
            return

        ala = getUtility(IALAService)
        try:
            ret = ala.searchjson(q=self.params['searchOccurrence_query'],
                                 fq='rank:species')
            result = json.load(ret)
        except:
            # TODO yield result with error
            return

        if 'searchResults' not in result:
            return
        if 'results' not in result['searchResults']:
            return
        for item in result['searchResults']['results']:
            search_words = self.params[
                'searchOccurrence_query'].lower().split()
            result = {
                'title': item['name'],
                'friendlyName': item['name'],
                'description': [],
                'actions': {},
            }
            if 'commonNameSingle' in item:
                result['title'] = '{} <i class="taxonomy">{}</i>'.format(
                    item['commonNameSingle'], result['title'])
                result['friendlyName'] = '{} {}'.format(
                    item['commonNameSingle'], result['friendlyName'])
            # and filter all searchwords
            resulttitle = result['title'].lower()
            if any(sw not in resulttitle for sw in search_words):
                continue
            # filter out results without occurrences
            if not item.get('occCount', 0) > 0:
                continue
            if item.get('rank'):
                result['description'].append('({})'.format(item['rank']))
            if item.get('occCount'):
                result['description'].append('{} occurrences from ALA'.format(
                    item['occCount']))
            result['description'] = ' '.join(result['description'])
            # prefer smallImage over thumbnail?
            if item.get('smallImageUrl'):
                result['thumbUrl'] = item['smallImageUrl']
            elif item.get('thumbnailUrl'):
                result['thumbUrl'] = item['thumbnailUrl']
            else:
                result['thumbUrl'] = ''
            # get actions
            if item.get('guid'):
                # TODO: uri path encode guid
                result['actions'][
                    'viz'] = 'https://bie-ws.ala.org.au/species/' + item['guid']
                params = urlencode({
                    'lsid': item['guid'],
                    'taxon': item['name'],
                    'common': item.get('commonNameSingle'),
                    'import': 'Import'})
                # TODO: need a way to generate ajax url?
                # TODO: can I use view name/id here?
                result['actions']['alaimport'] = self.context.absolute_url(
                ) + "/datasets_import_view?" + params
            yield result

    def __call__(self):
        # FIXME: write test: create dataset in correct location and
        #        test permissions to 'dv' view
        # FIXME: don't use parameter name lsid here, something more generic
        #        would be better, as only ala provides lsid and all other
        #        services use something else
        # TODO: implement non js-search, and import here
        #       -> reuse DataMover api endpoint
        self.params = self.parseRequest()
        if self.params.get('action') == 'import':
            portal = getToolByName(self.context,
                                   'portal_url').getPortalObject()
            # TODO: @@dv or getMultiAdapter would enusre we get the view
            view = portal.restrictedTraverse(
                '/'.join((defaults.DATASETS_FOLDER_ID,
                          defaults.DATASETS_SPECIES_FOLDER_ID,
                          'ala', 'dv', 'pullOccurrenceFromALA')))
            # create dataset and start import job
            view(lsid=self.params.get('lsid', None),
                 taxon=self.params.get('taxon', None),
                 dataSrc=self.params.get('searchOccurrence_source'),
                 common=self.params.get('common', None))
            # TODO: check return value for errors?
            # TODO: redirect to dataset or listing view?
            # everything went fine until here .. so let's redirect
            nexturl = portal[defaults.DATASETS_FOLDER_ID].absolute_url()
            self.request.response.redirect(nexturl)
            return
        return super(DatasetsImportView, self).__call__()

    # TODO: implement search: get list of data and feed template with it
    #       implement import: get selected lsid and title and do
    #                         what pullOccurrencesFromALA does


import os
import unittest
import requests
import pymongo

class BasicTests(unittest.TestCase):
    ipAdd = '18.206.119.216'

    def test_submitNote(self):
        response = requests.get('http://'+self.ipAdd+':5050/submitNote?keyword=Test Keyword&note=Test Note')
        self.assertEqual(response.status_code, 200)

    def test_retrieveNote(self):
        response = requests.get('http://'+self.ipAdd+':5050/retrieveNote?keyword=Test Keyword')
        self.assertEqual(response.status_code, 200)

if __name__ == "__main__":
    unittest.main()


"""
All logic regarding the Operation Modes (opmodes).

The opmode is defined based on the user's arguments and the available
resources of the host system
"""
import sys
import os
import logging
import wifiphisher.common.interfaces as interfaces
import wifiphisher.common.constants as constants
import wifiphisher.extensions.handshakeverify as handshakeverify

logger = logging.getLogger(__name__)

class OpMode(object):
    """
    Manager of the operation mode
    """

    def __init__(self):
        """
        Construct the class
        :param self: An OpModeManager object
        :type self: OpModeManager
        :return: None
        :rtype: None
        """

        self.op_mode = 0x0
        # True if the system only contains one phy interface
        self._is_one_phy_interface = False
        # The card which supports monitor and ap mode
        self._perfect_card = None

    def initialize(self, args):
        """
        Initialize the opmode manager
        :param self: An OpModeManager object
        :param args: An argparse.Namespace object
        :type self: OpModeManager
        :type args: argparse.Namespace
        :return: None
        :rtype: None
        """

        self._perfect_card, self._is_one_phy_interface =\
            interfaces.is_add_vif_required(args)
        self._check_args(args)

    def _check_args(self, args):
        """
        Checks the given arguments for logic errors.
        :param self: An OpModeManager object
        :param args: An argparse.Namespace object
        :type self: OpModeManager
        :type args: argparse.Namespace
        :return: None
        :rtype: None
        """

        if args.presharedkey and \
            (len(args.presharedkey) < 8 or
             len(args.presharedkey) > 64):
            sys.exit(
                '[' +
                constants.R +
                '-' +
                constants.W +
                '] Pre-shared key must be between 8 and 63 printable'
                'characters.')

        if args.handshake_capture and not os.path.isfile(
                args.handshake_capture):
            sys.exit('[' +
                     constants.R +
                     '-' +
                     constants.W +
                     '] handshake capture does not exist.')
        elif args.handshake_capture and not handshakeverify.\
                is_valid_handshake_capture(args.handshake_capture):
            sys.exit('[' +
                     constants.R +
                     '-' +
                     constants.W +
                     '] handshake capture does not contain valid handshake')

        if ((args.jamminginterface and not args.apinterface) or
                (not args.jamminginterface and args.apinterface)) and \
                not (args.nojamming and args.apinterface):
            sys.exit(
                '[' +
                constants.R +
                '-' +
                constants.W +
                '] --apinterface (-aI) and --jamminginterface (-jI)'
                '(or --nojamming (-nJ)) are used in conjuction.')

        if args.nojamming and args.jamminginterface:
            sys.exit(
                '[' +
                constants.R +
                '-' +
                constants.W +
                '] --nojamming (-nJ) and --jamminginterface (-jI)'
                'cannot work together.')

        if args.lure10_exploit and args.nojamming:
            sys.exit(
                '[' +
                constants.R +
                '-' +
                constants.W +
                '] --lure10-exploit (-lE) and --nojamming (-nJ)'
                'cannot work together.')

        if args.lure10_exploit and not os.path.isfile(
                constants.LOCS_DIR + args.lure10_exploit):
            sys.exit('[' +
                     constants.R +
                     '-' +
                     constants.W +
                     '] Lure10 capture does not exist. Listing directory'
                     'of captures: ' + str(os.listdir(constants.LOCS_DIR)))

        if (args.mac_ap_interface and args.no_mac_randomization) or \
                (args.mac_deauth_interface and args.no_mac_randomization):
            sys.exit(
                '[' +
                constants.R +
                '-' +
                constants.W +
                '] --no-mac-randomization (-iNM) cannot work together with'
                '--mac-ap-interface or --mac-deauth-interface (-iDM)')

        # if args.deauth_essid is set we need the second card to
        # do the frequency hopping
        if args.deauth_essid and self._is_one_phy_interface:
            print(
                '[' +
                constants.R +
                '!' +
                constants.W +
                '] Only one card was found. Wifiphisher will deauth only '
                'on the target AP channel')

    def set_opmode(self, args, network_manager):
        """
        Sets the operation mode.

        :param self: An OpModeManager object
        :param args: An argparse.Namespace object
        :param network_manager: A NetworkManager object
        :type self: OpModeManager
        :type args: argparse.Namespace
        :type network_manager: NetworkManager
        :return: None
        :rtype: None

        ..note: An operation mode resembles how the tool will best leverage
        the given resources.

        Modes of operation
        1) Advanced 0x1
          2 cards, 2 interfaces
          i) AP, ii) EM
        2) Advanced and Internet 0x2
          3 cards, 3 interfaces
          i) AP, ii) EM iii) Internet
        3) AP-only and Internet 0x3
          2 cards, 2 interfaces
          i) AP, ii) Internet
        4) AP-only 0x4
          1 card, 1 interface
          i) AP
        5) Advanced w/ 1 vif support AP/Monitor 0x5
          1 card, 2 interfaces
          i) AP, ii) Extensions
        6) Advanced and Internet w/ 1 vif support AP/Monitor 0x6
          2 cards, 3 interfaces
          i) AP, ii) Extensions, iii) Internet
        """

        if not args.internetinterface and not args.nojamming:
            if not self._is_one_phy_interface:
                self.op_mode = constants.OP_MODE1
                logger.info("Starting OP_MODE1 (0x1)")
            else:
                if self._perfect_card is not None:
                    network_manager.add_virtual_interface(
                        self._perfect_card)
                self.op_mode = constants.OP_MODE5
                logger.info("Starting OP_MODE5 (0x5)")
        if args.internetinterface and not args.nojamming:
            if not self._is_one_phy_interface:
                self.op_mode = constants.OP_MODE2
                logger.info("Starting OP_MODE2 (0x2)")
            else:
                if self._perfect_card is not None:
                    network_manager.add_virtual_interface(
                        self._perfect_card)
                self.op_mode = constants.OP_MODE6
                logger.info("Starting OP_MODE6 (0x6)")

        if args.internetinterface and args.nojamming:
            self.op_mode = constants.OP_MODE3
            logger.info("Starting OP_MODE3 (0x3)")
        if args.nojamming and not args.internetinterface:
            self.op_mode = constants.OP_MODE4
            logger.info("Starting OP_MODE4 (0x4)")

    def internet_sharing_enabled(self):
        """
        :param self: An OpModeManager object
        :type self: OpModeManager
        :return: True if we are operating in a mode that shares Internet
        access.
        :rtype: bool
        """

        return self.op_mode in [constants.OP_MODE2, constants.OP_MODE3]

    def advanced_enabled(self):
        """
        :param self: An OpModeManager object
        :type self: OpModeManager
        :return: True if we are operating in an advanced
        mode (a mode that leverages two network cards)
        :rtype: bool
        """

        return self.op_mode in [constants.OP_MODE1,
                                constants.OP_MODE2,
                                constants.OP_MODE5,
                                constants.OP_MODE6]

    def deauth_enabled(self):
        """
        :param self: An OpModeManager object
        :type self: OpModeManager
        :return: True if we are operating in a mode
        that deauth is enabled.
        :rtype: bool
        """

        return self.op_mode in [constants.OP_MODE1,
                                constants.OP_MODE2,
                                constants.OP_MODE5,
                                constants.OP_MODE6]

    def freq_hopping_enabled(self):
        """
        :param self: An OpModeManager object
        :type self: OpModeManager
        :return: True if we are separating the wireless cards
        for jamming and lunching AP.
        :rtype: bool
        ..note: MODE5 and MODE6 only use one card to do deauth and
        lunch ap so it is not allowed to do frequency hopping.
        """

        return self.op_mode in [constants.OP_MODE1,
                                constants.OP_MODE2]


import requests
from bs4 import BeautifulSoup

url = 'http://www.cgv.co.kr/common/showtimes/iframeTheater.aspx?areacode=01&theatercode=0056&date=20190812'
html = requests.get(url)
soup = BeautifulSoup(html.text, 'html.parser')
imax = soup.select_one('span.imax')
if(imax):
    imax = imax.find_parent('div', class_ = 'col-times')
    title = imax.select_one('div.info-movie > a > strong').text.strip()
    print(title + ' IMAX 예매가 열렸습니다')
else:
    print('IMAX 예매가 아직 열리지 않았습니다.')


import torch
import datetime
from data_processor import *
import copy
#return 형식 {'road': '1000?', "E": [3시간 데이터], "S": [3시간 데이터]}
so_E = [4.47, 1.05, 4.07, 5.06, 4.85, 2.45, 2.64, 1.77, 0.79, 1.21, 1.83,
2.87, 1.97, 7.87, 8.28, 6.68, 2.76, 7.23, 2.5, 1.83, 3.21, 0.74, 3.21, 2.92, 1.55,
1.85, 2.73, 2.78, 3.95, 1.14, 1.46, 5.35, 3.88, 5.9, 3.03, 3.21, 2.76, 6.16]
so_S = [6.96, 1.63, 4.35, 2.35, 5.8, 3.73, 5.37, 2.36, 0.41, 4.66, 2.07, 3.45,
0.9, 1.89, 2.94, 3.12, 0.66, 3.11, 2.16, 3.01, 6.51, 3.47, 6.19, 8.13, 7.3, 2.68, 2.73,
2.39, 1.21, 0.8, 1.02, 2.59, 2.55, 4.91, 4.54, 5.08, 0.59, 4.12]
sy_E = [0.99, 0.54, 2.2, 3.42, 7.3, 5.42, 12.78, 14.24, 9.09, 1.38, 3.93, 0.54, 16.33]
sy_S = [15.6, 1.26, 3.93, 0.59, 8.93, 14.48, 12.77, 5.33, 8.1, 2.62, 2.2, 1.34, 0.97]
ki_E = [3.9, 2.57, 3.7, 3.84, 2.54, 0.37, 1.92, 9.95]
ki_S = [9.45, 1.63, 1.17, 2.12, 3.76, 4.2, 2.57, 3.89]
yd_E = [4.85, 3.03, 2.7, 1.77, 3.18, 2.13, 5.6, 0.91, 2.05, 1.58, 2.91,
	5.87, 5.78, 4.14, 5.69, 8.02, 7.07, 5.37, 6.91, 8.01, 6.57, 19.89, 9.07, 4.9,
	1.87, 12.68, 17.45, 16.84, 5.74, 5.74, 9.47, 6.07, 13.21, 22.56]
yd_S = [21.85, 12.91, 6.58, 9.69, 5.73, 16.6, 17.15, 13, 2.32, 3.91, 9.49, 19.98, 5.34,
9.21, 6.27, 6.14, 6.83, 8.14, 2.23, 9.14, 4.2, 6.0, 2.81, 2.22, 1.54, 1.42, 4.79, 2.11, 3.27, 2.5, 2.69, 1.01, 6.87]
ws_E = [1.35, 1.04, 16.22, 2.31, 7.97, 24.33, 15.97, 2.82, 8.54, 9.33, 16.13, 14.01, 12.53,
8.06, 6.1, 9.4, 11.85, 8.71, 11.89, 19.63, 11.1, 13.59, 10.74, 6.31, 9.57, 8.16,
12.61, 6.34, 7.95, 13.43, 4.36, 3.4, 5.25, 1.92, 1.1, 3.22, 1.22, 2.4, 4.79]
ws_S = [3.76, 2.95, 1.24, 2.88, 1.89, 0.97, 6.23, 2.83, 4.17, 13.38, 7.59, 6.89, 12.65, 7.95, 9.1, 6.97, 10.68,
13.71, 10.81, 19.72, 11.9, 8.88, 11.8, 8.82, 6.44, 8.36, 12.59, 13.91, 16.19, 10.48, 7.31, 2.71, 16.07, 24.37, 7.44,
3.03, 16.64, 0.52, 1.87]
distance_dict = {'1000E': torch.tensor(so_E, dtype=torch.float32),
		'1000S': torch.tensor(so_S, dtype=torch.float32),
		'0600E': torch.tensor(sy_E, dtype=torch.float32),
		'0600S': torch.tensor(sy_S, dtype=torch.float32),
		'yd_E': torch.tensor(yd_E, dtype=torch.float32),
		'yd_S': torch.tensor(yd_S, dtype=torch.float32),
		'1200E': torch.tensor(ki_E, dtype=torch.float32),
		'1200S': torch.tensor(ki_S, dtype=torch.float32),
		'0150E': torch.tensor(ws_E, dtype=torch.float32),
		'0150S': torch.tensor(ws_S, dtype=torch.float32)}

file_list = ["lstm_1000_E.pt", "lstm_1000_S.pt", "lstm_0600_E.pt", "lstm_0600_S.pt",
"lstm_1200_E.pt", "lstm_1200_S.pt", "lstm_0150_E.pt", "lstm_0150_S.pt"]

#lstm = torch.load("lstm_0150_S.pt", map_location='cpu')

test_dict = {'road': '1000', 'direction': 'S', 'data':[100.35, 100.04, 16.22, 2.31, 7.97, 24.33, 15.97, 2.82, 8.54, 9.33, 16.13, 14.01, 12.53,
8.06, 6.1, 9.4, 11.85, 8.71, 11.89, 19.63, 11.1, 13.59, 10.74, 6.31, 9.57, 8.16,
12.61, 6.34, 7.95, 13.43, 4.36, 3.4, 5.25, 1.92, 1.1, 3.22, 1.22, 2.4]}
#input에 이어붙여서 추가 예측 ! --> 시간값도 바꿔서 넣기
def predictor(lstm, inp, d_inp, first_val, n_times=3):
	#lstm = torch.load("lstm_1000_E.pt", map_location='cpu')
	hidden = lstm.init_hidden()
	cell = lstm.init_hidden()
	#print(inp)
	mod_d_inp = d_inp.reshape(24, 6)
	#first_val = inp.reshape(24, -1)[-1][:]
	#print(inp.size())
	#print(first_val)
	#print(mod_d_inp.size())
	last_time_vec = copy.deepcopy(mod_d_inp[-1,:])#tensor's last vector deep copy
	last_hour = last_time_vec[-1]
	#print(torch.cat((last_time_vec[:-1], torch.tensor(last_hour+1, dtype=torch.float32).view(1))))
	all_output = []
	#
	mod_inp = inp.reshape(24, -1)
	for i in range(0, n_times):
		output, hidden, cell = lstm(inp, hidden, cell, d_inp)
		#print(output.size())
		#print(output[0][-1])#1*n
		last_hour += 1
		last_time_vec = torch.cat((last_time_vec[:-1], torch.tensor(last_hour, dtype=torch.float32).view(1)))
		mod_inp = torch.cat((mod_inp, output[0][-1].reshape(1,-1)), dim=0)
		#print(last_time_vec)
		#print(mod_d_inp)
		mod_d_inp = torch.cat((mod_d_inp, last_time_vec.reshape(1,-1)))
		#print(mod_d_inp)
		#print(mod_inp[i+1:].size())
		inp = mod_inp[i+1:].reshape(6,4,-1)#seq_len, batch, -1
		#print(inp)
		d_inp = mod_d_inp[i+1:].reshape(1,-1)
		all_output.append(output[0][-1])

	return_list = []
	return_list.append(list(first_val.detach().numpy()))
	for m in all_output:
		new_element = list(m.detach().numpy())
		return_list.append(new_element)
	#print(return_list)
	return_list = list(map(list, zip(*return_list)))
	return return_list
	#print(list(all_output[0].detach().numpy()))

def get_timenow(hol_date):
	#현재 기준 24시간 전까지
	_now = datetime.datetime.now()
	_now_str = _now.strftime("%Y.%m.%d %H")
	_now_date = _now_str[:-3]
	_now_time = int(_now_str[-2:])
	yesterday = (_now - datetime.timedelta(days=1)).strftime("%Y.%m.%d %H")
	yesterday_date = yesterday[:-3]
	yesterday_time = int(yesterday[-2:])

	now_tl = range(0, _now_time+1)
	yes_tl = range(yesterday_time+1, 24)
	#now_tl = range(0, 17)
	#yes_tl = range(17, 24)
	#hv = yes_tl + now_tl
	prev_hol_vec = holiday_vector(yesterday_date, hol_date, yes_tl)
	hol_vec = holiday_vector(_now_date, hol_date, now_tl)
	#hol_vec.append(_now_time)
	return_vec = torch.tensor(prev_hol_vec+hol_vec, dtype=torch.float32)
	#print(return_vec[-1][-1])
	#print(return_vec.size())
	return return_vec.reshape(1,-1)

def pre_processor(target_dict):
	with open("./holiday_date.txt", 'r') as f:
		hol_date = f.read()
		hol_date = hol_date.split('\n')
	road = target_dict['road']
	direction = target_dict['direction']
	data = target_dict['data']
	for i, m in enumerate(data):
		data[i] = float(m)
	data = torch.tensor(data, dtype=torch.float32).reshape(-1, 1)

	file_path = './data/'+road+'/'+direction+'/'+'past_data.csv'
	lstm = torch.load("lstm_"+road+"_"+direction+".pt", map_location='cpu')
	past_data = read_data(file_path)
	use_data = past_data[0:,17:40]#16시 기준
	#print(use_data)
	#print(use_data.size(), data.size())
	zero_v = torch.tensor([0], dtype=torch.float32)
	if data.size()[0] != use_data.size()[0]:
		#print("wow?")
		data = past_data[0:, 40].reshape(-1, 1)
	for i in data:
		if i.item() == 0:
			data = past_data[0:, 40].reshape(-1, 1)
			break
	#print(data.size())
	use_data = torch.cat((use_data, data), dim=1).transpose(0,1)#.reshape(6, 4, -1)

	#print('tq', use_data.size())
	time_data = distance_dict[road+direction]
	#print(time_data.size())
	use_data = torch.div(60*time_data, use_data)
	#print()
	first_val = use_data[-1][:]
	use_data = use_data.reshape(6,4,-1)#.transpose(0,1)

	#use_data = torch.cat((use_data, data), dim=0).reshape(6, 4, -1)
	#print(use_data.size())
	d_inp = get_timenow(hol_date)
	dict_template = target_dict#{"road":road, "direction":direction}
	return lstm, use_data, d_inp, dict_template, first_val

def change_format(result):
	for i,item in enumerate(result):
		for ii, m in enumerate(item):
			temp = round(m, 1)
			item[ii] = str(temp)
		result[i] = item
	return result

def main(target_dict):
	lstm, use_data, d_inp, result_dict, first_val = pre_processor(target_dict)
	result = predictor(lstm, use_data, d_inp, first_val)
	#print(result)
	result = change_format(result)
	result_dict['data'] = result
	return result_dict

if __name__ == '__main__':
	#lstm, use_data, d_inp = pre_processor(test_dict)
	x = main(test_dict)
	print(x)
	#print(use_data)
	#print(d_inp)
	'''
	with open("./holiday_date.txt", 'r') as f:
		hol_date = f.read()
		hol_date = hol_date.split('\n')
	test_data = read_data('./data/0150/test_data_S.csv')
	#get_timenow()
	test_time = "2018.11.20"#2018.09.28
	tt2 = "2018.11.19"#2018.09.29
	tl_list2 = range(17, 24)
	tl_list1 = range(0,17)
	prev_h = holiday_vector(tt2, hol_date, tl_list2)
	now_h = holiday_vector(test_time, hol_date, tl_list1)
	prev_h.extend(now_h)
	hv = torch.tensor(prev_h, dtype=torch.float32).reshape(1, -1)
	#hv = get_timenow(hol_date)
	#print(hv)
	inp = test_data[0:,1193:1217]#38*24
	#print(inp)
	inp = torch.tensor(inp).transpose(0,1).reshape(6,4,-1)
	r_inp = torch.div(60*distance_dict['ws_S'], inp)
	predictor(r_inp, hv)'''


from collections import OrderedDict

class Solution11:
    # Greedy + 2 pointers
    # Idea: if we move the lower one, total area might increase
    # However, if we move the higher one, total area most decrease
    def maxArea(self, height: List[int]) -> int:
        sp, ep = 0, len(height)-1
        area = 0
        while sp < ep:
            area = max(min(height[sp], height[ep]) * (ep-sp), area)
            if height[sp] > height[ep]:
                ep -= 1
            else:
                sp += 1
        return area

class Solution15:
    # 2 pointers at the twoSum helper function
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        ans, d = [], OrderedDict()
        nums.sort()
        for n in nums:
            if n in d:
                d[n] += 1
            else:
                d[n] = 1

        for n, cnt in d.items():
            if cnt >= 3 and n * 3 == 0:
                ans.append([n] * 3)
            if cnt >= 2:
                target = n*-2
                if target in d and target != n:
                    ans.append([n]*2 + [target])
            if cnt >= 1:
                two_sum_ans = self.twoSum(d, n)
                ans += two_sum_ans
        return ans

    def twoSum(self, d: dict[int, int], c: int) -> List[List[int]]:
        ans, target = [], c * -1
        nums = [k for k in d.keys() if k > c]
        s, e = 0, len(nums) - 1
        while s < e:
            s_val = nums[s]
            e_val = nums[e]
            curr_val = s_val + e_val
            if curr_val == target:
                ans.append([c, s_val, e_val])
                s, e = s+1, e-1
            elif curr_val < target:
                s += 1
            else :
                e -= 1
        return ans


"""
__author__ = 'LZL'
# code is far away from bugs with the god animal protecting
    I love animals. They taste delicious.
              ┏┓      ┏┓
            ┏┛┻━━━┛┻┓
            ┃      ☃      ┃
            ┃  ┳┛  ┗┳  ┃
            ┃      ┻      ┃
            ┗━┓      ┏━┛
                ┃      ┗━━━┓
                ┃  神兽保佑    ┣┓
                ┃　永无BUG！   ┏┛
                ┗┓┓┏━┳┓┏┛
                  ┃┫┫  ┃┫┫
                  ┗┻┛  ┗┻┛
"""

from public.pages.yz_pages.home_page import HomePage
from BeautifulReport import BeautifulReport
from public.util.util_ini import UtilIni
from selenium import webdriver
import unittest
import logging
import os

class SearchTest(unittest.TestCase):
    '''
        用例：搜索功能
    '''

    def save_img(self, img_name):
        """
        ## 为了让BeautifulReport进行自动的失败截图，必须在测试类定义该save_img方法
          传入一个img_name, 并存储到默认的图片保存路径：当前项目的img文件夹，必须存在该文件夹；
        :param img_name: 图片名称，无需后缀
        :return:null
        """
        self.driver.get_screenshot_as_file('{}/{}.png'.format(os.path.abspath('img'), img_name))

    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
        cls.homepage = HomePage(cls.driver)
        UtilIni().get_OperationLog()

    @BeautifulReport.add_test_img('test_1')
    def test_1(self):
        '''
         纯数字搜索
        '''
        search_content = '1'
        self.homepage.open()
        self.homepage.search_content(search_content)
        self.homepage.do_search()
        try:
            self.homepage.is_success(search_content)
        except AssertionError:
            logging.warning('纯数字搜索，结果对比错误')
            raise AssertionError

    @BeautifulReport.add_test_img('test_2')
    def test_2(self):
        '''
        纯英文搜索
        '''
        search_content = 'one'
        self.homepage.open()
        self.homepage.search_content(search_content)
        self.homepage.do_search()
        try:
            self.homepage.is_success(search_content)
        except AssertionError:
            logging.warning('纯英文搜索，结果对比错误')
            raise AssertionError

    @BeautifulReport.add_test_img('test_3')
    def test_3(self):
        '''
        纯中文搜索
        '''
        search_content = '中国'
        self.homepage.open()
        self.homepage.search_content(search_content)
        self.homepage.do_search()
        try:
            self.homepage.is_success(search_content)
        except AssertionError:
            logging.warning('纯中文搜索，结果对比错误')
            raise AssertionError

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


import random as rand
import time
from collections import deque

def creategraph(length):
    E = {(0,0)}
    que = [(0,0)]
    G = {}
    for x in range(length):
        for y in range(length):
            G[(x,y)] = []
    while len(que) > 0:
        exploring = que[-1]
        explorable = []
        nextnode = (exploring[0]-1, exploring[1])
        if nextnode not in E and nextnode in G:
            explorable.append(nextnode)
        nextnode = (exploring[0]+1, exploring[1])
        if nextnode not in E and nextnode in G:
            explorable.append(nextnode)
        nextnode = (exploring[0], exploring[1]-1)
        if nextnode not in E and nextnode in G:
            explorable.append(nextnode)
        nextnode = (exploring[0], exploring[1]+1)
        if nextnode not in E and nextnode in G:
            explorable.append(nextnode)
        if len(explorable) > 0:
            adding= rand.choice(explorable)
            G[exploring].append(adding)
            G[adding].append(exploring)
            que.append(adding)
            E.add(adding)
        else:
            que.pop()
    return G

def BFS_with_distance(G, s):

    dist = {s:0}

    Q = deque([s])

    while len(Q) > 0:
        V = Q.popleft()
        for node in G[V]:
            if node not in dist:
                dist[node] = dist[V] + 1
                Q.append(node)

    return dist

import pygame
pygame.font.init()
# Define the background colour
# using RGB color coding.
background_colour = (255, 255, 255)

# Define the dimensions of
# screen object(width,height)
screen = pygame.display.set_mode((1000, 800))

# Set the caption of the screen
pygame.display.set_caption('G')
alltimes =[]
#time.sleep(10)
for graphs in range(10):

    length = 15
    G = creategraph(length)
    dist = BFS_with_distance(G, (0,0))
    furthest = (0, 0)
    for y in range(length):
            if dist[(length-1,y)] > furthest[0]:
                furthest = (dist[(length-1,y)], (length-1,y), (25, 0))
    for x in range(length):
        if dist[(x,length-1)] > furthest[0]:
                furthest = (dist[(x,length-1)], (x, length-1), (0, 25))

    # Fill the background colour to the screen
    screen.fill(background_colour)

    # Update the display using flip
    pygame.display.flip()

    offset = 50
    scale = 50
    rect = (25,25,length*50,length*50)
    pygame.draw.rect(screen, (0,0,0), rect)
    for loc in G:
        pygame.draw.circle(screen, (255,255,255), (loc[0]*50+50, loc[1]*50+50), 10)

        for end in G[loc]:
            pygame.draw.line(screen, (255, 255, 255),
                             (end[0]*scale+offset, end[1]*scale+offset),
                             (loc[0]*scale+offset, loc[1]*scale+offset), 22)
        pygame.draw.circle(screen, (0,0,255), (loc[0]*50+50, loc[1]*50+50), 5)
    pygame.draw.line(screen, (255, 255, 255), (0, 50), (50, 50), 22)
    pygame.draw.line(screen, (255,255,255),
                     (furthest[1][0]*scale+offset+furthest[2][0], furthest[1][1]*scale+offset+furthest[2][1]),
                     (furthest[1][0]*scale+offset, furthest[1][1]*scale+offset), 22)

    running = True
    num =1
    E = [{(0,0)}]
    que = [[(0,0)]]
    locations = [(0,0)]

    count = 0
    timeadded = 0
    bouncetime = 0
    while running:
        pygame.draw.rect(screen, (0,0,0), rect)
        count += 1
        if count % 10 == 0:
            E.append({0,0})
            que.append([(0,0)])
            locations.append([(0,0)])
        for loc in G:
            pygame.draw.circle(screen, (255,255,255), (loc[0]*50+50, loc[1]*50+50), 10)
            for end in G[loc]:
                pygame.draw.line(screen, (255, 255, 255), (end[0]*50+50, end[1]*50+50), (loc[0]*50+50, loc[1]*50+50), 22)
        pygame.draw.line(screen, (255, 255, 255), (0, 50), (50, 50), 22)
        pygame.draw.line(screen, (255,255,255), (furthest[1][0]*scale+offset+furthest[2][0], furthest[1][1]*scale+offset+furthest[2][1]), (furthest[1][0]*scale+offset, furthest[1][1]*scale+offset), 22)

        for i in range(len(que)):
            if len(que[i]) > 0:

                exploring = que[i][-1]

                pygame.draw.circle(screen, (0,255,0), (exploring[0]*50+50, exploring[1]*50+50), 10)
                pygame.display.update()
                if exploring == furthest[1]:
                    que[i] = []
                    print(f"Person {i} finished at {count} in {count-i*10} steps")
                    continue
                for person in range(len(que)):
                    if exploring == locations[person] and person != i:
                        #print(i, person, exploring)
                        added = False
                        for node in E[person]:
                            if node not in E[i]:
                                E[i].add(node)
                                added = True
                        for node in E[i]:
                            if node not in E[person]:
                                E[person].add(node)
                                added = True
                        if added:
                            pygame.draw.circle(screen, (255,0,0),
                                               (exploring[0]*50+50, exploring[1]*50+50), 15)
                            time.sleep(0.02)

                explorable = []
                for new in G[exploring]:
                    if new not in E[i]:
                        explorable.append(new)
                if len(explorable) > 0:
                    adding= rand.choice(explorable)
                    que[i].append(adding)
                    E[i].add(adding)
                    locations[i] = adding

                else:
                    que[i].pop()
        time.sleep(0.01)

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


from collections import OrderedDict
class Solution:
    def firstUniqChar(self, s: str) -> int:
        dict1 = OrderedDict()
        for ele in s:
            if ele in dict1:
                dict1[ele] = dict1[ele] + 1
            else:
                dict1[ele] = 1
        ele = -1
        for key,values in dict1.items():
            if values == 1:
                ele = key
                break
        if ele == -1:
            return -1
        for i in range(0,len(s)):
            if s[i] == ele:
                return i
        return -1