#coding:utf-8
import random
from cocos.sprite import Sprite
from src.block import Block

class Level(object):
    def __init__(self, path='levelfile'):
        super(Level, self).__init__()
        self.levels = 1
        self.path = path
        self.blocks_props = []
        self.blocks = []

    def load(self):
        '''加载关卡数据'''
        self.blocks_props.clear()
        path = self.path + '\level' + str(self.levels) + '.txt'
        try:
            with open(path, 'r') as f:
                lines = f.readlines()
                for b in lines:
                    prop = b.split(', ')
                    if len(prop) < 3:
                        prop.append('0')
                    x = int(prop[0])
                    y = int(prop[1])
                    live = int(prop[2])
                    self.blocks_props.append((x, y, live))
                return True
        except:
            return False

    def reset(self):
        '''生成blocks'''
        self.load()
        self.blocks = []
        blocks_props = self.blocks_props
        number_of_blocks = len(blocks_props)
        for i in range(number_of_blocks):
            x, y, live = blocks_props[i]
            b = Block()
            bx = int(x)
            by = int(y)
            b.sprite.position = (bx, by)
            b.live = live
            b.reset()
            self.blocks.append(b)

    def next(self):
        '''载入下一关的坐标'''
        self.levels += 1
        return self.load()


#!/usr/bin/env python3
"""Task 1 Keras"""
import tensorflow.keras as K

def build_model(nx, layers, activations, lambtha, keep_prob):
    """Builds a neural network with the Keras library"""
    input = K.Input((nx, ))
    regularizer = K.regularizers.L2(lambtha)
    for i in range(len(layers)):
        if i == 0:
            prev = K.layers.Dense(layers[i], activations[i],
                                  kernel_regularizer=regularizer)(input)
        else:
            prev = (K.layers.Dense(layers[i], activations[i],
                                   kernel_regularizer=regularizer))(prev)
        if i != len(layers) - 1:
            prev = K.layers.Dropout(1 - keep_prob)(prev)

    model = K.Model(inputs=input, outputs=prev)

    return model


"""command_log

Revision ID: 14139f2c6c48
Revises: 545e262bdb51
Create Date: 2021-05-05 15:25:07.180186

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '14139f2c6c48'
down_revision = '545e262bdb51'
branch_labels = None
depends_on = None

def upgrade():
    op.create_table(
        'command_log',
        sa.Column('id', sa.Integer, primary_key=True),
        sa.Column('user_id', sa.Integer, nullable=False),
        sa.Column('created_at', sa.Integer, nullable=False),
        sa.Column('command_type', sa.Integer(), nullable=False),
        sa.Column('command', sa.String(255), nullable=False)
    )
    op.create_index('idx-command_log-user_id-created_at', 'command_log', ['user_id', 'created_at'])
    op.create_foreign_key(
        'fk-command_log-user',
        'command_log', 'user',
        ['user_id'], ['id'],
        onupdate='restrict',
        ondelete='cascade'
    )

def downgrade():
    op.drop_table('command_log')


from gearbox.migrations import Migration

class AddTableStoBal(Migration):

    database = "mobile"

    def up(self):
        t = self.table('StoBal', area="Sta_Data_128_2", multitenant="yes", dump_name="stobal", desc='''Article's Balance in certain stock
''')
        t.column('Stobal', 'character', format="x(8)", initial="", max_width=16, label="Stock", column_label="Stock", position=2, order=10, help="Stock Code")
        t.column('SimArt', 'character', format="x(12)", initial="", max_width=24, label="Sim Article", column_label="Sim Article", position=3, order=20, help="Article Code for a SIM type")
        t.column('DetBal', 'integer', format=">,>>>,>>9", initial="0", max_width=200, label="Balance", column_label="Balance", extent=10, position=4, order=30, help="Balance")
        t.column('Balance', 'integer', format="->,>>>,>>9", initial="0", max_width=4, label="TotalBal", column_label="Total Bal", position=5, order=40, help="Total Balance of article in this stock")
        t.column('OrdPoint', 'integer', format=">,>>>,>>9", initial="0", max_width=4, label="OrderPoint", column_label="OrderPoint", position=6, order=50, help="Ordering Point")
        t.column('Brand', 'character', format="x(8)", initial="", max_width=16, label="BrCode", column_label="BrCode", position=7, order=60, help="Code Of Brand")
        t.index('Stobal', [['Brand'], ['Stobal'], ['SimArt']], area="Sta_Index_3", primary=True, unique=True)
        t.index('SimArt', [['Brand'], ['SimArt'], ['Stobal']], area="Sta_Index_3", unique=True)

    def down(self):
        self.drop_table('StoBal')


"""View module for handling requests about park areas"""
from django.http import HttpResponseServerError
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework import status
from django.contrib.auth.models import User
from musicapp.models import Album, UserSpotify
from rest_framework.decorators import action

class AlbumSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Album
        url = serializers.HyperlinkedIdentityField(
            view_name='album',
            lookup_field='id'
        )
        fields = ('id', 'album_artist', 'album_name', 'album_image', 'album_rating', 'album_id', 'user', 'user_id')
        depth = 2

class Albums(ViewSet):

    def create(self, request):

        # user = User.objects.get(pk=request.auth.user.id)

        new_album = Album()
        new_album.user = request.auth.user
        # new_album.user_id = request.data["user_id"]
        new_album.album_artist = request.data["album_artist"]
        new_album.album_name = request.data["album_name"]
        new_album.album_image = request.data["album_image"]
        new_album.album_rating = request.data["album_rating"]
        new_album.album_id = request.data["album_id"]

        new_album.save()

        serializer = AlbumSerializer(
            new_album, context={'request': request})

        return Response(serializer.data)

    def retrieve(self, request, pk=None):

        try:
            album = Album.objects.get(pk=pk)
            serializer = AlbumSerializer(
                album, context={'request': request})
            return Response(serializer.data)
        except Exception as ex:
            return HttpResponseServerError(ex)

    def list(self, request):

        albums = Album.objects.filter(user=request.auth.user)
        serializer = AlbumSerializer(
            albums,
            many=True,
            context={'request': request}
        )
        return Response(serializer.data)

    def destroy(self, request, pk=None):

        try:
            album = Album.objects.get(pk=pk)
            album.delete()

            return Response({}, status=status.HTTP_204_NO_CONTENT)

        except Album.DoesNotExist as ex:
            return Response({'message': ex.args[0]}, status=status.HTTP_404_NOT_FOUND)

        except Exception as ex:
            return Response({'message': ex.args[0]}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def update(self, request, pk=None):

        album = Album.objects.get(pk=pk)
        # album.album_artist = request.data["album_artist"]
        # album.album_name = request.data["album_name"]
        # album.album_image = request.data["album_image"]
        album.album_rating = request.data["album_rating"]
        album.save()
        serializer = AlbumSerializer(
                album, context={'request': request})

        return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)


import sys
from collections import deque
input = sys.stdin.readline
INF = int(1e9)

t = int(input())
def dijkstra(start):
    q = deque()
    q.append((0,start,0))
    distance[start][0] = 0
    while q:
        dist,now,money = q.popleft()
        if distance[now][money] < dist:
            continue
        for i in graph[now]:
            cost = dist + i[1]
            price = money + i[2]
            if price > m:
                continue
            if cost < distance[i[0]][price]:
                distance[i[0]][price] = cost
                q.append((cost,i[0],price))

for _ in range(t):
    n,m,k = map(int,input().split())
    distance = [[INF]*(m+1) for _ in range(n+1)]
    graph = [[] for _ in range(n+1)]
    for _ in range(k):
        u,v,c,d = map(int,input().split())
        graph[u].append((v,d,c))
    dijkstra(1)
    if min(distance[n]) != INF:
        print(min(distance[n]))
    else:
        print('Poor KCM')


from __future__ import annotations
import os
from typing import NamedTuple, List
import multiprocessing as mp
from pathlib import Path
import warnings
import numpy as np
import pandas as pd
from tqdm.autonotebook import tqdm
from lobsterpy.featurize.core import (
    FeaturizeLobsterpy,
    FeaturizeCharges,
    FeaturizeCOXX,
)

warnings.filterwarnings("ignore")

class BatchSummaryFeaturizer:
    """
    Batch Featurizer sets that generates summary features from lobster data.

    Args:
        path_to_lobster_calcs: path to root directory consisting of all lobster calc
        path_to_jsons: path to root directory consisting of all lobster lightweight jsons
        feature_type: set the feature type for moment features.
        Possible options are "bonding", "antibonding" or "overall"
        charge_type : set charge type used for computing ionicity. Possible options are
        "mulliken", "loewdin or "both"
        bonds: "all_bonds" or "cation_anion_bonds"
        include_cobi_data : bool stating to include COBICAR.lobster features
        include_coop_data: bool stating to include COOPCAR.lobster features
        e_range : range of energy relative to fermi for which moment features needs to be computed
        n_jobs : parallel processes to run

    Attributes:
        get_df: A pandas dataframe with summary features
    """

    def __init__(
        self,
        path_to_lobster_calcs: str,
        path_to_jsons: str | None = None,
        feature_type: str = "antibonding",
        charge_type: str = "both",
        bonds: str = "all",
        include_cobi_data: bool = False,
        include_coop_data: bool = False,
        e_range: List[float] = [-5.0, 0.0],
        n_jobs: int = 4,
    ):
        self.path_to_lobster_calcs = path_to_lobster_calcs
        self.path_to_jsons = path_to_jsons
        self.feature_type = feature_type
        self.charge_type = charge_type
        self.bonds = bonds
        self.include_cobi_data = include_cobi_data
        self.include_coop_data = include_coop_data
        self.e_range = e_range
        self.n_jobs = n_jobs

    def _featurizelobsterpy(self, file_name_or_path) -> pd.DataFrame:
        """
        Wrapper method to featurize Lobsterpy condensed bonding analysis data by loading lightweight json
        if json file exists or invokes lobsterpy.analzye.Analysis module

        Returns:
            A pandas dataframe with ICOHP stats like mean, min, max of relevant bonds and
            madelung energies

        """
        if Path(file_name_or_path).is_file():
            featurize_lobsterpy = FeaturizeLobsterpy(
                path_to_json=file_name_or_path,
                bonds=self.bonds,
            )

        else:
            featurize_lobsterpy = FeaturizeLobsterpy(
                path_to_lobster_calc=file_name_or_path,
                bonds=self.bonds,
            )

        df = featurize_lobsterpy.get_df()

        return df

    def _featurizecoxx(self, path_to_lobster_calc) -> pd.DataFrame:
        """
        Wrapper method to featurize COHP/COBI/COOPCAR data that uses FeaturizeCOXX under the hood

        Returns:
            A pandas dataframe with COHP summary stats data mainly weighted ICOHP/ICOOP/ICOBI,
            Effective interaction number and moment features (center, width, skewness and kurtosis)

        """
        dir_name = Path(path_to_lobster_calc)

        req_files = {
            "structure_path": "POSCAR",
            "coxxcar_path": "COHPCAR.lobster",
            "icoxxlist_path": "ICOHPLIST.lobster",
        }
        for file, default_value in req_files.items():
            file_path = dir_name / default_value
            req_files[file] = file_path  # type: ignore
            if not file_path.exists():
                gz_file_path = file_path.with_name(file_path.name + ".gz")
                if gz_file_path.exists():
                    req_files[file] = gz_file_path  # type: ignore

        coxxcar_path = req_files.get("coxxcar_path")
        structure_path = req_files.get("structure_path")
        icoxxlist_path = req_files.get("icoxxlist_path")

        if (
            coxxcar_path.exists()  # type: ignore
            and structure_path.exists()  # type: ignore
            and icoxxlist_path.exists()  # type: ignore
        ):
            coxx = FeaturizeCOXX(
                path_to_coxxcar=str(coxxcar_path),
                path_to_icoxxlist=str(icoxxlist_path),
                path_to_structure=str(structure_path),
                feature_type=self.feature_type,
                e_range=self.e_range,
            )

            df_cohp = coxx.get_summarized_coxx_df()
        else:
            raise Exception(
                "COHPCAR.lobster or POSCAR or ICOHPLIST.lobster file "
                "not found in {}".format(dir_name.name)
            )

        if self.include_cobi_data:
            req_files = {
                "coxxcar_path": "COBICAR.lobster",
                "icoxxlist_path": "ICOBILIST.lobster",
            }
            for file, default_value in req_files.items():
                file_path = dir_name / default_value
                req_files[file] = file_path  # type: ignore
                if not file_path.exists():
                    gz_file_path = file_path.with_name(file_path.name + ".gz")
                    if gz_file_path.exists():
                        req_files[file] = gz_file_path  # type: ignore

            coxxcar_path = req_files.get("coxxcar_path")
            icoxxlist_path = req_files.get("icoxxlist_path")

            if coxxcar_path.exists() and icoxxlist_path.exists():  # type: ignore
                coxx = FeaturizeCOXX(
                    path_to_coxxcar=str(coxxcar_path),
                    path_to_icoxxlist=str(icoxxlist_path),
                    path_to_structure=str(structure_path),
                    feature_type=self.feature_type,
                    e_range=self.e_range,
                    are_cobis=True,
                )

                df_cobi = coxx.get_summarized_coxx_df()

            else:
                raise Exception(
                    "COBICAR.lobster or ICOBILIST.lobster file "
                    "not found in {}".format(dir_name.name)
                )

        if self.include_coop_data:
            req_files = {
                "coxxcar_path": "COOPCAR.lobster",
                "icoxxlist_path": "ICOOPLIST.lobster",
            }
            for file, default_value in req_files.items():
                file_path = dir_name / default_value
                req_files[file] = file_path  # type: ignore
                if not file_path.exists():
                    gz_file_path = file_path.with_name(file_path.name + ".gz")
                    if gz_file_path.exists():
                        req_files[file] = gz_file_path  # type: ignore

            coxxcar_path = req_files.get("coxxcar_path")
            icoxxlist_path = req_files.get("icoxxlist_path")

            if coxxcar_path.exists() and icoxxlist_path.exists():  # type: ignore
                coxx = FeaturizeCOXX(
                    path_to_coxxcar=str(coxxcar_path),
                    path_to_icoxxlist=str(icoxxlist_path),
                    path_to_structure=str(structure_path),
                    feature_type=self.feature_type,
                    e_range=self.e_range,
                    are_coops=True,
                )

                df_coop = coxx.get_summarized_coxx_df()

            else:
                raise Exception(
                    "COOPCAR.lobster or ICOOPLIST.lobster file "
                    "not found in {}".format(dir_name.name)
                )

        if self.include_cobi_data and self.include_coop_data:
            df = pd.concat([df_cohp, df_cobi, df_coop], axis=1)
        elif self.include_cobi_data and not self.include_coop_data:
            df = pd.concat([df_cohp, df_cobi], axis=1)
        elif not self.include_cobi_data and self.include_coop_data:
            df = pd.concat([df_cohp, df_coop], axis=1)
        else:
            df = df_cohp

        return df

    def _featurizecharges(self, path_to_lobster_calc) -> pd.DataFrame:
        """
        Wrapper method to featurize CHARGE.lobster.gz data that uses FeaturizeCharges under the hood

        Returns:
            A pandas dataframe with computed ionicity for the structure

        """
        dir_name = Path(path_to_lobster_calc)

        req_files = {
            "charge_path": "CHARGE.lobster",
            "structure_path": "POSCAR",
        }
        for file, default_value in req_files.items():
            file_path = dir_name / default_value
            req_files[file] = file_path  # type: ignore
            if not file_path.exists():
                gz_file_path = file_path.with_name(file_path.name + ".gz")
                if gz_file_path.exists():
                    req_files[file] = gz_file_path  # type: ignore

        charge_path = req_files.get("charge_path")
        structure_path = req_files.get("structure_path")

        if charge_path.exists() and structure_path.exists():  # type: ignore
            if self.charge_type == "mulliken":
                charge_mull = FeaturizeCharges(
                    path_to_charge=str(charge_path),
                    path_to_structure=str(structure_path),
                    charge_type="mulliken",
                )
                df = charge_mull.get_df()
            elif self.charge_type == "loewdin":
                charge_loew = FeaturizeCharges(
                    path_to_charge=str(charge_path),
                    path_to_structure=str(structure_path),
                    charge_type="loewdin",
                )
                df = charge_loew.get_df()
            elif self.charge_type == "both":
                charge_mull = FeaturizeCharges(
                    path_to_charge=str(charge_path),
                    path_to_structure=str(structure_path),
                    charge_type="mulliken",
                )
                df_mull = charge_mull.get_df()

                charge_loew = FeaturizeCharges(
                    path_to_charge=str(charge_path),
                    path_to_structure=str(structure_path),
                    charge_type="loewdin",
                )
                df_loew = charge_loew.get_df()

                df = pd.concat([df_mull, df_loew], axis=1)

            return df
        else:
            raise Exception(
                "CHARGE.lobster or POSCAR not found in {}".format(dir_name.name)
            )

    def get_df(self) -> pd.DataFrame:
        """
        This method will return a pandas dataframe with summary features extracted from LOBSTER files
        as columns. Uses multiprocessing to speed up the process.

        Returns:
            Returns a pandas dataframe

        """
        if self.path_to_jsons:
            file_name_or_path = [
                os.path.join(self.path_to_jsons, f)
                for f in os.listdir(self.path_to_jsons)
                if not f.startswith("t")
                and not f.startswith(".")
                and not os.path.isdir(f)
            ]

        elif self.path_to_lobster_calcs and not self.path_to_jsons:
            file_name_or_path = [
                os.path.join(self.path_to_lobster_calcs, f)
                for f in os.listdir(self.path_to_lobster_calcs)
                if not f.startswith("t")
                and not f.startswith(".")
                and os.path.isdir(os.path.join(self.path_to_lobster_calcs, f))
            ]

        with mp.Pool(processes=self.n_jobs, maxtasksperchild=1) as pool:
            results = tqdm(
                pool.imap_unordered(
                    self._featurizelobsterpy, file_name_or_path, chunksize=1
                ),
                total=len(file_name_or_path),
                desc="Generating LobsterPy summary stats",
            )
            pool.close()
            pool.join()
            row = []
            for result in results:
                row.append(result)

        df_lobsterpy = pd.concat(row)
        df_lobsterpy.sort_index(inplace=True)

        paths = [
            os.path.join(self.path_to_lobster_calcs, f)
            for f in os.listdir(self.path_to_lobster_calcs)
            if not f.startswith("t")
            and not f.startswith(".")
            and os.path.isdir(os.path.join(self.path_to_lobster_calcs, f))
        ]

        with mp.Pool(processes=self.n_jobs, maxtasksperchild=1) as pool:
            results = tqdm(
                pool.imap_unordered(self._featurizecoxx, paths, chunksize=1),
                total=len(paths),
                desc="Generating COHP/COOP/COBI summary stats",
            )
            pool.close()
            pool.join()
            row = []
            for result in results:
                row.append(result)

        df_coxx = pd.concat(row)
        df_coxx.sort_index(inplace=True)

        with mp.Pool(processes=self.n_jobs, maxtasksperchild=1) as pool:
            results = tqdm(
                pool.imap_unordered(self._featurizecharges, paths, chunksize=1),
                total=len(paths),
                desc="Generating charge based features",
            )
            pool.close()
            pool.join()
            row = []
            for result in results:
                row.append(result)

        df_charges = pd.concat(row)
        df_charges.sort_index(inplace=True)

        df = pd.concat([df_lobsterpy, df_coxx, df_charges], axis=1)

        return df

class BatchCoxxFingerprint:
    """
    BatchFeaturizer that generates COHP/COOP/COBI fingerprints and
    Tanimoto index similarity matrix from fingerprint objects.

    Args:
        path_to_lobster_calcs: path to root directory consisting of all lobster calc
        feature_type: set the feature type for moment features.
        Possible options are "bonding", "antibonding" or "overall"
        label_list: bond labels list for which fingerprints needs to be generated.
        tanimoto : bool to state to compute tanimoto index betweeen fingerprint objects
        normalize: bool to state to normalize the fingerprint data
        n_bins: sets number for bins for fingerprint objects
        e_range : range of energy relative to fermi for which moment features needs to be computed
        n_jobs : number of parallel processes to run
        fingerprint_for: Possible options are 'cohp/cobi/coop'.
        Based on this fingerprints will be computed for COHPCAR/COOBICAR/COOPCAR.lobster files

    Attributes:
        fingerprint_df: A pandas dataframe with fingerprint objects
        get_similarity_matrix_df: A symmetric pandas dataframe consisting of
        similarity index (tanimoto/normalized dot product/dot product)
        computed between all pairs of compunds
    """

    def __init__(
        self,
        path_to_lobster_calcs: str,
        feature_type: str = "overall",
        label_list: List[str] | None = None,
        tanimoto: bool = True,
        normalize: bool = True,
        spin_type: str = "summed",
        n_bins: int = 56,
        e_range: List[float] = [-15.0, 0.0],
        n_jobs=4,
        fingerprint_for: str = "cohp",
    ):
        self.path_to_lobster_calcs = path_to_lobster_calcs
        self.feature_type = feature_type
        self.tanimoto = tanimoto
        self.normalize = normalize
        self.label_list = label_list
        self.spin_type = spin_type
        self.n_bins = n_bins
        self.e_range = e_range
        self.n_jobs = n_jobs
        self.fingerprint_for = fingerprint_for

        self.fingerprint_df = self._get_fingerprints_df()

    def get_similarity_matrix_df(self) -> pd.DataFrame:
        """
        This function will compute pairwise similarity index for each fingerprint object in input dataframe

        Returns:
             A Pandas dataframe
        """
        matrix = np.full(
            (self.fingerprint_df.shape[0], self.fingerprint_df.shape[0]), np.nan
        )
        for i, (row, col) in enumerate(self.fingerprint_df.iterrows()):
            for j, (row1, col1) in enumerate(self.fingerprint_df.iterrows()):
                if self.tanimoto:
                    simi = self._get_fp_similarity(
                        col["COXX_FP"],
                        col1["COXX_FP"],
                        tanimoto=self.tanimoto,
                        normalize=False,
                    )
                else:
                    simi = self._get_fp_similarity(
                        col["COXX_FP"],
                        col1["COXX_FP"],
                        tanimoto=False,
                        normalize=True,
                    )
                matrix[i][j] = simi

        df = pd.DataFrame(
            matrix,
            index=list(self.fingerprint_df.index),
            columns=list(self.fingerprint_df.index),
        )

        return df

    @staticmethod
    def _fp_to_dict(fp) -> dict:
        """
        Converts a fingerprint into a dictionary

        Args:
            fp: The fingerprint to be converted into a dictionary

        Returns:
            dict: A dict of the fingerprint Keys=type, Values=np.ndarray(energies, cohp)
        """
        fp_dict = {}
        fp_dict[fp[2]] = np.array([fp[0], fp[1]], dtype="object").T

        return fp_dict

    @staticmethod
    def _get_fp_similarity(
        fp1: NamedTuple,
        fp2: NamedTuple,
        col: int = 1,
        pt: int | str = "All",
        normalize: bool = False,
        tanimoto: bool = True,
    ) -> float:
        """
        Calculates the similarity index (dot product) of two fingerprints

        Args:
            fp1 (NamedTuple): The 1st dos fingerprint object
            fp2 (NamedTuple): The 2nd dos fingerprint object
            col (int): The item in the fingerprints (0:energies,1: coxxs) to take the dot product of (default is 1)
            pt (int or str) : The index of the point that the dot product is to be taken (default is All)
            normalize (bool): If True normalize the scalar product to 1 (default is False)
            tanimoto (bool): If True will compute Tanimoto index (default is False)

        Raises:
            ValueError: If both tanimoto and normalize are set to True.

        Returns:
            Similarity index (float): The value of dot product

        """
        fp1_dict = (
            BatchCoxxFingerprint._fp_to_dict(fp1) if not isinstance(fp1, dict) else fp1
        )

        fp2_dict = (
            BatchCoxxFingerprint._fp_to_dict(fp2) if not isinstance(fp2, dict) else fp2
        )

        if pt == "All":
            vec1 = np.array([pt[col] for pt in fp1_dict.values()]).flatten()
            vec2 = np.array([pt[col] for pt in fp2_dict.values()]).flatten()
        else:
            vec1 = fp1_dict[fp1[2][pt]][col]
            vec2 = fp2_dict[fp2[2][pt]][col]

        if not normalize and tanimoto:
            rescale = (
                np.linalg.norm(vec1) ** 2
                + np.linalg.norm(vec2) ** 2
                - np.dot(vec1, vec2)
            )
            return np.dot(vec1, vec2) / rescale

        elif not tanimoto and normalize:
            rescale = np.linalg.norm(vec1) * np.linalg.norm(vec2)
            return np.dot(vec1, vec2) / rescale

        elif not tanimoto and not normalize:
            rescale = 1.0
            return np.dot(vec1, vec2) / rescale

        else:
            raise ValueError(
                "Cannot compute similarity index. Please set either normalize=True or tanimoto=True or both to False."
            )

    def _fingerprint_df(self, path_to_lobster_calc) -> pd.DataFrame:
        """
        Wrapper method to get fingerprint object dataframe using FeaturizeCOXX.get_coxx_fingerprint_df method.
        Also helps switching the data used for fingerprint generation

        Returns:
            A pandas dataframe with COXX fingerprint object

        """
        dir_name = Path(path_to_lobster_calc)

        if self.fingerprint_for.upper() == "COBI":
            req_files = {
                "coxxcar_path": "COBICAR.lobster",
                "icoxxlist_path": "ICOBILIST.lobster",
            }
            for file, default_value in req_files.items():
                file_path = dir_name / default_value
                req_files[file] = file_path  # type: ignore
                if not file_path.exists():
                    gz_file_path = file_path.with_name(file_path.name + ".gz")
                    if gz_file_path.exists():
                        req_files[file] = gz_file_path  # type: ignore

            coxxcar_path = req_files.get("coxxcar_path")
            icoxxlist_path = req_files.get("icoxxlist_path")
            are_cobis = True
            are_coops = False

        elif self.fingerprint_for.upper() == "COOP":
            req_files = {
                "coxxcar_path": "COOPCAR.lobster",
                "icoxxlist_path": "ICOOPLIST.lobster",
            }
            for file, default_value in req_files.items():
                file_path = dir_name / default_value
                req_files[file] = file_path  # type: ignore
                if not file_path.exists():
                    gz_file_path = file_path.with_name(file_path.name + ".gz")
                    if gz_file_path.exists():
                        req_files[file] = gz_file_path  # type: ignore

            coxxcar_path = req_files.get("coxxcar_path")
            icoxxlist_path = req_files.get("icoxxlist_path")
            are_cobis = False
            are_coops = True

        else:
            req_files = {
                "coxxcar_path": "COHPCAR.lobster",
                "icoxxlist_path": "ICOHPLIST.lobster",
            }
            for file, default_value in req_files.items():
                file_path = dir_name / default_value
                req_files[file] = file_path  # type: ignore
                if not file_path.exists():
                    gz_file_path = file_path.with_name(file_path.name + ".gz")
                    if gz_file_path.exists():
                        req_files[file] = gz_file_path  # type: ignore

            coxxcar_path = req_files.get("coxxcar_path")
            icoxxlist_path = req_files.get("icoxxlist_path")
            are_cobis = False
            are_coops = False

        structure_path = dir_name / "POSCAR"
        if not structure_path.exists():
            gz_file_path = structure_path.with_name(structure_path.name + ".gz")
            if gz_file_path.exists():
                structure_path = gz_file_path

        coxx = FeaturizeCOXX(
            path_to_coxxcar=str(coxxcar_path),
            path_to_icoxxlist=str(icoxxlist_path),
            path_to_structure=str(structure_path),
            feature_type=self.feature_type,
            e_range=self.e_range,
            are_coops=are_coops,
            are_cobis=are_cobis,
        )

        df_fp = coxx.get_coxx_fingerprint_df(
            spin_type=self.spin_type,
            n_bins=self.n_bins,
            normalize=self.normalize,
            label_list=self.label_list,
        )

        return df_fp

    def _get_fingerprints_df(self) -> pd.DataFrame:
        """
        Batch wrapper method to get fingerprint objects dataframe using
        BatchCoxxFingerprint._fingerprint_df method.

        Returns:
            A pandas dataframe with COXX fingerprint objects

        """
        paths = [
            os.path.join(self.path_to_lobster_calcs, f)
            for f in os.listdir(self.path_to_lobster_calcs)
            if not f.startswith("t")
            and not f.startswith(".")
            and os.path.isdir(os.path.join(self.path_to_lobster_calcs, f))
        ]

        with mp.Pool(processes=self.n_jobs, maxtasksperchild=1) as pool:
            results = tqdm(
                pool.imap_unordered(self._fingerprint_df, paths, chunksize=1),
                total=len(paths),
                desc="Generating {} fingerprints".format(self.fingerprint_for.upper()),
            )
            pool.close()
            pool.join()
            row = []
            for result in results:
                row.append(result)

        df = pd.concat(row)
        df.sort_index(inplace=True)

        return df


import shutil
from core.factories import IngredientFactory, RecipeFactory
from django.contrib.auth import get_user_model
from django.conf import settings
from django.urls import reverse
from django.test import TestCase, override_settings

from rest_framework import status
from rest_framework.test import APIClient

from core.models import Ingredient

from recipe.serializers import IngredientSerializer

INGREDIENT_URL = reverse("recipe:ingredient-list")

class PublicIngredientApiTest(TestCase):
    """Test publicly available ingredients API"""

    def setUp(self):
        self.client = APIClient()

    def test_login_required(self):
        """Test that login is required to access the endpoint"""
        res = self.client.get(INGREDIENT_URL)

        self.assertEqual(res.status_code, status.HTTP_401_UNAUTHORIZED)

@override_settings(MEDIA_ROOT=settings.TEST_MEDIA_ROOT)
class PrivateIngredientApiTest(TestCase):
    """Test ingredients can retreived by authorized user"""

    def setUp(self):
        self.client = APIClient()
        self.user = get_user_model().objects.create_user("test@test.com", "testpass")
        self.client.force_authenticate(self.user)
        IngredientFactory.reset_sequence()

    def tearDown(self):
        shutil.rmtree(settings.TEST_MEDIA_ROOT)

    def test_retrieving_ingredient_list(self):
        """Test retrieving a list of ingredients"""
        IngredientFactory.create_batch(2, user=self.user)

        res = self.client.get(INGREDIENT_URL)
        ingredients = Ingredient.objects.all().order_by("-name")
        serializer = IngredientSerializer(ingredients, many=True)
        self.assertEqual(res.status_code, status.HTTP_200_OK)
        self.assertEqual(serializer.data, res.data)

    def test_ingredients_limited_to_user(self):
        """Test only ingredients for the authenticated user are returned"""
        user2 = get_user_model().objects.create_user("test2@test.com", "pass12344")
        IngredientFactory.create(user=user2)
        ingredient = IngredientFactory.create(user=self.user)

        res = self.client.get(INGREDIENT_URL)
        self.assertEqual(res.status_code, status.HTTP_200_OK)
        self.assertEqual(len(res.data), 1)
        self.assertEqual(res.data[0]["name"], ingredient.name)

    def test_create_ingredient_successful(self):
        """Test create a new ingredient"""
        payload = {"name": "Cabbage"}
        self.client.post(INGREDIENT_URL, payload)

        exists = Ingredient.objects.filter(user=self.user, name=payload["name"]).exists()
        self.assertTrue(exists)

    def test_create_ingredient_invalid(self):
        """Test creating invalid ingredient fails"""
        payload = {"name": ""}
        res = self.client.post(INGREDIENT_URL, payload)

        self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)

    def test_retrieve_ingredients_assigned_to_recipes(self):
        """Test filtering ingredients by those assinged to recipes"""
        ingredient1 = IngredientFactory.create(user=self.user)
        ingredient2 = IngredientFactory.create(user=self.user)
        recipe = RecipeFactory.create(user=self.user)
        recipe.ingredients.add(ingredient1)

        res = self.client.get(INGREDIENT_URL, {"assigned_only": 1})

        serializer1 = IngredientSerializer(ingredient1)
        serializer2 = IngredientSerializer(ingredient2)
        self.assertIn(serializer1.data, res.data)
        self.assertNotIn(serializer2.data, res.data)


import re

def count(filename):
    arr = [0]*601
    sum = 0
    i = 0
    max = 0
    maxi = 0
    with open(filename) as f:
        content = f.readlines()
    for line in content:
        print(line)
        pattern = re.compile(r'ns_ at [0-9]\d*')
        match = pattern.search(line)
        if match:
            print(match.group())
            numpattern = re.compile('[0-9]\d*')
            nummatch = numpattern.search(match.group())
            num = int(nummatch.group())
            arr[num] = arr[num] + 1
        else:
            print('not match')
    for times in arr:
        print(times)
        if times > max:
            max = times
            maxi = i
        sum = sum + times
        i = i + 1

    avarage = sum / i
    print('sum is '+ str(sum))
    print('i = ' + str(i))
    print('average is ' + str(avarage))
    print('max is' + str(max))
    print('maxi is '+ str(maxi))

count("beijing3am.tcl")


from pathlib import Path

class PathTree(object):
    # Set up spacer character defs
    display_filename_prefix_middle = '├──'
    display_filename_prefix_last = '└──'
    display_parent_prefix_middle = '│   '
    display_parent_prefix_last = '    '
    max_children = 1000

    def __init__(self, path, parent_path, is_last, max_depth):
        self.path = Path(str(path))
        self.parent = parent_path
        self.is_last = is_last
        self.max_depth = max_depth
        if self.parent:
            self.depth = self.parent.depth + 1
        else:
            self.depth = 0

    @property
    def display_name(self):
        out_str = self.path.name
        if self.path.is_dir():
            out_str += '/'  # add directory /
        return out_str + ("   -- Max depth ({}) for scan-input logfile reached. ".format(self.max_depth) if self.depth == self.max_depth else "")

    @classmethod
    def make_tree(cls, root, max_depth=6, parent=None, is_last=False):
        root = Path(str(root))
        displayable_root = cls(root, parent, is_last, max_depth)
        yield displayable_root
        if displayable_root.depth < max_depth:
            children = sorted(list(path for path in root.iterdir()), key=lambda s: str(s).lower())
            count = 1

            for path in children[0:min(PathTree.max_children, len(children))]:
                is_last = count == len(children) or displayable_root.depth+1 == max_depth
                if path.is_dir():
                    yield from cls.make_tree(path,
                                             parent=displayable_root,
                                             is_last=is_last,
                                             max_depth=max_depth)
                else:
                    yield cls(path, displayable_root, is_last, max_depth)
                count += 1

    def displayable(self):
        if self.parent is None:
            return self.display_name

        _filename_prefix = (self.display_filename_prefix_last
                            if self.is_last
                            else self.display_filename_prefix_middle)

        parts = ['{!s} {!s}'.format(_filename_prefix, self.display_name)]

        parent = self.parent
        while parent and parent.parent is not None:
            parts.append(self.display_parent_prefix_middle
                         if not parent.is_last
                         else self.display_parent_prefix_last)
            parent = parent.parent
        return ''.join(reversed(parts))


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

import unittest
import os
import json
from mock import patch, PropertyMock
from ADSOrcid import report, app
from ADSOrcid.models import Base, ClaimsLog

logdir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../logs'))

class TestReport(unittest.TestCase):

    def setUp(self):
        unittest.TestCase.setUp(self)
        proj_home = os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))
        self.app = app.ADSOrcidCelery('test', local_config= \
            {
                'SQLALCHEMY_URL': 'sqlite:///',
                'SQLALCHEMY_ECHO': False,
                'PROJ_HOME': proj_home,
                'TEST_DIR': os.path.join(proj_home, 'ADSOrcid/tests'),
            })
        Base.metadata.bind = self.app._session.get_bind()
        Base.metadata.create_all()

    def tearDown(self):
        unittest.TestCase.tearDown(self)
        Base.metadata.drop_all()
        self.app.close_app()

    def test_claimed_records(self):
        claimed_log = logdir + '/test_claimed.log'
        if os.path.exists(claimed_log):
            os.remove(claimed_log)

        json_1 = {'response': {'docs': [{'bibcode': '2018Test..001..123A'},
                                          {'bibcode': '2018Test..001..123B'}],
                                'numFound': 2,
                                'start': 0}
                  }

        json_2 = [{'bibcode': '2018Test..001..123A',
                   'orcid_pub': ['0000-0001-0002-0003',
                                  '-']
                  },
                  {'bibcode': '2018Test..001..123B',
                   'orcid_user': ['0000-0001-0002-0003',
                                   '0000-0001-0002-0004',
                                   '-']
                   },
                  {'bibcode': '2018Test..001..123C',
                   'orcid_other': ['0000-0001-0002-0003',
                                    '0000-0001-0002-0004',
                                    '0000-0001-0002-0005',
                                    '-']
                   }
                  ]

        with patch.object(report, 'query_solr', return_value=json_1) as query_solr, \
             patch.object(report, 'query_records', return_value=json_2) as query_records:

            report.claimed_records(debug=False,test=True)

            log = open(logdir + '/test_claimed.log').read()
            line = 0
            for x in log.strip().split('\n'):
                j = json.loads(x)
                if line == 0:
                    self.assertEqual(j['message'],'Number of records with an orcid_pub: 2')
                if line == 1:
                    self.assertEqual(j['message'],'Number of records with an orcid_user: 2')
                if line == 2:
                    self.assertEqual(j['message'],'Number of records with an orcid_other: 2')
                if line == 3:
                    self.assertEqual(j['message'],'Total number of orcid_pub claims: 1')
                if line == 4:
                    self.assertEqual(j['message'],'Total number of orcid_user claims: 2')
                if line == 5:
                    self.assertEqual(j['message'],'Total number of orcid_other claims: 3')
                if line == 6:
                    self.assertEqual(j['message'],'Total number of records with any ORCID claims: 3')
                line += 1

    def test_num_claims(self):
        num_claims_log = logdir + '/test_num_claimed.log'
        if os.path.exists(num_claims_log):
            os.remove(num_claims_log)

        r = self.app.insert_claims([
            self.app.create_claim(bibcode='2018Test..001..123D',
                                  orcidid='0000-0001-0002-0006',
                                  provenance='ads test',
                                  status='claimed'),
            self.app.create_claim(bibcode='2018Test..001..123E',
                                  orcidid='0000-0001-0002-0007',
                                  status='updated'),
            self.app.create_claim(bibcode='2018Test..001..123F',
                                  orcidid='0000-0001-0002-0008',
                                  status='removed')
            ])

        self.assertEqual(len(r), 3)
        self.assertTrue(len(self.app._session.query(ClaimsLog)
                            .filter_by(bibcode='2018Test..001..123D').all()) == 1)

        report.num_claims(self.app,n_days=1,test=True)

        log = open(logdir + '/test_num_claimed.log').read()
        line = 0
        for x in log.strip().split('\n'):
            j = json.loads(x)
            if line == 0:
                self.assertEqual(j['message'], 'Number of unique ORCID IDs generating claims of type claimed in last 1 days: 1')
            if line == 1:
                self.assertEqual(j['message'], 'Number of unique ORCID IDs generating claims of type removed in last 1 days: 1')
            if line == 2:
                self.assertEqual(j['message'], 'Number of unique ORCID IDs generating claims of type updated in last 1 days: 1')
            if line == 3:
                self.assertEqual(j['message'], 'Number of unique claims by a unique bibcode+ORCID ID pair that have been claimed in the last 1 days: 1')
            if line == 4:
                self.assertEqual(j['message'], 'Number of unique claims by a unique bibcode+ORCID ID pair that have been removed in the last 1 days: 1')
            if line == 5:
                self.assertEqual(j['message'], 'Number of unique claims by a unique bibcode+ORCID ID pair that have been updated in the last 1 days: 1')
            if line == 6:
                self.assertEqual(j['message'], "Total number of non-unique claims with status ['claimed', 'removed', 'updated'] in the last 1 days, to compare with logging on rejected claims: 3")
            line += 1

    def test_Kibana(self):
        kibana_log = logdir + '/test_kibana.log'
        if os.path.exists(kibana_log):
            os.remove(kibana_log)

        with patch.object(report.app.client, 'post') as post:
            data = {'responses': [{'hits': {'total': 5},
                                    'status': 200}]}
            resp = PropertyMock()
            resp.text = str(data)
            resp.json = lambda: data
            resp.status_code = 200
            post.return_value = resp

            report.num_refused_claims(n_days=1,test=True)
            report.num_missing_profile(n_days=1,test=True)

            log = open(logdir + '/test_kibana.log').read()
            line = 0
            for x in log.strip().split('\n'):
                j = json.loads(x)
                if line == 0:
                    self.assertEqual(j['message'], 'Number of claims rejected in the last 1 days: 5')
                if line == 1:
                    self.assertEqual(j['message'], 'Number of missing profile errors in the last 1 days: 5')
                line += 1


#############
################   DO NOT USE, THERE ARE SOME bugs not fixed yet, but should be fixed

##########################
import pysam
import sys
import numpy as np
import pandas as pd

HashDic_file=sys.argv[1]
Hashcall_file=sys.argv[2]
# LibraryType="RNA"   ## The CB tag in both ATAC and RNA bams are RNA barcode
BamInput=sys.argv[3]
tag="CB"
SigCut=0.6

HashDic={}
f=open(HashDic_file)
for line in f:
    L=line.strip().split(",")
    HashDic[L[0]]=L[1]
f.close()

## Make a dictionary to convert from RNA names to ATAC names(Not in use in this script, but leave it here for other cases)
ATACWhite=pd.read_table("/lab/solexa_weissman/cweng/Genomes/10X/WhiteList_10X_Multiome.ATAC",names=["ATACName"])
RNAWhite=pd.read_table("/lab/solexa_weissman/cweng/Genomes/10X/WhiteList_10X_Multiome.RNA",names=["RNAName"])
RNAWhite["ATAC_Name"]=ATACWhite.ATACName
RNA2ATACDic=RNAWhite.set_index("RNAName")["ATAC_Name"].to_dict()
# ATAC2RNADic=RNAWhite.set_index("ATAC_Name")["RNA_Name"].to_dict()

Hashcall=pd.read_table(Hashcall_file)
Hashcall["ATACName"]=[RNA2ATACDic[n] for n in Hashcall.cell]

## Make HashCall_RNA_dict convert RNA cell names into group bam file;
## Make HashCall_ATAC_dict convert ATAC cell names into group bam file;
Hashcall_sig=Hashcall[Hashcall["sig"]>SigCut]
Hashcall_sig["group"]=[HashDic[x] for x in Hashcall_sig["call"]]
Hashcall_sig.cell=Hashcall_sig.cell+"-1"
Hashcall_sig.ATACName=Hashcall_sig.ATACName+"-1"

# Define Bam files
# BamInput="/lab/solexa_weissman/cweng/Projects/MitoTracing_Velocity/SecondaryAnalysis/Donor04_BMMC1_ConditionTest/CellRanger/Donor04_BMMC1_GDN/outs/atac_possorted_bam.bam"
BamInput="/lab/solexa_weissman/cweng/Projects/MitoTracing_Velocity/SecondaryAnalysis/Donor04_BMMC1_ConditionTest/CellRanger/Donor04_BMMC1_GDN/outs/gex_possorted_bam.bam"

out_handles_Dic={}
TPLT= pysam.AlignmentFile(BamInput, "rb")

for group in set(Hashcall_sig.group):
    Bam=pysam.AlignmentFile("Post10x."+group+".bam","wb",template=TPLT)
    out_handles_Dic[group]=Bam

Hashcall_sig["output"]=[out_handles_Dic[n] for n in Hashcall_sig.group]
HashCall_RNA2out_dict=Hashcall_sig[["cell","output"]].set_index("cell")["output"].to_dict()
HashCall_RNA2group_dict=Hashcall_sig[["cell","group"]].set_index("cell")["group"].to_dict()
# HashCall_ATAC_dict=Hashcall_sig[["ATACName","output"]].set_index("ATACName")["output"].to_dict()
## The HashCall_ATAC_dict is not in use becasue the CB tag in ATAC bam is also gex(rna) barcode

## ReadIn the bam

Bam=pysam.AlignmentFile(BamInput,"rb")
for read in Bam.fetch(until_eof=True):
    try:
        CB=read.get_tag('CB')
    except KeyError:
        continue
    if CB in HashCall_RNA2out_dict.keys():
        # print(CB)
        read.set_tag('GP',HashCall_RNA2group_dict[CB])
        HashCall_RNA2out_dict[CB].write(read)

print("BamSpliting successfully completed!")

with open("SplitLog","w") as f:
    f.write("BamSpliting successfully completed!\n")

f.close()


import cv2

# HSV
low_black = (37, 37, 37)
high_black = (0, 0, 0)

cap = cv2.VideoCapture("/home/nikita/PycharmProjects/test_task_cv/v.mp4")

width = int(cap.get(3))
height = int(cap.get(4))
result_frames = []

#
while True:
    ret, frame = cap.read()
    if not ret:
        break

    frame_b = cv2.inRange(frame, high_black, low_black)
    contours, hierarchy = cv2.findContours(frame_b.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cv2.drawContours(frame, contours, -1, (255, 8, 0), cv2.FILLED)

    result_frames.append(frame)

cap.release()

#
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
# fourcc = cv2.VideoWriter_fourcc(*'avc1')
video = cv2.VideoWriter('/home/nikita/PycharmProjects/test_task_cv/video.mp4', fourcc, 20.0, (width, height))
#
#
for result_frame in result_frames:
    video.write(result_frame)


import logging
import voluptuous as vol
import json
from datetime import timedelta
import time

from homeassistant.components import sensor
from custom_components.smartthinq import (
	DOMAIN, LGE_DEVICES, LGEDevice)
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
    ATTR_ENTITY_ID, CONF_TOKEN, CONF_ENTITY_ID)
from homeassistant.exceptions import PlatformNotReady

import wideq

REQUIREMENTS = ['wideq']
DEPENDENCIES = ['smartthinq']

LGE_WASHER_DEVICES = 'lge_washer_devices'

ATTR_CURRENT_STATUS = 'current_status'
ATTR_RUN_STATE = 'run_state'
ATTR_PRE_STATE = 'pre_state'
ATTR_REMAIN_TIME = 'remain_time'
ATTR_INITIAL_TIME = 'initial_time'
ATTR_RESERVE_TIME = 'reserve_time'
ATTR_CURRENT_COURSE = 'current_course'
ATTR_ERROR_STATE = 'error_state'
ATTR_SPIN_OPTION_STATE = 'spin_option_state'
ATTR_WATERTEMP_OPTION_STATE = 'watertemp_option_state'
ATTR_CREASECARE_MODE = 'creasecare_mode'
ATTR_CHILDLOCK_MODE = 'childlock_mode'
ATTR_STEAM_MODE = 'steam_mode'
ATTR_STEAM_SOFTENER_MODE = 'steam_softener_mode'
ATTR_DOORLOCK_MODE = 'doorlock_mode'
ATTR_PREWASH_MODE = 'prewash_mode'
ATTR_REMOTESTART_MODE = 'remotestart_mode'
ATTR_TURBOWASH_MODE = 'turbowash_mode'
ATTR_TUBCLEAN_COUNT = 'tubclean_count'

RUNSTATES = {
    'OFF': wideq.STATE_WASHER_POWER_OFF,
    'INITIAL': wideq.STATE_WASHER_INITIAL,
    'PAUSE': wideq.STATE_WASHER_PAUSE,
    'ERROR_AUTO_OFF': wideq.STATE_WASHER_ERROR_AUTO_OFF,
    'RESERVE': wideq.STATE_WASHER_RESERVE,
    'DETECTING': wideq.STATE_WASHER_DETECTING,
    'ADD_DRAIN': wideq.STATE_WASHER_ADD_DRAIN,
    'DETERGENT_AMOUNT': wideq.STATE_WASHER_DETERGENT_AMOUT,
    'RUNNING': wideq.STATE_WASHER_RUNNING,
    'PREWASH': wideq.STATE_WASHER_PREWASH,
    'RINSING': wideq.STATE_WASHER_RINSING,
    'RINSE_HOLD': wideq.STATE_WASHER_RINSE_HOLD,
    'SPINNING': wideq.STATE_WASHER_SPINNING,
    'DRYING': wideq.STATE_WASHER_DRYING,
    'END': wideq.STATE_WASHER_END,
    'REFRESHWITHSTEAM': wideq.STATE_WASHER_REFRESHWITHSTEAM,
    'COOLDOWN': wideq.STATE_WASHER_COOLDOWN,
    'STEAMSOFTENING': wideq.STATE_WASHER_STEAMSOFTENING,
    'ERRORSTATE': wideq.STATE_WASHER_ERRORSTATE,
    'TCL_ALARM_NORMAL': wideq.STATE_WASHER_TCL_ALARM_NORMAL,
    'FROZEN_PREVENT_INITIAL': wideq.STATE_WASHER_FROZEN_PREVENT_INITIAL,
    'FROZEN_PREVENT_RUNNING': wideq.STATE_WASHER_FROZEN_PREVENT_RUNNING,
    'FROZEN_PREVENT_PAUSE': wideq.STATE_WASHER_FROZEN_PREVENT_PAUSE,
    'ERROR': wideq.STATE_WASHER_ERROR,
}

WATERTEMPSTATES = {
    'NO_SELECT': wideq.STATE_WASHER_TERM_NO_SELECT,
    'COLD' : wideq.STATE_WASHER_WATERTEMP_COLD,
    'TWENTY' : wideq.STATE_WASHER_WATERTEMP_20,
    'THIRTY' : wideq.STATE_WASHER_WATERTEMP_30,
    'FOURTY' : wideq.STATE_WASHER_WATERTEMP_40,
    'SIXTY': wideq.STATE_WASHER_WATERTEMP_60,
    'NINTYFIVE': wideq.STATE_WASHER_WATERTEMP_95,
    'OFF': wideq.STATE_WASHER_POWER_OFF,

}

SPINSPEEDSTATES = {
    'NOSPIN': wideq.STATE_WASHER_SPINSPEED_NOSPIN,
    'SPIN_400' : wideq.STATE_WASHER_SPINSPEED_400,
    'SPIN_800' : wideq.STATE_WASHER_SPINSPEED_800,
    'SPIN_1000' : wideq.STATE_WASHER_SPINSPEED_1000,
    'SPIN_1200': wideq.STATE_WASHER_SPINSPEED_1200,
    'SPIN_1400': wideq.STATE_WASHER_SPINSPEED_1400,
    'OFF': wideq.STATE_WASHER_POWER_OFF,
}

ERRORS = {
    'ERROR_dE2' : wideq.STATE_WASHER_ERROR_dE2,
    'ERROR_IE' : wideq.STATE_WASHER_ERROR_IE,
    'ERROR_OE' : wideq.STATE_WASHER_ERROR_OE,
    'ERROR_UE' : wideq.STATE_WASHER_ERROR_UE,
    'ERROR_FE' : wideq.STATE_WASHER_ERROR_FE,
    'ERROR_PE' : wideq.STATE_WASHER_ERROR_PE,
    'ERROR_tE' : wideq.STATE_WASHER_ERROR_tE,
    'ERROR_LE' : wideq.STATE_WASHER_ERROR_LE,
    'ERROR_CE' : wideq.STATE_WASHER_ERROR_CE,
    'ERROR_PF' : wideq.STATE_WASHER_ERROR_PF,
    'ERROR_FF' : wideq.STATE_WASHER_ERROR_FF,
    'ERROR_dCE' : wideq.STATE_WASHER_ERROR_dCE,
    'ERROR_EE' : wideq.STATE_WASHER_ERROR_EE,
    'ERROR_PS' : wideq.STATE_WASHER_ERROR_PS,
    'ERROR_dE1' : wideq.STATE_WASHER_ERROR_dE1,
    'ERROR_LOE' : wideq.STATE_WASHER_ERROR_LOE,
    'NO_ERROR' : wideq.STATE_NO_ERROR,
    'OFF': wideq.STATE_WASHER_POWER_OFF,
}

OPTIONITEMMODES = {
    'ON': wideq.STATE_OPTIONITEM_ON,
    'OFF': wideq.STATE_OPTIONITEM_OFF,
}

MAX_RETRIES = 5

LOGGER = logging.getLogger(__name__)

def setup_platform(hass, config, add_entities, discovery_info=None):
    import wideq
    refresh_token = hass.data[CONF_TOKEN]
    client = wideq.Client.from_token(refresh_token)

    """Set up the LGE Washer components."""

    LOGGER.debug("Creating new LGE Washer")

    if LGE_WASHER_DEVICES not in hass.data:
        hass.data[LGE_WASHER_DEVICES] = []

    for device_id in (d for d in hass.data[LGE_DEVICES]):
        device = client.get_device(device_id)

        if device.type == wideq.DeviceType.WASHER:
            try:
            	washer_entity = LGEWASHERDEVICE(client, device)
            except wideq.NotConnectError:
                LOGGER.info('Connection Lost. Retrying.')
                raise PlatformNotReady
            hass.data[LGE_WASHER_DEVICES].append(washer_entity)
    add_entities(hass.data[LGE_WASHER_DEVICES])

    LOGGER.debug("LGE Washer is added")

class LGEWASHERDEVICE(LGEDevice):
    def __init__(self, client, device):

        """initialize a LGE Washer Device."""
        LGEDevice.__init__(self, client, device)

        import wideq
        self._washer = wideq.WasherDevice(client, device)

        self._washer.monitor_start()
        self._washer.monitor_start()
        self._washer.delete_permission()
        self._washer.delete_permission()

        # The response from the monitoring query.
        self._state = None

        self.update()

    @property
    def supported_features(self):
        """ none """

    @property
    def state_attributes(self):
        """Return the optional state attributes."""
        data={}
        data[ATTR_RUN_STATE] = self.current_run_state
        data[ATTR_PRE_STATE] = self.pre_state
        data[ATTR_REMAIN_TIME] = self.remain_time
        data[ATTR_INITIAL_TIME] = self.initial_time
        data[ATTR_RESERVE_TIME] = self.reserve_time
        data[ATTR_CURRENT_COURSE] = self.current_course
        data[ATTR_ERROR_STATE] = self.error_state
        data[ATTR_SPIN_OPTION_STATE] = self.spin_option_state
        data[ATTR_WATERTEMP_OPTION_STATE] = self.watertemp_option_state
        data[ATTR_CREASECARE_MODE] = self.creasecare_mode
        data[ATTR_CHILDLOCK_MODE] = self.childlock_mode
        data[ATTR_STEAM_MODE] = self.steam_mode
        data[ATTR_STEAM_SOFTENER_MODE] = self.steam_softener_mode
        data[ATTR_DOORLOCK_MODE] = self.doorlock_mode
        data[ATTR_PREWASH_MODE] = self.prewash_mode
        data[ATTR_REMOTESTART_MODE] = self.remotestart_mode
        data[ATTR_TURBOWASH_MODE] = self.turbowash_mode
        data[ATTR_TUBCLEAN_COUNT] = self.tubclean_count
        return data

    @property
    def is_on(self):
        if self._state:
            return self._state.is_on

    @property
    def current_run_state(self):
        if self._state:
            run = self._state.run_state
            return RUNSTATES[run.name]

    @property
    def run_list(self):
        return list(RUNSTATES.values())

    @property
    def pre_state(self):
        if self._state:
            pre = self._state.pre_state
            return RUNSTATES[pre.name]

    @property
    def remain_time(self):
        if self._state:
            remain_hour = self._state.remaintime_hour
            remain_min = self._state.remaintime_min
            remaintime = [remain_hour, remain_min]
            if int(remain_min) < 10:
                return ":0".join(remaintime)
            else:
                return ":".join(remaintime)

    @property
    def initial_time(self):
        if self._state:
            initial_hour = self._state.initialtime_hour
            initial_min = self._state.initialtime_min
            initialtime = [initial_hour, initial_min]
            if int(initial_min) < 10:
                return ":0".join(initialtime)
            else:
                return ":".join(initialtime)

    @property
    def reserve_time(self):
        if self._state:
            reserve_hour = self._state.reservetime_hour
            reserve_min = self._state.reservetime_min
            reservetime = [reserve_hour, reserve_min]
            if int(reserve_min) < 10:
                return ":0".join(reservetime)
            else:
                return ":".join(reservetime)

    @property
    def current_course(self):
        if self._state:
            course = self._state.current_course
            smartcourse = self._state.current_smartcourse
            if course == '다운로드코스':
                return smartcourse
            elif course == 'OFF':
                return 'Off'
            else:
                return course

    @property
    def error_state(self):
        if self._state:
            error = self._state.error_state
            return ERRORS[error]

    @property
    def spin_option_state(self):
        if self._state:
            spin_option = self._state.spin_option_state
            if spin_option == 'OFF':
                return SPINSPEEDSTATES['OFF']
            else:
                return SPINSPEEDSTATES[spin_option.name]

    @property
    def watertemp_option_state(self):
        if self._state:
            watertemp_option = self._state.water_temp_option_state
            if watertemp_option == 'OFF':
                return WATERTEMPSTATES['OFF']
            else:
                return WATERTEMPSTATES[watertemp_option.name]

    @property
    def creasecare_mode(self):
        if self._state:
            mode = self._state.creasecare_state
            return OPTIONITEMMODES[mode]

    @property
    def childlock_mode(self):
        if self._state:
            mode = self._state.childlock_state
            return OPTIONITEMMODES[mode]

    @property
    def steam_mode(self):
        if self._state:
            mode = self._state.steam_state
            return OPTIONITEMMODES[mode]

    @property
    def steam_softener_mode(self):
        if self._state:
            mode = self._state.steam_softener_state
            return OPTIONITEMMODES[mode]

    @property
    def prewash_mode(self):
        if self._state:
            mode = self._state.prewash_state
            return OPTIONITEMMODES[mode]

    @property
    def doorlock_mode(self):
        if self._state:
            mode = self._state.doorlock_state
            return OPTIONITEMMODES[mode]

    @property
    def remotestart_mode(self):
        if self._state:
            mode = self._state.remotestart_state
            return OPTIONITEMMODES[mode]

    @property
    def turbowash_mode(self):
        if self._state:
            mode = self._state.turbowash_state
            return OPTIONITEMMODES[mode]

    @property
    def tubclean_count(self):
        if self._state:
            return self._state.tubclean_count

    def update(self):

        import wideq

        LOGGER.info('Updating %s.', self.name)
        for iteration in range(MAX_RETRIES):
            LOGGER.info('Polling...')

            try:
                state = self._washer.poll()
            except wideq.NotLoggedInError:
                LOGGER.info('Session expired. Refreshing.')
                self._client.refresh()
                self._washer.monitor_start()
                self._washer.monitor_start()
                self._washer.delete_permission()
                self._washer.delete_permission()

                continue

            except wideq.NotConnectError:
                LOGGER.info('Connection Lost. Retrying.')
                self._client.refresh()
                time.sleep(60)
                continue

            if state:
                LOGGER.info('Status updated.')
                self._state = state
                self._client.refresh()
                self._washer.monitor_start()
                self._washer.monitor_start()
                self._washer.delete_permission()
                self._washer.delete_permission()
                return

            LOGGER.info('No status available yet.')
            time.sleep(2 ** iteration)

        # We tried several times but got no result. This might happen
        # when the monitoring request gets into a bad state, so we
        # restart the task.
        LOGGER.warn('Status update failed.')

        self._washer.monitor_start()
        self._washer.monitor_start()
        self._washer.delete_permission()
        self._washer.delete_permission()


from engine.service import (
    AbstractService, GraphicsService,
    ServiceManager
)
import pyglet
import kytten

class GuiService(AbstractService):
    """ Service to manage GUI screens """

    def __init__(self, window, group_index = 1):
        self.guis = {}
        self.window = window
        self.batch = ServiceManager.instance[GraphicsService].batch
        self.group = pyglet.graphics.OrderedGroup(group_index)

    def add_gui(self, gui):
        """Add a gui to the manager."""
        assert(isinstance(gui, AbstractGui))
        self.guis[gui.name] = gui

    def show_gui(self, name):
        self.guis[name].show(self.window, self.batch,
                             self.group)

    def hide_gui(self, name):
        self.guis[name].hide()

    def on_draw(self):
        self.batch.draw()

class AbstractGui(object):
    def __init__(self, name):
        self.name = name
        self.root = None
        import os.path
        pth = os.path.abspath(os.path.join('graphics', 'theme'))
        self.theme = kytten.Theme(pth,
            override={
            "gui_color": [64, 128, 255, 255],
            "font_size": 14
        })
        self.visible = False

    def _build_gui(self, window, batch, group):
        return kytten.Dialog(
            kytten.TitleFrame("AbstractGui",
                width=200, height=150
            ),
            window=window, batch=batch,
            group=group, theme=self.theme
        )

    def show(self, window, batch, group):
        if not self.visible:
            self.root = self._build_gui(window, batch, group)
            self.visible = True

    def hide(self):
        if self.visible:
            self.root.teardown()
            self.visible = False
            self.root = None


import logging
from ctypes import c_int, c_void_p, byref, c_uint32
from random import randrange
from itertools import count

import pytest

from pyocf.ocf import OcfLib
from pyocf.types.cache import Cache, CacheMode, MetadataLayout, EvictionPolicy, CleaningPolicy
from pyocf.types.core import Core
from pyocf.types.data import Data
from pyocf.types.io import IoDir
from pyocf.types.shared import OcfError, OcfCompletion, CacheLineSize, SeqCutOffPolicy
from pyocf.types.volume import Volume
from pyocf.utils import Size

logger = logging.getLogger(__name__)

def test_start_check_default(pyocf_ctx):
    """Test if default values are correct after start.
    """

    cache_device = Volume(Size.from_MiB(40))
    core_device = Volume(Size.from_MiB(10))
    cache = Cache.start_on_device(cache_device)

    core = Core.using_device(core_device)
    cache.add_core(core)

    # Check if values are default
    stats = cache.get_stats()
    assert stats["conf"]["cleaning_policy"] == CleaningPolicy.DEFAULT
    assert stats["conf"]["cache_mode"] == CacheMode.DEFAULT
    assert stats["conf"]["cache_line_size"] == CacheLineSize.DEFAULT
    assert stats["conf"]["eviction_policy"] == EvictionPolicy.DEFAULT

    core_stats = core.get_stats()
    assert core_stats["seq_cutoff_policy"] == SeqCutOffPolicy.DEFAULT

@pytest.mark.parametrize("cls", CacheLineSize)
@pytest.mark.parametrize("mode", CacheMode)
def test_start_write_first_and_check_mode(pyocf_ctx, mode: CacheMode, cls: CacheLineSize):
    """Test starting cache in different modes with different cache line sizes.
    After start check proper cache mode behaviour, starting with write operation.
    """

    cache_device = Volume(Size.from_MiB(40))
    core_device = Volume(Size.from_MiB(10))
    cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls)
    core_exported = Core.using_device(core_device)

    cache.add_core(core_exported)

    logger.info("[STAGE] Initial write to exported object")
    cache_device.reset_stats()
    core_device.reset_stats()

    test_data = Data.from_string("This is test data")
    io_to_core(core_exported, test_data, Size.from_sector(1).B)
    check_stats_write_empty(core_exported, mode, cls)

    logger.info("[STAGE] Read from exported object after initial write")
    io_from_exported_object(core_exported, test_data.size, Size.from_sector(1).B)
    check_stats_read_after_write(core_exported, mode, cls, True)

    logger.info("[STAGE] Write to exported object after read")
    cache_device.reset_stats()
    core_device.reset_stats()

    test_data = Data.from_string("Changed test data")

    io_to_core(core_exported, test_data, Size.from_sector(1).B)
    check_stats_write_after_read(core_exported, mode, cls)

    check_md5_sums(core_exported, mode)

@pytest.mark.parametrize("cls", CacheLineSize)
@pytest.mark.parametrize("mode", CacheMode)
def test_start_read_first_and_check_mode(pyocf_ctx, mode: CacheMode, cls: CacheLineSize):
    """Starting cache in different modes with different cache line sizes.
    After start check proper cache mode behaviour, starting with read operation.
    """

    cache_device = Volume(Size.from_MiB(20))
    core_device = Volume(Size.from_MiB(5))
    cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls)
    core_exported = Core.using_device(core_device)

    cache.add_core(core_exported)

    logger.info("[STAGE] Initial write to core device")
    test_data = Data.from_string("This is test data")
    io_to_core(core_exported, test_data, Size.from_sector(1).B, True)

    cache_device.reset_stats()
    core_device.reset_stats()

    logger.info("[STAGE] Initial read from exported object")
    io_from_exported_object(core_exported, test_data.size, Size.from_sector(1).B)
    check_stats_read_empty(core_exported, mode, cls)

    logger.info("[STAGE] Write to exported object after initial read")
    cache_device.reset_stats()
    core_device.reset_stats()

    test_data = Data.from_string("Changed test data")

    io_to_core(core_exported, test_data, Size.from_sector(1).B)

    check_stats_write_after_read(core_exported, mode, cls, True)

    logger.info("[STAGE] Read from exported object after write")
    io_from_exported_object(core_exported, test_data.size, Size.from_sector(1).B)
    check_stats_read_after_write(core_exported, mode, cls)

    check_md5_sums(core_exported, mode)

@pytest.mark.parametrize("cls", CacheLineSize)
@pytest.mark.parametrize("mode", CacheMode)
@pytest.mark.parametrize("layout", MetadataLayout)
def test_start_params(pyocf_ctx, mode: CacheMode, cls: CacheLineSize, layout: MetadataLayout):
    """Starting cache with different parameters.
    Check if cache starts without errors.
    If possible check whether cache reports properly set parameters.
    """
    cache_device = Volume(Size.from_MiB(20))
    queue_size = randrange(60000, 2**32)
    unblock_size = randrange(1, queue_size)
    volatile_metadata = randrange(2) == 1
    unaligned_io = randrange(2) == 1
    submit_fast = randrange(2) == 1
    name = "test"

    logger.info("[STAGE] Start cache")
    cache = Cache.start_on_device(
        cache_device,
        cache_mode=mode,
        cache_line_size=cls,
        name=name,
        metadata_layout=MetadataLayout.SEQUENTIAL,
        metadata_volatile=volatile_metadata,
        max_queue_size=queue_size,
        queue_unblock_size=unblock_size,
        pt_unaligned_io=unaligned_io,
        use_submit_fast=submit_fast)

    stats = cache.get_stats()
    assert stats["conf"]["cache_mode"] == mode, "Cache mode"
    assert stats["conf"]["cache_line_size"] == cls, "Cache line size"
    assert stats["conf"]["eviction_policy"] == EvictionPolicy.DEFAULT, "Eviction policy"
    assert cache.get_name() == name, "Cache name"
    # TODO: metadata_layout, metadata_volatile, max_queue_size,
    #  queue_unblock_size, pt_unaligned_io, use_submit_fast
    # TODO: test in functional tests

@pytest.mark.parametrize("cls", CacheLineSize)
@pytest.mark.parametrize("mode", CacheMode)
@pytest.mark.parametrize("with_flush", {True, False})
def test_stop(pyocf_ctx, mode: CacheMode, cls: CacheLineSize, with_flush: bool):
    """Stopping cache.
    Check if cache is stopped properly in different modes with or without preceding flush operation.
    """

    cache_device = Volume(Size.from_MiB(20))
    core_device = Volume(Size.from_MiB(5))
    cache = Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls)
    core_exported = Core.using_device(core_device)
    cache.add_core(core_exported)
    cls_no = 10

    run_io_and_cache_data_if_possible(core_exported, mode, cls, cls_no)

    stats = cache.get_stats()
    assert int(stats["conf"]["dirty"]) == (cls_no if mode.lazy_write() else 0),\
        "Dirty data before MD5"

    md5_exported_core = core_exported.exp_obj_md5()

    if with_flush:
        cache.flush()
    cache.stop()

    if mode.lazy_write() and not with_flush:
        assert core_device.md5() != md5_exported_core, \
            "MD5 check: core device vs exported object with dirty data"
    else:
        assert core_device.md5() == md5_exported_core, \
            "MD5 check: core device vs exported object with clean data"

def test_start_stop_multiple(pyocf_ctx):
    """Starting/stopping multiple caches.
    Check whether OCF allows for starting multiple caches and stopping them in random order
    """

    caches = []
    caches_no = randrange(6, 11)
    for i in range(1, caches_no):
        cache_device = Volume(Size.from_MiB(20))
        cache_name = f"cache{i}"
        cache_mode = CacheMode(randrange(0, len(CacheMode)))
        size = 4096 * 2**randrange(0, len(CacheLineSize))
        cache_line_size = CacheLineSize(size)

        cache = Cache.start_on_device(
            cache_device,
            name=cache_name,
            cache_mode=cache_mode,
            cache_line_size=cache_line_size)
        caches.append(cache)
        stats = cache.get_stats()
        assert stats["conf"]["cache_mode"] == cache_mode, "Cache mode"
        assert stats["conf"]["cache_line_size"] == cache_line_size, "Cache line size"
        assert stats["conf"]["cache_name"] == cache_name, "Cache name"

    caches.sort(key=lambda e: randrange(1000))
    for cache in caches:
        logger.info("Getting stats before stopping cache")
        stats = cache.get_stats()
        cache_name = stats["conf"]["cache_name"]
        cache.stop()
        assert get_cache_by_name(pyocf_ctx, cache_name) != 0, "Try getting cache after stopping it"

def test_100_start_stop(pyocf_ctx):
    """Starting/stopping stress test.
    Check OCF behaviour when cache is started and stopped continuously
    """

    for i in range(1, 101):
        cache_device = Volume(Size.from_MiB(20))
        cache_name = f"cache{i}"
        cache_mode = CacheMode(randrange(0, len(CacheMode)))
        size = 4096 * 2**randrange(0, len(CacheLineSize))
        cache_line_size = CacheLineSize(size)

        cache = Cache.start_on_device(
            cache_device,
            name=cache_name,
            cache_mode=cache_mode,
            cache_line_size=cache_line_size)
        stats = cache.get_stats()
        assert stats["conf"]["cache_mode"] == cache_mode, "Cache mode"
        assert stats["conf"]["cache_line_size"] == cache_line_size, "Cache line size"
        assert stats["conf"]["cache_name"] == cache_name, "Cache name"
        cache.stop()
        assert get_cache_by_name(pyocf_ctx, "cache1") != 0, "Try getting cache after stopping it"

def test_start_stop_incrementally(pyocf_ctx):
    """Starting/stopping multiple caches incrementally.
    Check whether OCF behaves correctly when few caches at a time are
    in turns added and removed (#added > #removed) until their number reaches limit,
    and then proportions are reversed and number of caches gradually falls to 0.
    """

    counter = count()
    caches = []
    caches_limit = 10
    add = True
    run = True
    increase = True
    while run:
        if add:
            for i in range(0, randrange(3, 5) if increase else randrange(1, 3)):
                cache_device = Volume(Size.from_MiB(20))
                cache_name = f"cache{next(counter)}"
                cache_mode = CacheMode(randrange(0, len(CacheMode)))
                size = 4096 * 2**randrange(0, len(CacheLineSize))
                cache_line_size = CacheLineSize(size)

                cache = Cache.start_on_device(
                    cache_device,
                    name=cache_name,
                    cache_mode=cache_mode,
                    cache_line_size=cache_line_size)
                caches.append(cache)
                stats = cache.get_stats()
                assert stats["conf"]["cache_mode"] == cache_mode, "Cache mode"
                assert stats["conf"]["cache_line_size"] == cache_line_size, "Cache line size"
                assert stats["conf"]["cache_name"] == cache_name, "Cache name"
                if len(caches) == caches_limit:
                    increase = False
        else:
            for i in range(0, randrange(1, 3) if increase else randrange(3, 5)):
                if len(caches) == 0:
                    run = False
                    break
                cache = caches.pop()
                logger.info("Getting stats before stopping cache")
                stats = cache.get_stats()
                cache_name = stats["conf"]["cache_name"]
                cache.stop()
                assert get_cache_by_name(pyocf_ctx, cache_name) != 0, \
                    "Try getting cache after stopping it"
        add = not add

@pytest.mark.parametrize("mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_start_cache_same_id(pyocf_ctx, mode, cls):
    """Adding two caches with the same name
    Check that OCF does not allow for 2 caches to be started with the same cache_name
    """

    cache_device1 = Volume(Size.from_MiB(20))
    cache_device2 = Volume(Size.from_MiB(20))
    cache_name = "cache"
    cache = Cache.start_on_device(cache_device1,
                                  cache_mode=mode,
                                  cache_line_size=cls,
                                  name=cache_name)
    cache.get_stats()

    with pytest.raises(OcfError, match="OCF_ERR_CACHE_EXIST"):
        cache = Cache.start_on_device(cache_device2,
                                      cache_mode=mode,
                                      cache_line_size=cls,
                                      name=cache_name)
    cache.get_stats()

@pytest.mark.parametrize("cls", CacheLineSize)
def test_start_cache_huge_device(pyocf_ctx_log_buffer, cls):
    """
    Test whether we can start cache which would overflow ocf_cache_line_t type.
    pass_criteria:
      - Starting cache on device too big to handle should fail
    """
    class HugeDevice(Volume):
        def get_length(self):
            return Size.from_B((cls * c_uint32(-1).value))

        def submit_io(self, io):
            io.contents._end(io, 0)

    cache_device = HugeDevice(Size.from_MiB(20))

    with pytest.raises(OcfError, match="OCF_ERR_START_CACHE_FAIL"):
        cache = Cache.start_on_device(cache_device, cache_line_size=cls, metadata_volatile=True)

    assert any(
        [line.find("exceeds maximum") > 0 for line in pyocf_ctx_log_buffer.get_lines()]
    ), "Expected to find log notifying that max size was exceeded"

@pytest.mark.parametrize("mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_start_cache_same_device(pyocf_ctx, mode, cls):
    """Adding two caches using the same cache device
    Check that OCF does not allow for 2 caches using the same cache device to be started
    """

    cache_device = Volume(Size.from_MiB(20))
    cache = Cache.start_on_device(
        cache_device, cache_mode=mode, cache_line_size=cls, name="cache1"
    )
    cache.get_stats()

    with pytest.raises(OcfError, match="OCF_ERR_NOT_OPEN_EXC"):
        cache = Cache.start_on_device(
            cache_device, cache_mode=mode, cache_line_size=cls, name="cache2"
        )
    cache.get_stats()

@pytest.mark.parametrize("mode", CacheMode)
@pytest.mark.parametrize("cls", CacheLineSize)
def test_start_too_small_device(pyocf_ctx, mode, cls):
    """Starting cache with device below 100MiB
    Check if starting cache with device below minimum size is blocked
    """

    cache_device = Volume(Size.from_B(20 * 1024 * 1024 - 1))

    with pytest.raises(OcfError, match="OCF_ERR_INVAL_CACHE_DEV"):
        Cache.start_on_device(cache_device, cache_mode=mode, cache_line_size=cls)

def test_start_stop_noqueue(pyocf_ctx):
    # cache object just to construct cfg conveniently
    _cache = Cache(pyocf_ctx.ctx_handle)

    cache_handle = c_void_p()
    status = pyocf_ctx.lib.ocf_mngt_cache_start(
        pyocf_ctx.ctx_handle, byref(cache_handle), byref(_cache.cfg)
    )
    assert not status, "Failed to start cache: {}".format(status)

    # stop without creating mngmt queue
    c = OcfCompletion(
        [("cache", c_void_p), ("priv", c_void_p), ("error", c_int)]
    )
    pyocf_ctx.lib.ocf_mngt_cache_stop(cache_handle, c, None)
    c.wait()
    assert not c.results["error"], "Failed to stop cache: {}".format(c.results["error"])

def run_io_and_cache_data_if_possible(exported_obj, mode, cls, cls_no):
    test_data = Data(cls_no * cls)

    if mode in {CacheMode.WI, CacheMode.WA}:
        logger.info("[STAGE] Write to core device")
        io_to_core(exported_obj, test_data, 0, True)
        logger.info("[STAGE] Read from exported object")
        io_from_exported_object(exported_obj, test_data.size, 0)
    else:
        logger.info("[STAGE] Write to exported object")
        io_to_core(exported_obj, test_data, 0)

    stats = exported_obj.cache.get_stats()
    assert stats["usage"]["occupancy"]["value"] == \
        ((cls_no * cls / CacheLineSize.LINE_4KiB) if mode != CacheMode.PT else 0), "Occupancy"

def io_to_core(exported_obj: Core, data: Data, offset: int, to_core_device=False):
    new_io = exported_obj.new_core_io if to_core_device else exported_obj.new_io
    io = new_io(exported_obj.cache.get_default_queue(), offset, data.size,
                IoDir.WRITE, 0, 0)
    io.set_data(data)

    completion = OcfCompletion([("err", c_int)])
    io.callback = completion.callback
    io.submit()
    completion.wait()

    assert completion.results["err"] == 0, "IO to exported object completion"

def io_from_exported_object(exported_obj: Core, buffer_size: int, offset: int):
    read_buffer = Data(buffer_size)
    io = exported_obj.new_io(exported_obj.cache.get_default_queue(), offset,
                             read_buffer.size, IoDir.READ, 0, 0)
    io.set_data(read_buffer)

    completion = OcfCompletion([("err", c_int)])
    io.callback = completion.callback
    io.submit()
    completion.wait()

    assert completion.results["err"] == 0, "IO from exported object completion"
    return read_buffer

def check_stats_read_empty(exported_obj: Core, mode: CacheMode, cls: CacheLineSize):
    stats = exported_obj.cache.get_stats()
    assert stats["conf"]["cache_mode"] == mode, "Cache mode"
    assert exported_obj.cache.device.get_stats()[IoDir.WRITE] == (1 if mode.read_insert() else 0), \
        "Writes to cache device"
    assert exported_obj.device.get_stats()[IoDir.READ] == 1, "Reads from core device"
    assert stats["req"]["rd_full_misses"]["value"] == (0 if mode == CacheMode.PT else 1), \
        "Read full misses"
    assert stats["usage"]["occupancy"]["value"] == \
        ((cls / CacheLineSize.LINE_4KiB) if mode.read_insert() else 0), "Occupancy"

def check_stats_write_empty(exported_obj: Core, mode: CacheMode, cls: CacheLineSize):
    stats = exported_obj.cache.get_stats()
    assert stats["conf"]["cache_mode"] == mode, "Cache mode"
    # TODO(ajrutkow): why 1 for WT ??
    assert exported_obj.cache.device.get_stats()[IoDir.WRITE] == \
        (2 if mode.lazy_write() else (1 if mode == CacheMode.WT else 0)), \
        "Writes to cache device"
    assert exported_obj.device.get_stats()[IoDir.WRITE] == (0 if mode.lazy_write() else 1), \
        "Writes to core device"
    assert stats["req"]["wr_full_misses"]["value"] == (1 if mode.write_insert() else 0), \
        "Write full misses"
    assert stats["usage"]["occupancy"]["value"] == \
        ((cls / CacheLineSize.LINE_4KiB) if mode.write_insert() else 0), \
        "Occupancy"

def check_stats_write_after_read(exported_obj: Core,
                                 mode: CacheMode,
                                 cls: CacheLineSize,
                                 read_from_empty=False):
    stats = exported_obj.cache.get_stats()
    assert exported_obj.cache.device.get_stats()[IoDir.WRITE] == \
        (0 if mode in {CacheMode.WI, CacheMode.PT} else
            (2 if read_from_empty and mode.lazy_write() else 1)), \
        "Writes to cache device"
    assert exported_obj.device.get_stats()[IoDir.WRITE] == (0 if mode.lazy_write() else 1), \
        "Writes to core device"
    assert stats["req"]["wr_hits"]["value"] == \
        (1 if (mode.read_insert() and mode != CacheMode.WI)
            or (mode.write_insert() and not read_from_empty) else 0), \
        "Write hits"
    assert stats["usage"]["occupancy"]["value"] == \
        (0 if mode in {CacheMode.WI, CacheMode.PT} else (cls / CacheLineSize.LINE_4KiB)), \
        "Occupancy"

def check_stats_read_after_write(exported_obj, mode, cls, write_to_empty=False):
    stats = exported_obj.cache.get_stats()
    assert exported_obj.cache.device.get_stats()[IoDir.WRITE] == \
        (2 if mode.lazy_write() else (0 if mode == CacheMode.PT else 1)), \
        "Writes to cache device"
    assert exported_obj.cache.device.get_stats()[IoDir.READ] == \
        (1 if mode in {CacheMode.WT, CacheMode.WB, CacheMode.WO}
            or (mode == CacheMode.WA and not write_to_empty) else 0), \
        "Reads from cache device"
    assert exported_obj.device.get_stats()[IoDir.READ] == \
        (0 if mode in {CacheMode.WB, CacheMode.WO, CacheMode.WT}
            or (mode == CacheMode.WA and not write_to_empty) else 1), \
        "Reads from core device"
    assert stats["req"]["rd_full_misses"]["value"] == \
        (1 if mode in {CacheMode.WA, CacheMode.WI} else 0) \
        + (0 if write_to_empty or mode in {CacheMode.PT, CacheMode.WA} else 1), \
        "Read full misses"
    assert stats["req"]["rd_hits"]["value"] == \
        (1 if mode in {CacheMode.WT, CacheMode.WB, CacheMode.WO}
            or (mode == CacheMode.WA and not write_to_empty) else 0), \
        "Read hits"
    assert stats["usage"]["occupancy"]["value"] == \
        (0 if mode == CacheMode.PT else (cls / CacheLineSize.LINE_4KiB)), "Occupancy"

def check_md5_sums(exported_obj: Core, mode: CacheMode):
    if mode.lazy_write():
        assert exported_obj.device.md5() != exported_obj.exp_obj_md5(), \
            "MD5 check: core device vs exported object without flush"
        exported_obj.cache.flush()
        assert exported_obj.device.md5() == exported_obj.exp_obj_md5(), \
            "MD5 check: core device vs exported object after flush"
    else:
        assert exported_obj.device.md5() == exported_obj.exp_obj_md5(), \
            "MD5 check: core device vs exported object"

def get_cache_by_name(ctx, cache_name):
    cache_pointer = c_void_p()
    return OcfLib.getInstance().ocf_mngt_cache_get_by_name(
        ctx.ctx_handle, cache_name, byref(cache_pointer)
    )


import datetime
import threading

from app.main import db
from app.main.model.member import Member

def save_new_member(data):
    member = Member.query.filter_by(user_id=data['user_id']).first()
    if not member:
        new_member = Member(
            user_id=data['user_id'],
            username=data['username'],
            created_date=datetime.datetime.utcnow()
        )
        save_changes(new_member)
        response_object = {
            'status': 'success',
            'message': 'Successfully created.'
        }
        return response_object, 201
    else:
        response_object = {
            'status': 'fail',
            'message': 'User already exists.',
        }
        return response_object, 409

INSERTION_LOCK = threading.RLock()

def get_all_members():
    return Member.query.all()

def get_a_member(user_id):
    return Member.query.filter_by(user_id=user_id).first()

def update_level(user_id,level):
    with INSERTION_LOCK:
        member =Member.query.filter_by(user_id=user_id).first()
        if member:
            member.level=level
            save_changes(member)
            response_object = {
                'status': 'success',
                'message': 'Successfully updated.'
            }
            return response_object, 201
        else:
            response_object = {
                'status': 'fail',
                'message': 'User does not exists.',
            }
            return response_object, 409

def save_changes(data):
    db.session.add(data)
    db.session.commit()


"""Factories for making test data"""
import operator
import string
from functools import reduce

import base36
import faker
import factory
from factory import SubFactory
from factory.django import DjangoModelFactory
from factory.fuzzy import FuzzyChoice, FuzzyText
import pytz

from django.contrib.contenttypes.models import ContentType
from channels import api
from channels.constants import (
    VALID_CHANNEL_TYPES,
    LINK_TYPE_SELF,
    LINK_TYPE_LINK,
    VALID_EXTENDED_POST_TYPES,
    EXTENDED_POST_TYPE_ARTICLE,
)
from channels.factories.utils import channel_name
from channels.models import (
    RedditRefreshToken,
    RedditAccessToken,
    Subscription,
    LinkMeta,
    Channel,
    ChannelInvitation,
    ChannelMembershipConfig,
    Post,
    Comment,
    Article,
    SpamCheckResult,
)
from open_discussions.factories import UserFactory

FAKE = faker.Factory.create()

DEFAULT_ALLOWED_POST_TYPES = reduce(operator.or_, Channel.allowed_post_types.values())

class RedditRefreshTokenFactory(DjangoModelFactory):
    """Factory for refresh tokens"""

    user = factory.SubFactory(UserFactory)

    token_value = factory.Faker("word")

    class Meta:
        model = RedditRefreshToken

class RedditAccessTokenFactory(DjangoModelFactory):
    """Factory for access tokens"""

    user = factory.SubFactory(UserFactory)

    token_value = factory.Faker("word")
    token_expires_at = factory.LazyFunction(
        lambda: FAKE.date_time_this_year(
            before_now=False, after_now=True, tzinfo=pytz.utc
        )
    )

    class Meta:
        model = RedditAccessToken

    class Params:
        expired = factory.Trait(
            token_expires_at=factory.LazyFunction(
                lambda: FAKE.date_time_this_year(
                    before_now=True, after_now=False, tzinfo=pytz.utc
                )
            )
        )

class LinkMetaFactory(DjangoModelFactory):
    """Factory for a channels.models.LinkMeta object"""

    url = FuzzyText(prefix="https://")
    thumbnail = FuzzyText(prefix="https://", suffix=".jpg")

    class Meta:
        model = LinkMeta

class ChannelMembershipConfigFactory(DjangoModelFactory):
    """Factory for channels.models.ChannelMembershipConfig"""

    name = factory.Faker("text", max_nb_chars=50)
    query = factory.LazyFunction(lambda: {"email__endswith": "@mit.edu"})

    class Meta:
        model = ChannelMembershipConfig

class ChannelFactory(DjangoModelFactory):
    """Factory for a channels.models.Channel object"""

    name = factory.LazyAttributeSequence(channel_name)
    allowed_post_types = DEFAULT_ALLOWED_POST_TYPES
    title = factory.Faker("text", max_nb_chars=50)
    channel_type = FuzzyChoice(VALID_CHANNEL_TYPES)
    about = factory.List(
        [
            factory.Dict({"node": "text", "value": factory.Faker("text")}),
            factory.Dict({"node": "text", "value": factory.Faker("text")}),
            factory.Dict({"node": "text", "value": factory.Faker("text")}),
        ]
    )
    moderator_notifications = False

    @factory.post_generation
    def create_roles(
        self, create, extracted, **kwargs
    ):  # pylint: disable=unused-argument
        """Create the channel groups and roles after the channel is created"""
        if not create:
            return

        api.create_channel_groups_and_roles(self)

    class Meta:
        model = Channel

class ChannelInvitationFactory(DjangoModelFactory):
    """Factory for a channels.models.ChannelInvitation object"""

    inviter = SubFactory(UserFactory)
    channel = SubFactory(ChannelFactory)
    user = None
    email = factory.Faker("email")

    class Params:
        redeemed = factory.Trait(
            user=SubFactory(UserFactory, email=factory.SelfAttribute("..email"))
        )

    class Meta:
        model = ChannelInvitation

class PostFactory(DjangoModelFactory):
    """Factory for a channels.models.Post object"""

    author = SubFactory(UserFactory)
    channel = SubFactory(ChannelFactory)
    link_meta = factory.Maybe(
        factory.LazyAttribute(lambda post: post.post_type == LINK_TYPE_LINK),
        yes_declaration=SubFactory(LinkMetaFactory),
        no_declaration=None,
    )

    post_id = factory.Sequence(base36.dumps)
    post_type = FuzzyChoice(VALID_EXTENDED_POST_TYPES)

    title = factory.Faker("text", max_nb_chars=50)
    text = factory.Maybe(
        factory.LazyAttribute(lambda post: post.post_type == LINK_TYPE_SELF),
        yes_declaration=factory.Faker("text", max_nb_chars=100),
        no_declaration=None,
    )
    url = factory.Maybe(
        factory.LazyAttribute(lambda post: post.post_type == LINK_TYPE_LINK),
        yes_declaration=factory.Faker("url"),
        no_declaration=None,
    )
    article = factory.Maybe(
        factory.LazyAttribute(
            lambda post: post.post_type == EXTENDED_POST_TYPE_ARTICLE
        ),
        yes_declaration=factory.RelatedFactory(
            "channels.factories.models.ArticleFactory",
            "post",
            # special case to support nullable author only on Post
            author=factory.LazyAttribute(
                lambda article: article.factory_parent.author or UserFactory.create()
            ),
        ),
        no_declaration=None,
    )

    edited = False
    removed = False
    deleted = False
    num_comments = 0

    class Meta:
        model = Post

    class Params:
        # this trait denotes what a comment that hasn't had data populated from reddit looks like
        unpopulated = factory.Trait(
            title=None,
            url=None,
            text=None,
            author=None,
            edited=None,
            removed=None,
            deleted=None,
        )
        is_link = factory.Trait(post_type=LINK_TYPE_LINK)
        is_text = factory.Trait(post_type=LINK_TYPE_SELF)
        is_article = factory.Trait(post_type=EXTENDED_POST_TYPE_ARTICLE)

class ArticleFactory(DjangoModelFactory):
    """Factory for channels.models.Article"""

    author = factory.SubFactory(UserFactory)
    post = factory.SubFactory(
        "channels.factories.models.PostFactory",
        is_article=True,
        article=None,
        author=factory.SelfAttribute("..author"),
    )
    content = factory.List(
        [
            factory.Dict({"node": "text", "value": factory.Faker("text")}),
            factory.Dict({"node": "text", "value": factory.Faker("text")}),
            factory.Dict({"node": "text", "value": factory.Faker("text")}),
        ]
    )

    class Meta:
        model = Article

class CommentFactory(DjangoModelFactory):
    """Factory for a channels.models.Comment object"""

    post = SubFactory(PostFactory)
    author = SubFactory(UserFactory)

    comment_id = FuzzyText(chars=string.ascii_lowercase)
    parent_id = FuzzyText(chars=string.ascii_lowercase)

    text = factory.Faker("text", max_nb_chars=100)
    edited = False
    removed = False
    deleted = False

    class Meta:
        model = Comment

    class Params:
        # this trait denotes what a comment that hasn't had data populated from reddit looks like
        unpopulated = factory.Trait(
            text=None, author=None, edited=None, removed=None, deleted=None
        )

class SubscriptionFactory(DjangoModelFactory):
    """Factory for Subscription"""

    user = factory.SubFactory(UserFactory)
    post_id = factory.Sequence(base36.dumps)
    comment_id = factory.Maybe(
        "is_comment",
        yes_declaration=factory.Sequence(base36.dumps),
        no_declaration=None,
    )

    class Meta:
        model = Subscription

    class Params:
        is_comment = False

class SpamCheckResultFactory(DjangoModelFactory):
    """Factory for SpamCheckResult"""

    content_object = SubFactory(CommentFactory)
    object_id = factory.SelfAttribute("content_object.id")
    content_type = factory.LazyAttribute(
        lambda o: ContentType.objects.get_for_model(o.content_object)
    )
    user_ip = "111.11.111.1"

    class Meta:
        model = SpamCheckResult