from allennlp.common import Params
import numpy as np
from numpy.testing import assert_allclose
import pytest

from contexteval.common.custom_test_case import CustomTestCase
from contexteval.contextualizers import PrecomputedContextualizer
from contexteval.data.dataset_readers import ConllXPOSDatasetReader

class TestConllXPOSDatasetReader():
    data_path = CustomTestCase.FIXTURES_ROOT / "data" / "pos" / "wsj.conllx"
    contextualizer_path = (CustomTestCase.FIXTURES_ROOT / "contextualizers" /
                           "precomputed_elmo" / "elmo_layers_all.hdf5")

    @pytest.mark.parametrize('lazy', (True, False))
    @pytest.mark.parametrize('use_contextualizer', (True, False))
    def test_read_from_file(self, lazy, use_contextualizer):
        # Set up contextualizer, if using.
        contextualizer = None
        if use_contextualizer:
            contextualizer = PrecomputedContextualizer(self.contextualizer_path)
        reader = ConllXPOSDatasetReader(contextualizer=contextualizer,
                                        lazy=lazy)
        instances = list(reader.read(str(self.data_path)))

        # First read instance
        instance = instances[0]
        fields = instance.fields
        assert [token.metadata for token in fields["raw_tokens"].field_list] == [
            'Pierre', 'Vinken', ',', '61', 'years', 'old', ',', 'will', 'join', 'the', 'board', 'as',
            'a', 'nonexecutive', 'director', 'Nov.', '29', '.']
        assert fields["labels"].labels == [
            'NNP', 'NNP', ',', 'CD', 'NNS', 'JJ', ',', 'MD', 'VB', 'DT', 'NN',
            'IN', 'DT', 'JJ', 'NN', 'NNP', 'CD', '.']
        if use_contextualizer:
            assert_allclose(
                fields["token_representations"].array[:, :2],
                np.array([[-2.2218986, -2.451917], [-0.7551352, -0.7919447],
                          [-0.9525466, -0.985806], [-1.4567664, 0.1637534],
                          [0.21862003, -0.00878072], [-0.7341557, -0.57776076],
                          [-1.6816409, -1.2562131], [-0.9079286, 0.15607932],
                          [-0.44011104, -0.3434037], [0.56341827, -0.97181696],
                          [-0.7166206, -0.33435553], [-0.14051008, -1.260754],
                          [0.42426592, -0.35762805], [-1.0153385, -0.7949409],
                          [-0.7065723, 0.05164766], [-0.11002721, -0.11039695],
                          [0.41112524, 0.27260625], [-1.0369725, -0.6278316]]),
                rtol=1e-4)

        # Second read instance
        instance = instances[1]
        fields = instance.fields
        assert [token.metadata for token in fields["raw_tokens"].field_list] == [
            'Mr.', 'Vinken', 'is', 'chairman', 'of', 'Elsevier', 'N.V.', ',', 'the', 'Dutch',
            'publishing', 'group', '.']
        assert fields["labels"].labels == [
            'NNP', 'NNP', 'VBZ', 'NN', 'IN', 'NNP', 'NNP', ',', 'DT', 'NNP', 'VBG', 'NN', '.']
        if use_contextualizer:
            assert_allclose(
                fields["token_representations"].array[:, :2],
                np.array([[0.7069745, -0.5422047], [-1.8885247, -1.4432149],
                          [-1.7570897, -1.1201282], [-1.2288755, -0.8003752],
                          [-0.08672556, -0.99020493], [-0.6416313, -1.147429],
                          [-0.7924329, 0.14809224], [-1.0645872, -1.0505759],
                          [0.69725895, -0.8735154], [0.27878952, -0.339666],
                          [0.20708983, -0.7103262], [-1.1115363, -0.16295972],
                          [-1.3495405, -0.8656957]]),
                rtol=1e-4)

    @pytest.mark.parametrize('lazy', (True, False))
    @pytest.mark.parametrize('max_instances', (1, 2, 0.5, 0.75, 1.0))
    def test_reproducible_with_and_without_contextualization(self, lazy, max_instances):
        uncontextualized_params = Params({
            "max_instances": max_instances,
            "lazy": lazy})
        uncontextualized_reader = ConllXPOSDatasetReader.from_params(uncontextualized_params)
        uncontextualized_instances = list(uncontextualized_reader.read(str(self.data_path)))

        contextualized_params = Params({
            "lazy": lazy,
            "max_instances": max_instances,
            "contextualizer": {
                "type": "precomputed_contextualizer",
                "representations_path": self.contextualizer_path
            }})
        contextualized_reader = ConllXPOSDatasetReader.from_params(contextualized_params)
        contextualized_instances = list(contextualized_reader.read(str(self.data_path)))
        # Assert they are the same
        for uncontextualized_instance, contextualized_instance in zip(uncontextualized_instances,
                                                                      contextualized_instances):
            assert ([token.metadata for token in uncontextualized_instance.fields["raw_tokens"].field_list] ==
                    [token.metadata for token in contextualized_instance.fields["raw_tokens"].field_list])
            assert (uncontextualized_instance.fields["labels"].labels ==
                    contextualized_instance.fields["labels"].labels)
            contextualized_extra_keys = list(set(contextualized_instance.fields.keys()) -
                                             set(uncontextualized_instance.fields.keys()))
            assert (set(contextualized_extra_keys) == set(["token_representations"]))

    @pytest.mark.parametrize('lazy', (True, False))
    @pytest.mark.parametrize('use_contextualizer', (True, False))
    @pytest.mark.parametrize('max_instances', (1, 2, 0.5, 0.75, 1.0))
    def test_truncation(self, lazy, use_contextualizer, max_instances):
        # Set up contextualizer, if using.
        contextualizer = None
        if use_contextualizer:
            contextualizer = PrecomputedContextualizer(self.contextualizer_path)
        reader = ConllXPOSDatasetReader(
            contextualizer=contextualizer,
            max_instances=max_instances,
            lazy=lazy)
        instances = list(reader.read(str(self.data_path)))
        num_total_instances = 2
        max_instances_to_num_instances = {
            int(1): 1,
            int(2): 2,
            0.5: int(num_total_instances * 0.5),
            0.75: int(num_total_instances * 0.75),
            1.0: num_total_instances}
        assert len(instances) == max_instances_to_num_instances[max_instances]


#!/usr/bin/env python

from __future__ import absolute_import, division, print_function
import os
import time
import numpy as np
import tflearn
import tensorflow as tf
from numba import cuda

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '0'

from tensorflow.python.client import device_lib
def print_gpu_info():
    for device in device_lib.list_local_devices():
        print(device.name, 'memory_limit', str(round(device.memory_limit/1024/1024))+'M',
            device.physical_device_desc)
    print('=======================')

print_gpu_info()

DATA_PATH = "/Volumes/Cloud/DataSet"

mnist = tflearn.datasets.mnist.read_data_sets(DATA_PATH+"/mnist", one_hot=True)

config = tf.ConfigProto()
config.log_device_placement = True
config.allow_soft_placement = True

config.gpu_options.allocator_type = 'BFC'
config.gpu_options.allow_growth = True
config.gpu_options.per_process_gpu_memory_fraction = 0.3

# Building convolutional network
net = tflearn.input_data(shape=[None, 28, 28, 1], name='input')
net = tflearn.conv_2d(net, 32, 5, weights_init='variance_scaling', activation='relu', regularizer="L2")
net = tflearn.conv_2d(net, 64, 5, weights_init='variance_scaling', activation='relu', regularizer="L2")
net = tflearn.fully_connected(net, 10, activation='softmax')
net = tflearn.regression(net,
                         optimizer='adam',
                         learning_rate=0.01,
                         loss='categorical_crossentropy',
                         name='target')

# Training
model = tflearn.DNN(net, tensorboard_verbose=3)

start_time = time.time()
model.fit(mnist.train.images.reshape([-1, 28, 28, 1]),
          mnist.train.labels.astype(np.int32),
          validation_set=(
              mnist.test.images.reshape([-1, 28, 28, 1]),
              mnist.test.labels.astype(np.int32)
          ),
          n_epoch=1,
          batch_size=128,
          shuffle=True,
          show_metric=True,
          run_id='cnn_mnist_tflearn')

duration = time.time() - start_time
print('Training Duration %.3f sec' % (duration))

cuda.select_device(0)
cuda.close()


from rsf.proj import *
from rsf.gallery import sigsbee

par = {
    'nz':1201, 'dz':0.00762, 'oz':0.0,   # Depth
    'nx':2133, 'dx':0.01143, 'ox':3.055, # Lateral
    'smz':13, 'smx':9,                   # Smoothing radius for slowness
    'na':720,                            # Number of take-off angles
    'oazmin':80,'oazmax':50,             # Opening angle mute
    'dazmin':100,'dazmax':40,            # Dip angle mute
    'ts':4,'th':6,                       # Tapering in traces (shot, receiver axes)
    'smax':1.0,'hmax':1.0,               # Escape tables filter
# Screen params for the model
    'scrpar':'''screenratio=0.375 screenht=5
                yll=2.0 xll=1.5 crowd=0.8 labelsz=6
                o2num=1.0 d2num=2.0 n2tic=5
                o1num=4.0 d1num=2.0 n1tic=12
                barmove=y tickscale=0.3
                barwidth=0.2 label2= unit2=''',
# Screen params for CIGs
    'scrpar2':'''yreverse=y wanttitle=n wherexlabel=top
                 transp=y poly=y plotcol=7 gridcol=8
                 screenht=10 screenratio=1.25
                 unit2="\F16 \^g\_\F-1 "'''}

# Prepare model
###############

sigsbee.getvel('vel','migvel')

Result('sigsbvel','vel',
       '''
       grey title= wantitle=n allpos=y color=j
            pclip=100 scalebar=y bartype=h
            barlabel="V" barunit="km/s"
            dbarnum=1.0 obarnum=1.5
            bias=1.0 minval=1.5 maxval=4.5
            %(scrpar)s
       ''' % par)

Flow('vspl','vel','bspvel2 verb=y')

# Escape tables with ray tracing in reduced phase space
###############

Flow('sigsbnesc','vel vspl',
     '''
     escrt2 verb=y na=%(na)d vspl=${SOURCES[1]}
     ''' % par, split=[2,par['nx'],[0]], reduce='cat axis=4')

# Angle migration
###############

sigsbee.getshots('shots')

Result('shots','byte | grey3 flat=n frame1=250 frame2=80 frame3=100 title=Shots')

Flow('data','shots',
     '''
     window min1=2.0 |
     mutter t0=0.5 v0=1.5 half=n |
     pcrdata2 absoff=n KMAH=y filter=y verb=y
     ''')

# Migration
Flow('sigsbocram sigsbdcram sigsboill sigsbdill sigsbosmb',
     'sigsbnesc data vel',
     '''
     cram2 data=${SOURCES[1]} vz=${SOURCES[2]}
           mute=y oazmin=%(oazmin)g oazmax=%(oazmax)g
                  dazmin=%(dazmin)g dazmax=%(dazmax)g
           ts=%(ts)d th=%(th)d smax=%(smax)g hmax=%(hmax)g
           dipagath=${TARGETS[1]}
           imap=${TARGETS[2]} dipimap=${TARGETS[3]}
           smap=${TARGETS[4]}
     ''' % par, split=[4,par['nx'],[0]], reduce='cat axis=3')

# Plotting
###############

# Stack
Flow('sigsbdcrstk','sigsbdcram','stack axis=1')
#Flow('sigsbdcrstk',['sigsbdill','sigsbdcram'],'''
#     dd type=float |
#     math d=${SOURCES[1]} output="d/(input+1)" |
#     stack axis=1
#     ''')

Result('sigsbdcrstk','''
       grey title= wantitle=n pclip=99.3 scalebar=n
            %(scrpar)s
       ''' % par)

# Image gathers
cigs = [7,11,15,19,23]
for i in range(len(cigs)):
    ocig = 'sigsbocig%d' % i
    dcig = 'sigsbdcig%d' % i
    Result(ocig,'sigsbocram','''
           window sfwindow min3=%g max3=%g j1=3 | transp |
           wiggle pclip=99.8 zplot=1.2 label2="\F10 g \F-1 " %s
           ''' % (cigs[i],cigs[i],par['scrpar2']))
    Result(dcig,'sigsbdcram','''
           window sfwindow min3=%g max3=%g j1=8 | transp |
           wiggle pclip=99.8 zplot=1.2 label2="\F10 v \F-1 " %s
           ''' % (cigs[i],cigs[i],par['scrpar2']))

End()


import argparse
from PCM import PCM
from header import Header, ChannelModeInfo
from main_data import MainData
from side_info import SideInfo
from utils.bit import byte2str

class MP3File(object):
    """
    MP3File : look for frames and break them up into headers and data, meanwhile decoding then into PCM.
    """

    def __init__(self, mp3_file:str):
        self.filename = mp3_file
        self.position = 0
        # open file, read data into header and data frame objects
        with open(mp3_file, 'rb') as audio:
            # read audio data into a buffer. stop once we've reached the first mp3 frame
            buf = audio.read(2)
            while self._is_not_frame_start(buf[-2], buf[-1]):
                buf += audio.read(1)
            # should we save the start location of the mp3 data? Yes
            self.position = audio.tell() - 2
        # print(self.position)
        self.previous_frame_size = 0
        # keep a buffer of main data from previous frames. when we need to read main data
        # we will follow one of the following:
        #   - read all of the main data from the buffer
        #   - read the rest of the buffer, then some of the main data in the current physical frame
        #   - read the main data from immediately after the side information
        self.main_data_buffer = b''
        self.PCM_buffer = PCM()

    def read_frames(self, nframes=-1):
        """
        read n frames, save decoding PCM in PCM.buffer
        default: read all frames
        """
        frames_count = 0
        if nframes == 0:
            return
        with open(self.filename, 'rb') as audio:
            still_reading = True
            audio.seek(self.position)
            while still_reading:
                if frames_count == nframes:
                    break
                print("\n>>> Start decoding frame: %d"%frames_count)
                print('reading header starting at byte offset: {}'.format(audio.tell()))
                # Read the 4 header bytes
                buf = audio.read(4)
                bytes_str = byte2str(buf,4)
                header = Header(bytes_str)
                non_main_data_len = 4

                if header.protection == '1':
                    # TODO: CRC check
                    audio.read(2)
                    non_main_data_len += 2

                # if mono: side info is 17 bytes; else: 32
                side_info_length = 17 if header.channel_mode == ChannelModeInfo.MONO else 32
                print("channel mode: ",header.channel_mode)
                print("reading side info at byte offset: {}".format(audio.tell()))
                side_info_bytes = audio.read(side_info_length)
                bytes_str = byte2str(side_info_bytes,side_info_length)
                side_info = SideInfo(bytes_str,header.channel_mode)

                # print("side info granules: {}".format(side_info.granules))
                # print("side info main_data_begin: {}".format(side_info.main_data_begin))
                # print("side info scfsi_band: {}".format(side_info.scfsi))
                non_main_data_len += side_info_length

                main_data_length = header.frame_size - non_main_data_len # main data size in current frame
                print("reading main data at byte offset: {}\ntotal bits length: {}".format(audio.tell(),main_data_length))
                # read until the next header so we have all the main data we could possibly want

                read_bytes_count = 0
                while True:
                    new_byte = audio.peek(1)
                    if new_byte == b'':
                        still_reading = False
                        break
                    # if self.main_data_buffer == b'':
                    if read_bytes_count==0:
                        self.main_data_buffer += audio.read(2)
                        read_bytes_count+=2
                    else:
                        self.main_data_buffer += audio.read(1)
                        read_bytes_count+=1

                    if read_bytes_count >= main_data_length and not self._is_not_frame_start(self.main_data_buffer[-2], self.main_data_buffer[-1]):
                        # we've stumbled upon a new frame. return the file back to the start
                        # of the header and remove the last two bytes from the main data buffer
                        print('- read bytes: %d, find next frame, current position: %d -'%(read_bytes_count,audio.tell()-2))
                        audio.seek(audio.tell() - 2)
                        self.main_data_buffer = self.main_data_buffer[:-2]
                        break
                # At this point, we now have all the main data up until the start of the next frame

                # calculate the position at which to start reading the main data
                # then read the main data into a buffer and send that buffer somewhere
                # so that we might one day hope to know the scaling factors
                print("- main_data_begin:%-5d frame_size:%-5d main_data_length:%-5d -"
                      %(side_info.main_data_begin,header.frame_size, main_data_length))

                # main_data_bytes = self.main_data_buffer[:main_data_length]
                # self.main_data_buffer = self.main_data_buffer[main_data_length:] # remaining data would be used in next frame.

                # TODO: check data length
                this_frame_data_length = side_info.main_data_begin+main_data_length
                main_data_bytes = self.main_data_buffer[-this_frame_data_length:]
                self.main_data_buffer = self.main_data_buffer[-this_frame_data_length:]

                bytes_str = byte2str(main_data_bytes,this_frame_data_length)
                print("- main data buffer size:%d"%len(main_data_bytes))
                print("- main data length:%d" % (this_frame_data_length))
                main_data = MainData(header, side_info, bytes_str)

                if not self.PCM_buffer.is_init:
                    # provide information, e.g. sampling rate
                    # TODO: check sampling rate
                    self.PCM_buffer.set_params(main_data.channel_num,2,16000)
                self.PCM_buffer.push(main_data.pcm_output)

                frames_count+=1

            self.position = audio.tell()

    def _is_not_frame_start(self, byte1, byte2):
        '''
        header would start with FF FF or FF FB
        '''
        return (byte1 != 0xFF or (byte2 & 0xF0 != 240 and byte2 & 0xE0 != 224))

    def save_as_wav(self,filename:str):
        '''
        save decoding PCM as .wav format file
        '''
        self.PCM_buffer.flush(filename)

if __name__=='__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("mp3file",help="the MP3's file path")
    args = parser.parse_args()
    mp3_file=args.mp3file
    print(mp3_file)
    mp3 = MP3File(mp3_file)
    mp3.read_frames()
    mp3.save_as_wav(mp3_file[:-4]+'.wav')


def main():
    user_input = input("camelCase: ").strip()
    i:int = 0
    output = []
    offset:int = 0

    # loop through the entire string and print each character
    while i < (len(user_input)):
        letter:str = user_input[i]
        if letter.isupper() == True:
            output.append("_")
            output.append(letter.lower())
        else:
            output.append(letter)
        i += 1

    output = "".join(output)
    print ("snake_case:", output)

main()


"""
Trend strategy.

"""

from pyalgotrade.strategy import BacktestingStrategy
from pyalgotrade.technical import ma

class SMAStrategy(BacktestingStrategy):

    def __init__(self, feed, instrument):
        super(SMAStrategy, self).__init__(feed)

        # We want a 15 period SMA over the closing prices.
        self.__sma = ma.SMA(feed[instrument].getCloseDataSeries(), 15)
        self.__instrument = instrument

    def onBars(self, bars):
        bar = bars[self.__instrument]
        self.info("%s %s" % (bar.getClose(), self.__sma[-1]))


import pygame, config
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL.shaders import *

from pygame.constants import HWSURFACE, OPENGL, DOUBLEBUF

from sprite import Sprite

_shader_registry=dict()

def init_gl_viewport( viewport=(800,600),
                      display_flags=HWSURFACE | OPENGL | DOUBLEBUF,
                      screen_extents=(-5, 5, -5, 5),
                      clipping_planes=(1,100),
                      clear_color=(0.0,0.0,0.0,0.0)):

    pygame.init()

    srf = pygame.display.set_mode(viewport, display_flags )

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    width, height = viewport

    aspect_ratio=(float(width)/float(height))

    horizontal_midpoint=(screen_extents[0]+screen_extents[1])/2
    vertical_distance=screen_extents[3]-screen_extents[2]

    glOrtho( horizontal_midpoint-(vertical_distance*aspect_ratio)/2,
             horizontal_midpoint+(vertical_distance*aspect_ratio)/2,
             screen_extents[2],
             screen_extents[3],
             clipping_planes[0],
             clipping_planes[1] )

    glMatrixMode(GL_MODELVIEW)
    glEnable(GL_COLOR_MATERIAL)

    glEnable(GL_CULL_FACE)
    glEnable (GL_BLEND)
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

    glClearColor(0.0, 0.0, 0.0, 0.0)

    Sprite.init()

    if config.enable_shaders:
        for (shader_filenames, shader) in _shader_registry.items():
            if shader is None:
                shader_files=map(load_file, shader_filenames)
                _shader_registry[shader_filenames]=load_shaders(*shader_files)

    return ( horizontal_midpoint-(vertical_distance*aspect_ratio)/2,
             horizontal_midpoint+(vertical_distance*aspect_ratio)/2,
             screen_extents[2],
             screen_extents[3] )

def setup_light( gl_light_index=GL_LIGHT0,
                 ambient=(0.2,0.2,0.2,1.0),
                 diffuse=(1.0,1.0,1.0,1.0),
                 specular=(0.0,0.0,0.0,1.0),
                 position=(0.0,0.0,0.0,0.0) ):
    glLightfv(gl_light_index, GL_POSITION,  position)
    glLightfv(gl_light_index, GL_AMBIENT,   ambient)
    glLightfv(gl_light_index, GL_DIFFUSE,   diffuse)
    glLightfv(gl_light_index, GL_SPECULAR,  specular)
    glEnable(gl_light_index)
    glEnable(GL_LIGHTING)

def load_shaders(vertex_shader, fragment_shader):
    return compileProgram(compileShader(vertex_shader, GL_VERTEX_SHADER),
                          compileShader(fragment_shader, GL_FRAGMENT_SHADER))

def setup_for_shader(shader):
    if config.enable_shaders:
        glUseProgram(shader)
    else:
        glUseProgram(0)

def teardown_for_shader():
    glUseProgram(0)

def setup_for_sprites():
    glDisable(GL_LIGHTING)
    glDisable(GL_DEPTH_TEST)
    glEnable (GL_BLEND)
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
    glEnable(GL_TEXTURE_2D)
    glPolygonMode( GL_FRONT_AND_BACK, GL_FILL )

def teardown_for_sprites():
    glEnable(GL_LIGHTING)
    glEnable(GL_DEPTH_TEST)

    glEnable (GL_BLEND)
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
    glEnable(GL_TEXTURE_2D)

def setup_for_debug():
    glDisable(GL_LIGHTING)
    glDisable(GL_DEPTH_TEST)
    glDisable(GL_TEXTURE_2D)
#    glEnable(GL_POINT_SMOOTH)

def teardown_for_debug():
    glEnable(GL_TEXTURE_2D)
    glEnable(GL_DEPTH_TEST)
    glEnable(GL_LIGHTING)

def sprites_configuration( draw_fxn ):
    def _wrapped(*args,**kwargs):
        setup_for_sprites()
        draw_fxn(*args,**kwargs)
        teardown_for_sprites()
    return _wrapped

def shader_configuration(vertex_shader_path, fragment_shader_path):

    _shader_registry[(vertex_shader_path,fragment_shader_path)]=None

    def _wrapped_decorator(draw_fxn):
        def _wrapped(*args,**kwargs):

            setup_for_shader(_shader_registry[(vertex_shader_path,
	                                           fragment_shader_path)])
            draw_fxn(*args,**kwargs)
            teardown_for_shader()

        return _wrapped
    return _wrapped_decorator

def debug_configuration(color=(1.0,1.0,1.0), point_size=1.0, line_width=1.0 ):
    def _wrapped_decorator(draw_fxn):
        def _wrapped(*args, **kwargs):
            setup_for_debug()
            glColor(color)
            glPointSize(point_size)
            glLineWidth(line_width)
            draw_fxn(*args,**kwargs)
            teardown_for_debug()
        return _wrapped
    return _wrapped_decorator

def load_file(filename):
    f=open(filename)
    lines=f.readlines()
    f.close()
    return "\n".join(lines)


from __future__ import annotations

from asyncio import (CancelledError, Task, Queue, # pylint: disable=unused-import; typing
                     create_task, ensure_future, get_event_loop, shield, sleep)
import builtins
from datetime import datetime, timedelta, timezone
from email.message import EmailMessage
from functools import partial
from inspect import isawaitable
import json
from logging import getLogger
from pathlib import Path
import re
from smtplib import SMTP
import string
import sys
from typing import (AsyncIterator, Awaitable, Callable, Coroutine, Dict, Generic, Iterator, List,
                    Optional, Set, Sequence, Tuple, Type, TypeVar, Union, cast, overload)
from urllib.parse import SplitResult, urlparse, urlsplit

from pywebpush import WebPusher, WebPushException
from py_vapid import Vapid
from py_vapid.utils import b64urlencode
from redis import StrictRedis
from redis.exceptions import ResponseError
from requests.exceptions import RequestException
from typing_extensions import Protocol

from . import error
from .core import Device, Devices, Object, RewriteFunc, context
from .error import ValueError
from .jsonredis import (ExpectFunc, JSONRedis, JSONRedisSequence, RedisList, RedisSequence,
                        RedisSortedSet, bzpoptimed)
from .ratelimit import RateLimit, RateLimiter
from .resource import ( # pylint: disable=unused-import; typing
    AnalysisError, Analyzer, Files, HandleResourceFunc, Image, Resource, Video, handle_image,
    handle_webpage, handle_youtube)
from .util import Expect, check_email, expect_opt_type, expect_type, randstr, str_or_none
from .webapi import CommunicationError

_PUSH_TTL = 24 * 60 * 60

O = TypeVar('O', bound='Object')

class JSONifiable(Protocol):
    """Object which can be encoded to and decoded from a JSON representation."""

    def __init__(self, **kwargs: object) -> None:
        # pylint: disable=super-init-not-called; protocol
        pass

    def json(self, restricted: bool = False, include: bool = False, *,
             rewrite: RewriteFunc = None) -> Dict[str, object]:
        """Return a JSON representation of the object.

        The name of the object type is included as ``__type__``.

        By default, all attributes are included. If *restricted* is ``True``, a restricted view of
        the object is returned, i.e. attributes that should not be available to the current
        :attr:`Application.user` are excluded. If *include* is ``True``, additional fields that may
        be of interest to the caller are included. If *rewrite* is given, it is applied to all URL
        attributes.
        """

class JSONifiableWithParse(JSONifiable, Protocol):
    # pylint: disable=missing-docstring; semi-public

    @staticmethod
    def parse(data: Dict[str, object], **args: object) -> JSONifiable:
        """Parse the given JSON *data* into an object.

        Additional keyword arguments *args* are passed to the constructor.
        """

class Application:
    """Social micro web app.

    .. attribute:: user

       Current :class:`User`. ``None`` means anonymous access.

    .. attribute:: users

       Collection of all :class:`User`s.

    .. attribute:: analytics

       Analytics unit.

    .. attribute:: redis_url

       See ``--redis-url`` command line option.

    .. attribute:: email

       Sender email address to use for outgoing email. Defaults to ``bot@localhost``.

    .. attribute:: smtp_url

       See ``--smtp-url`` command line option.

    .. attribute:: render_email_auth_message

       Hook function of the form *render_email_auth_message(email, auth_request, auth)*, responsible
       for rendering an email message for the authentication request *auth_request*. *email* is the
       email address to authenticate and *auth* is the secret authentication code.

    .. attribute:: video_service_keys

       See ``--video-service-keys`` command line option.

    .. attribute:: r

       :class:`Redis` database. More precisely a :class:`JSONRedis` instance.

    .. attribute:: files

       File storage. *files_path* is the directory where files are stored (see ``--files-path``
       command line option).

    .. attribute:: analyzer

       Web resource analyzer.

    .. attribute:: rate_limiter

       Subclass API: Mechanism to limit the rate of operations per client.
    """

    def __init__(
            self, redis_url: str = '', email: str = 'bot@localhost', smtp_url: str = '',
            render_email_auth_message: Callable[[str, 'AuthRequest', str], str] = None, *,
            files_path: str = 'data', video_service_keys: Dict[str, str] = {}) -> None:
        check_email(email)
        try:
            # pylint: disable=pointless-statement; port errors are only triggered on access
            urlparse(smtp_url).port
        except builtins.ValueError as e:
            raise ValueError('smtp_url_invalid') from e

        self.redis_url = redis_url
        try:
            urlparts = urlsplit(self.redis_url)
            url = SplitResult(
                urlparts.scheme or 'redis', urlparts.netloc or 'localhost', urlparts.path,
                urlparts.query, urlparts.fragment
            ).geturl()
            self.r = JSONRedis(StrictRedis.from_url(url), self._encode, self._decode)
        except builtins.ValueError as e:
            raise ValueError('redis_url_invalid') from e
        self.files = Files(files_path)

        # pylint: disable=import-outside-toplevel; circular dependency
        from .analytics import Analytics, Referral
        self.types = {
            'User': User,
            'Device': Device,
            'Settings': Settings,
            'Activity': Activity,
            'Event': Event,
            'AuthRequest': AuthRequest,
            'Resource': Resource,
            'Image': Image,
            'Video': Video,
            'Referral': Referral
        } # type: Dict[str, Type[JSONifiable]]
        self.user = None # type: Optional[User]
        self.users = Users(self)
        self.devices = Devices(self)
        self.analytics = Analytics(app=self)

        self.email = email
        self.smtp_url = smtp_url
        self.render_email_auth_message = render_email_auth_message

        self.video_service_keys = video_service_keys
        handlers = [handle_image, handle_webpage] # type: List[HandleResourceFunc]
        if 'youtube' in self.video_service_keys:
            handlers.insert(0, handle_youtube(self.video_service_keys['youtube']))
        self.analyzer = Analyzer(handlers=handlers, files=self.files)
        self.rate_limiter = RateLimiter()

    @property
    def settings(self) -> 'Settings':
        """App :class:`Settings`."""
        return self.r.oget('Settings', default=AssertionError, expect=expect_type(Settings))

    @property
    def activity(self) -> 'Activity':
        """Global :class:`Activity` feed."""
        activity = self.r.oget('Activity', default=AssertionError, expect=expect_type(Activity))
        activity.pre = self.check_user_is_staff
        return activity

    @staticmethod
    def now() -> datetime:
        """Return the current UTC date and time, as aware object with second accuracy."""
        return datetime.now(timezone.utc).replace(microsecond=0)

    async def update(self) -> None:
        """Update the database.

        If the database is fresh, it will be initialized. If the database is already up-to-date,
        nothing will be done. It is thus safe to call :meth:`update` without knowing if an update is
        necessary or not.
        """
        Path(self.files.path).mkdir(parents=True, exist_ok=True)

        v = cast(Optional[bytes], self.r.get('micro_version'))
        # If fresh, initialize database
        if not v:
            settings = self.create_settings()
            settings.push_vapid_private_key, settings.push_vapid_public_key = (
                self._generate_push_vapid_keys()) # type: ignore
            self.r.oset(settings.id, settings)
            activity = Activity(id='Activity', app=self, subscriber_ids=[])
            self.r.oset(activity.id, activity)
            self.r.set('micro_version', 9)
            self.do_update()
            return

        v = int(v)
        r = JSONRedis[Dict[str, object]](self.r.r)
        r.caching = False

        user_updates = {}
        device_updates = {}
        users = r.omget([id.decode() for id in r.r.lrange('users', 0, -1)], default=AssertionError)
        for user in users:
            # Deprecated since 0.58.0
            if 'auth_secret' in user:
                device = Device(
                    id=f'Device:{randstr()}', app=self, auth_secret=user.pop('auth_secret'),
                    notification_status=user.pop('device_notification_status'),
                    push_subscription=user.pop('push_subscription'), user_id=user['id'])
                r.sadd('devices', device.id)
                authenticate_time = datetime.fromisoformat(cast(str, user['authenticate_time']))
                r.zadd(f"{user['id']}.devices", {device.id: -authenticate_time.timestamp()})
                r.hset('auth_secret_map', device.auth_secret, device.id)
                user_updates[cast(str, user['id'])] = user
                device_updates[device.id] = device.json()
        r.omset(user_updates)
        r.omset(device_updates)

        object_updates = {}
        for obj in self._scan_objects(r, WithContent):
            id = cast(str, obj['id'])
            resource = cast('dict[str, object] | None', obj['resource'])
            if resource:
                # Deprecated since 0.67.0
                if 'thumbnail' not in resource:
                    src = (resource if resource['__type__'] == 'Image'
                           else cast('dict[str, object] | None', resource['image']))
                    if src:
                        try:
                            thumbnail = await self.analyzer.thumbnail(cast(str, src['url']))
                        except (CommunicationError, AnalysisError):
                            thumbnail = await self.analyzer.thumbnail(
                                b'<svg xmlns="http://www.w3.org/2000/svg" />', 'image/svg+xml')
                        resource['thumbnail'] = thumbnail.json()
                    else:
                        resource['thumbnail'] = None
                    del resource['image']
                    object_updates[id] = obj

                # Deprecated since 0.69.0
                else:
                    thumbnail_data = cast('dict[str, object] | None', resource['thumbnail'])
                    if thumbnail_data and 'color' not in thumbnail_data:
                        thumbnail = await self.analyzer.thumbnail(cast(str, thumbnail_data['url']))
                        thumbnail_data['color'] = thumbnail.color
                        object_updates[id] = obj
        r.omset(object_updates)

        updates = {
            'User': len(user_updates),
            'Device': len(device_updates),
            'Object': len(object_updates),
            **self.do_update()
        }
        getLogger(__name__).info('Updated database\n%s',
                                 '\n'.join(f'{name}: {n}' for name, n in updates.items()))

    def do_update(self) -> Dict[str, int]:
        """Subclass API: Perform the database update.

        Information about the update, more precisely the number of entity updates by type, is
        returned.

        May be overridden by subclass. Called by :meth:`update`, which takes care of updating (or
        initializing) micro specific data. The default implementation does nothing.
        """
        # pylint: disable=no-self-use; part of subclass API
        return {}

    def create_user(self, data: Dict[str, object]) -> 'User':
        """Subclass API: Create a new user.

        *data* is the JSON data required for :class:`User`.

        May be overridden by subclass. Called by :meth:`login` for new users, which takes care of
        storing the object. By default, a standard :class:`User` is returned.
        """
        # pylint: disable=no-self-use; part of subclass API
        return User(**data) # type: ignore

    def create_settings(self) -> 'Settings':
        """Subclass API: Create and return the app :class:`Settings`.

        *id* must be set to ``Settings``.

        Must be overridden by subclass. Called by :meth:`update` when initializing the database.
        """
        raise NotImplementedError()

    def get_object(self, id, default=KeyError):
        """Get the :class:`Object` given by *id*.

        *default* is the value to return if no object with *id* is found. If it is an
        :exc:`Exception`, it is raised instead.
        """
        object = self.r.oget(id)
        if object is None:
            object = default
        if isinstance(object, Exception):
            raise object
        return object

    def check_user_is_staff(self) -> None:
        """Check if the current :attr:`user` is a staff member."""
        # pylint: disable=protected-access; Settings is a friend
        if not (self.user and self.user.id in self.settings._staff): # type: ignore
            raise error.PermissionError()

    def start_garbage_collect_files(self) -> 'Task[None]':
        """Start the :attr:`files` garbage collect job."""
        async def _run() -> None:
            t = self.now().replace(hour=0, minute=5, second=0)
            while True:
                t += timedelta(days=1)
                await sleep((t - self.now()).total_seconds())
                n = await self.files.garbage_collect(list(self.file_references()))
                getLogger(__name__).info('Garbage collected %d file(s)', n)
        return cast('Task[None]', get_event_loop().create_task(_run()))

    def start_empty_trash(self) -> 'Task[None]':
        """Start the empty trash job."""
        async def _empty_trash() -> None:
            loop = get_event_loop()
            while True:
                coro = cast(
                    Awaitable[Tuple[bytes, float]],
                    loop.run_in_executor(None, partial(bzpoptimed, self.r.r, 'micro_trash')))
                try:
                    id, _ = await shield(coro)
                except CancelledError:
                    # Note that we assume there is only a single consumer of micro_trash
                    self.r.r.zadd('micro_trash', {b'int': 0})
                    await coro
                    raise

                obj = self.r.oget(id.decode(), default=AssertionError,
                                  expect=expect_type(Trashable))
                # pylint: disable=broad-except; catch unhandled exceptions
                try:
                    obj.delete()
                except Exception as e:
                    t = (
                        datetime.now(timezone.utc) + Trashable.RETENTION).timestamp() # type: ignore
                    self.r.r.zadd('micro_trash', {id: t})
                    loop.call_exception_handler(
                        cast(Dict[str, object], {
                            'message': 'Unexpected exception in delete()',
                            'exception': e
                        }))
                del obj
        return cast('Task[None]', ensure_future(_empty_trash()))

    def file_references(self) -> Iterator[str]:
        """Subclass API: Iterate over all file references.

        Called by the :attr:`files` garbage collect job. The default implementation returns an empty
        iterator.
        """
        # pylint: disable=no-self-use; part of API
        return iter(())

    async def send_device_notification(self, push_subscription: str, event: Event) -> None:
        """Send an *event* notification to the device with *push_subscription*.

        If there is a communication issue, a :exc:`micro.error.CommunicationError` is raised.
        """
        try:
            push_subscription = json.loads(push_subscription)
            if not isinstance(push_subscription, dict):
                raise builtins.ValueError()
            urlparts = urlparse(push_subscription['endpoint'])
            pusher = WebPusher(push_subscription)
        except (builtins.ValueError, KeyError, WebPushException) as e:
            raise ValueError('push_subscription_invalid') from e

        # Unfortunately sign() tries to validate the email address
        email = 'bot@email.localhost' if self.email == 'bot@localhost' else self.email
        headers = Vapid.from_raw(self.settings.push_vapid_private_key.encode()).sign({
            'aud': '{}://{}'.format(urlparts.scheme, urlparts.netloc),
            'sub': 'mailto:{}'.format(email)
        })

        try:
            # Firefox does not yet support aes128gcm encoding (see
            # https://bugzilla.mozilla.org/show_bug.cgi?id=1525872)
            send = partial(pusher.send, json.dumps(event.json(restricted=True, include=True)),
                           headers=headers, ttl=_PUSH_TTL, content_encoding='aesgcm')
            response = await get_event_loop().run_in_executor(None, send)
        except RequestException as e:
            raise CommunicationError(f"{e} for POST {push_subscription['endpoint']}") from e
        if response.status_code in (404, 410):
            raise ValueError('push_subscription_invalid')
        if response.status_code != 201:
            raise CommunicationError(
                'Unexpected response status {} for POST {}'.format(response.status_code,
                                                                   push_subscription['endpoint']))

    @staticmethod
    def _encode(object: JSONifiable) -> Dict[str, object]:
        return object.json()

    def _decode(self, json: Dict[str, object]) -> Union[JSONifiable, Dict[str, object]]:
        # pylint: disable=redefined-outer-name; good name
        try:
            type = self.types[str(json['__type__'])]
        except KeyError:
            return json
        if hasattr(type, 'parse'):
            return cast(JSONifiableWithParse, type).parse(json, app=self)
        del json['__type__']
        return type(app=self, **json)

    def _scan_objects(self, r: JSONRedis[dict[str, object]],
                      cls: type[object] = None) -> Iterator[dict[str, object]]:
        for key in cast(List[bytes], r.keys('*')):
            try:
                obj = r.oget(key.decode(), default=AssertionError)
            except ResponseError:
                pass
            else:
                if ('__type__' in obj and
                        issubclass(self.types[expect_type(str)(obj['__type__'])], cls or Object)):
                    yield obj

    @staticmethod
    def _generate_push_vapid_keys():
        vapid = Vapid()
        vapid.generate_keys()
        return (b64urlencode(vapid.private_key.private_numbers().private_value.to_bytes(32, 'big')),
                b64urlencode(vapid.public_key.public_numbers().encode_point()))

class Gone:
    """See :ref:`Gone`."""

    def json(self, restricted: bool = False, include: bool = False, *,
             rewrite: RewriteFunc = None) -> Dict[str, object]:
        """See :meth:`JSONifiable.json`."""
        # pylint: disable=unused-argument; part of API
        return {'__type__': type(self).__name__}

class Editable:
    """:class:`Object` that can be edited."""

    id = None # type: str
    app = None # type: Application
    trashed = None # type: bool

    def __init__(self, authors: List[str],
                 activity: Union['Activity', Callable[[], 'Activity']] = None) -> None:
        self._authors = authors
        self.__activity = activity

    @property
    def authors(self) -> List['User']:
        # pylint: disable=missing-docstring; already documented
        return self.app.r.omget(self._authors, default=AssertionError, expect=expect_type(User))

    async def edit(self, **attrs: object) -> None:
        """See :http:post:`/api/(object-url)`."""
        if not self.app.user:
            raise error.PermissionError()
        if isinstance(self, Trashable) and self.trashed:
            raise ValueError('object_trashed')

        coro = self.do_edit(**attrs)
        if isawaitable(coro):
            await cast(Awaitable[None], coro)
        if not self.app.user.id in self._authors:
            self._authors.append(self.app.user.id)
        self.app.r.oset(self.id, self)

        if self.__activity is not None:
            activity = self.__activity() if callable(self.__activity) else self.__activity
            activity.publish(Event.create('editable-edit', self, app=self.app)) # type: ignore

    def do_edit(self, **attrs: object) -> Optional[Awaitable[None]]:
        """Subclass API: Perform the edit operation.

        More precisely, validate and then set the given *attrs*.

        Must be overridden by host. Called by :meth:`edit`, which takes care of basic permission
        checking, managing *authors* and storing the updated object in the database.
        """
        raise NotImplementedError()

    def json(self, restricted: bool = False, include: bool = False, *,
             rewrite: RewriteFunc = None) -> Dict[str, object]:
        """Subclass API: Return a JSON object representation of the editable part of the object."""
        return {
            'authors': [a.json(restricted=restricted, rewrite=rewrite) for a in self.authors]
                       if include else self._authors
        }

class Trashable:
    """Mixin for :class:`Object` which can be trashed and restored.

    .. attribute:: RETENTION

       Duration after a trashed object will permanently be deleted.
    """

    RETENTION = timedelta(days=7)

    id = None # type: str
    app = None # type: Application

    def __init__(self, trashed: bool,
                 activity: Union['Activity', Callable[[], 'Activity']] = None) -> None:
        self.trashed = trashed
        self.__activity = activity

    def delete(self) -> None:
        """Subclass API: Permanently delete the object."""
        raise NotImplementedError()

    def trash(self) -> None:
        """See :http:post:`/api/(object-url)/trash`."""
        if not self.app.user:
            raise error.PermissionError()
        if self.trashed:
            return

        self.trashed = True
        self.app.r.oset(self.id, self)
        self.app.r.r.zadd(
            'micro_trash',
            {self.id.encode(): (datetime.now(timezone.utc) + Trashable.RETENTION).timestamp()})
        if self.__activity is not None:
            activity = self.__activity() if callable(self.__activity) else self.__activity
            activity.publish(Event.create('trashable-trash', cast(Object, self), app=self.app))

    def restore(self) -> None:
        """See :http:post:`/api/(object-url)/restore`."""
        if not self.app.user:
            raise error.PermissionError()
        if not self.trashed:
            return

        self.trashed = False
        self.app.r.oset(self.id, self)
        self.app.r.r.zrem('micro_trash', self.id.encode())
        if self.__activity is not None:
            activity = self.__activity() if callable(self.__activity) else self.__activity
            activity.publish(Event.create('trashable-restore', cast(Object, self), app=self.app))

    def json(self, restricted: bool = False, include: bool = False, *,
             rewrite: RewriteFunc = None) -> Dict[str, object]:
        """Subclass API: Return a JSON representation of the trashable part of the object."""
        # pylint: disable=unused-argument; part of subclass API
        return {'trashed': self.trashed}

class WithContent:
    """:class:`Editable` :class:`Object` with content."""

    app = None # type: Application

    @staticmethod
    async def process_attrs(attrs: Dict[str, object], *, app: Application) -> Dict[str, object]:
        """Pre-Process the given attributes *attrs* for editing."""
        if 'text' in attrs:
            text = attrs['text']
            if text is not None:
                attrs['text'] = str_or_none(expect_type(str)(text))
        if 'resource' in attrs:
            url = attrs['resource']
            if url is not None:
                attrs['resource'] = await app.analyzer.analyze(expect_type(str)(url))
        return attrs

    def __init__(self, *, text: str = None, resource: Resource = None) -> None:
        self.text = text
        self.resource = resource

    async def pre_edit(self, attrs: Dict[str, object]) -> Dict[str, object]:
        """Prepare the edit operation.

        More precisely, validate and pre-process the given *attrs*.
        """
        if 'resource' in attrs and self.resource and attrs['resource'] == self.resource.url:
            del attrs['resource']
        return await self.process_attrs(attrs, app=self.app)

    def do_edit(self, **attrs: object) -> Optional[Awaitable[None]]: # type: ignore
        """See :meth:`Editable.do_edit`."""
        if 'text' in attrs:
            self.text = expect_opt_type(str)(attrs['text'])
        if 'resource' in attrs:
            self.resource = expect_opt_type(Resource)(attrs['resource'])

    def json(self, restricted: bool = False, include: bool = False, *,
             rewrite: RewriteFunc = None) -> Dict[str, object]:
        """See :meth:`JSONifiable.json`."""
        # pylint: disable=unused-argument; part of API
        return {
            'text': self.text,
            'resource': self.resource.json(rewrite=rewrite) if self.resource else None
        }

class Collection(Generic[O], Sequence[O]):
    """See :ref:`Collection`.

    .. attribute:: ids

       Redis sequence tracking the (IDs of) objects that the collection contains.

    .. attribute:: check

       Function of the form *check(key)*, which is called before an object is retrieved via *key*.
       May be ``None``.

    .. attribute:: expect

       Function narrowing the type of a retrieved object.

    .. attribute:: app

       Context application.

    .. describe:: c[key]

       *key* may also be a string, in which case the object with the given ID is retrieved.

       Redis operations: ``RedisSequence[k] + n``, where n is the number of retrieved items, or
       ``i in RedisSequence + 1`` if *key* is a string.

    .. describe:: item in c

       *item* may also be a string, in which case the membership of the object with the given ID is
       tested.
    """

    def __init__(
            self, ids: RedisSequence, *, check: Callable[[Union[int, slice, str]], None] = None,
            expect: ExpectFunc[O] = cast(ExpectFunc[O], expect_type(Object)),
            app: Application) -> None:
        self.ids = ids
        self.check = check
        self.expect = expect
        self.app = app

    def index(self, value: Union[O, str], start: int = 0, stop: int = sys.maxsize) -> int:
        """See :meth:`Sequence.index`.

        *x* may also be a string, in which case the index of the object with the given ID is
        returned.
        """
        if isinstance(value, Object):
            return self.index(value.id, start, stop)
        return self.ids.index(cast(str, value).encode(), start, stop)

    def __len__(self) -> int:
        return len(self.ids)

    @overload
    def __getitem__(self, key: Union[int, str]) -> O:
        # pylint: disable=function-redefined; overload
        pass
    @overload
    def __getitem__(self, key: slice) -> List[O]:
        # pylint: disable=function-redefined; overload
        pass
    def __getitem__(self, key: Union[int, slice, str]) -> Union[O, List[O]]:
        # pylint: disable=function-redefined; overload
        if self.check:
            self.check(key)
        if isinstance(key, str):
            if key not in self:
                raise KeyError()
            return self.app.r.oget(key, default=ReferenceError, expect=self.expect)
        if isinstance(key, slice): # type: ignore[misc]
            return self.app.r.omget([id.decode() for id in self.ids[key]],
                                    default=ReferenceError, expect=self.expect)
        return self.app.r.oget(self.ids[key].decode(), default=ReferenceError,
                               expect=self.expect)

    def __iter__(self) -> Iterator[O]:
        # Optimized and used by count()
        return iter(self[:])

    def __contains__(self, item: object) -> bool:
        if isinstance(item, Object):
            return item.id in self
        return isinstance(item, str) and item.encode() in self.ids

    def json(self, restricted: bool = False, include: bool = False, *, rewrite: RewriteFunc = None,
             slc: slice = None) -> Dict[str, object]:
        """See :meth:`JSONifiable.json`.

        *slc* is a slice of items to include, if any.
        """
        count = len(self)
        if slc:
            items = self[slc]
            start = 0 if cast(Optional[int], slc.start) is None else cast(int, slc.start)
            stop = start + len(items)
        return {
            'count': count,
            **(
                {
                    'items': [item.json(restricted=restricted, include=include, rewrite=rewrite)
                              for item in items],
                    'slice': [start, stop]
                } if slc else {})
        }

class Orderable:
    """Mixin for :class:`Collection` whose items can be ordered.

    The underlying Redis collection must be a Redis list.
    """

    ids = None # type: RedisSequence
    app = None # type: Application

    def move(self, item: Object, to: Optional[Object]) -> None:
        """See :http:post:`/api/(collection-path)/move`."""
        if to:
            if to.id not in self: # type: ignore
                raise ValueError('to_not_found')
            if to == item:
                # No op
                return
        if not cast(int, self.app.r.lrem(self.ids.key, 1, item.id)):
            raise ValueError('item_not_found')
        if to:
            self.app.r.linsert(self.ids.key, 'after', to.id, item.id)
        else:
            self.app.r.lpush(self.ids.key, item.id)

class User(Object, Editable):
    """See :ref:`User`."""

    def __init__(self, *, app: Application, **data: Dict[str, object]) -> None:
        super().__init__(id=cast(str, data['id']), app=app)
        Editable.__init__(self, authors=cast(List[str], data['authors']))
        self.name = cast(str, data['name'])
        self.email = cast(Optional[str], data['email'])
        self.create_time = datetime.fromisoformat(cast(str, data['create_time']))
        self.authenticate_time = datetime.fromisoformat(cast(str, data['authenticate_time']))
        self.devices = Collection(
            RedisSortedSet(f'{self.id}.devices', app.r.r), check=self._check_user,
            expect=expect_type(Device), app=app)

    def store_email(self, email: str) -> None:
        """Update the user's *email* address.

        If *email* is already associated with another user, a :exc:`ValueError`
        (``email_duplicate``) is raised.
        """
        check_email(email)
        id = self.app.r.r.hget('user_email_map', email.encode())
        if id and id.decode() != self.id:
            raise ValueError('email_duplicate')

        if self.email:
            self.app.r.hdel('user_email_map', self.email)
        self.email = email
        self.app.r.oset(self.id, self)
        self.app.r.hset('user_email_map', self.email, self.id)

    def set_email(self, email: str) -> 'AuthRequest':
        """See :http:post:`/api/users/(id)/set-email`."""
        if self.app.user != self:
            raise error.PermissionError()
        check_email(email)

        code = randstr(length=5, charset=string.ascii_uppercase)
        auth_request = AuthRequest(id='AuthRequest:' + randstr(), app=self.app, email=email,
                                   code=code)
        self.app.r.oset(auth_request.id, auth_request)
        self.app.r.expire(auth_request.id, 10 * 60)
        if self.app.render_email_auth_message:
            self._send_email(email, self.app.render_email_auth_message(email, auth_request, code))
        return auth_request

    def finish_set_email(self, auth_request: 'AuthRequest', auth: str) -> None:
        """See :http:post:`/api/users/(id)/finish-set-email`."""
        # pylint: disable=protected-access; auth_request is a friend
        if self.app.user != self:
            raise error.PermissionError()
        auth_request.verify(auth)
        self.app.r.delete(auth_request.id)
        self.store_email(auth_request._email)

    def remove_email(self):
        """See :http:post:`/api/users/(id)/remove-email`."""
        if self.app.user != self:
            raise error.PermissionError()
        if not self.email:
            raise ValueError('user_no_email')

        self.app.r.hdel('user_email_map', self.email)
        self.email = None
        self.app.r.oset(self.id, self)

    def send_email(self, msg):
        """Send an email message to the user.

        *msg* is the message string of the following form: It starts with a line containing the
        subject prefixed with ``Subject:_``, followed by a blank line, followed by the body.

        If the user's ::attr:`email` is not set, a :exc:`ValueError` (``user_no_email``) is raised.
        If communication with the SMTP server fails, an :class:`EmailError` is raised.
        """
        if not self.email:
            raise ValueError('user_no_email')
        self._send_email(self.email, msg)

    def notify(self, event: Event) -> None:
        """Notify the user about *event*.

        The notification is delivered to all enabled devices. If a device's *push_subscription* has
        expired, notifications are disabled for that device.
        """
        devices = self.app.r.omget([id.decode() for id in self.devices.ids], default=AssertionError,
                                   expect=expect_type(Device))
        for device in devices:
            if device.notification_status == 'on':
                create_task(self._notify(device, event))

    def do_edit(self, **attrs):
        if self.app.user != self:
            raise error.PermissionError()

        e = InputError()
        if 'name' in attrs and not str_or_none(attrs['name']):
            e.errors['name'] = 'empty'
        e.trigger()

        if 'name' in attrs:
            self.name = attrs['name']

    def json(self, restricted: bool = False, include: bool = False, *,
             rewrite: RewriteFunc = None) -> dict[str, object]:
        """See :meth:`Object.json`."""
        return {
            **super().json(restricted=restricted, include=include, rewrite=rewrite),
            **Editable.json(self, restricted=restricted, include=include, rewrite=rewrite),
            'name': self.name,
            **(
                {} if restricted and context.user.get() != self else {
                    'email': self.email,
                    'create_time': self.create_time.isoformat(),
                    'authenticate_time': self.authenticate_time.isoformat()
                })
        }

    def _send_email(self, to: str, msg: str) -> None:
        match = re.fullmatch(r'Subject: ([^\n]+)\n\n(.+)', msg, re.DOTALL)
        if not match:
            raise ValueError('msg_invalid')

        msg = EmailMessage()
        msg['To'] = to
        msg['From'] = self.app.email
        msg['Subject'] = match.group(1)
        msg.set_content(match.group(2))

        components = urlparse(self.app.smtp_url)
        host = components.hostname or 'localhost'
        port = components.port or 25
        try:
            with SMTP(host=host, port=port) as smtp:
                smtp.send_message(msg)
        except OSError as e:
            raise EmailError() from e

    async def _notify(self, device: Device, event: Event) -> None:
        try:
            assert device.push_subscription
            await self.app.send_device_notification(device.push_subscription, event)
        except (ValueError, CommunicationError) as e:
            if isinstance(e, ValueError):
                if str(e) != 'push_subscription_invalid':
                    raise e
                device.abort_notifications()
            getLogger(__name__).error('Failed to deliver notification: %s', str(e))

    def _check_user(self, _: Union[int, slice, str]) -> None:
        if context.user.get() != self:
            raise error.PermissionError()

class Users:
    """See :ref:`Users`."""

    def __init__(self, app: Application) -> None:
        self.app = app
        self._ids = RedisList('users', app.r.r)

    def __len__(self) -> int:
        return len(self._ids)

    def __getitem__(self, key: str) -> User:
        if not key.startswith('User:'):
            raise KeyError(key)
        return self.app.r.oget(key, default=KeyError, expect=expect_type(User))

    def __iter__(self) -> Iterator[User]:
        users = self.app.r.omget([id.decode() for id in self._ids], default=AssertionError,
                                 expect=expect_type(User))
        return iter(users)

class Settings(Object, Editable):
    """See :ref:`Settings`.

    .. attribute:: push_vapid_private_key

       VAPID private key used for sending device notifications.
    """

    def __init__(
            self, *, id: str, app: Application, authors: List[str], title: str, icon: Optional[str],
            icon_small: str = None, icon_large: str = None, provider_name: Optional[str],
            provider_url: Optional[str], provider_description: Optional[Dict[str, str]],
            feedback_url: Optional[str], staff: List[str], push_vapid_private_key: str,
            push_vapid_public_key: str) -> None:
        super().__init__(id, app)
        Editable.__init__(self, authors=authors, activity=lambda: app.activity)
        self.title = title
        self.icon = icon
        self.icon_small = icon_small
        self.icon_large = icon_large
        self.provider_name = provider_name
        self.provider_url = provider_url
        self.provider_description = provider_description
        self.feedback_url = feedback_url
        self._staff = staff
        self.push_vapid_private_key = push_vapid_private_key
        self.push_vapid_public_key = push_vapid_public_key

    @property
    def staff(self) -> List[User]:
        # pylint: disable=missing-docstring; already documented
        return self.app.r.omget(self._staff, default=AssertionError, expect=expect_type(User))

    def do_edit(self, **attrs):
        if not self.app.user.id in self._staff:
            raise error.PermissionError()

        e = InputError()
        if 'title' in attrs and not str_or_none(attrs['title']):
            e.errors['title'] = 'empty'
        e.trigger()

        if 'title' in attrs:
            self.title = attrs['title']
        if 'icon' in attrs:
            self.icon = str_or_none(attrs['icon'])
        if 'icon_small' in attrs:
            self.icon_small = str_or_none(attrs['icon_small'])
        if 'icon_large' in attrs:
            self.icon_large = str_or_none(attrs['icon_large'])
        if 'provider_name' in attrs:
            self.provider_name = str_or_none(attrs['provider_name'])
        if 'provider_url' in attrs:
            self.provider_url = str_or_none(attrs['provider_url'])
        if 'provider_description' in attrs:
            self.provider_description = attrs['provider_description']
        if 'feedback_url' in attrs:
            self.feedback_url = str_or_none(attrs['feedback_url'])

    def json(self, restricted=False, include=False, *, rewrite=None):
        return {
            **super().json(restricted=restricted, include=include, rewrite=rewrite),
            **Editable.json(self, restricted=restricted, include=include, rewrite=rewrite),
            'title': self.title,
            'icon': self.icon,
            'icon_small': self.icon_small,
            'icon_large': self.icon_large,
            'provider_name': self.provider_name,
            'provider_url': self.provider_url,
            'provider_description': self.provider_description,
            'feedback_url': self.feedback_url,
            'staff': [u.json(restricted=restricted, rewrite=rewrite) for u in self.staff] if include
                     else self._staff,
            'push_vapid_public_key': self.push_vapid_public_key,
            **({} if restricted else {'push_vapid_private_key': self.push_vapid_private_key})
        }

class Activity(Object, JSONRedisSequence[JSONifiable]):
    """See :ref:`Activity`.

    .. attribute:: post

       Hook of the form ``post(event)`` that is called after :meth:`publish` with the corresponding
       *event*. May be ``None``.
    """

    class Stream(AsyncIterator['Event']):
        """:cls:`collections.abc.AsyncGenerator` of events."""
        # Syntax for Python 3.6+:
        #
        # async def stream(self) -> AsyncGenerator['Event', None]:
        #     """Return a live stream of events."""
        #     queue = Queue() # type: Queue[Event]
        #     self._streams.add(queue)
        #     try:
        #         while True:
        #             yield await cast(Coroutine[object, None, Event], queue.get())
        #     except GeneratorExit as e:
        #         self._streams.remove(queue)
        #         # Work around https://bugs.python.org/issue34730
        #         queue.put_nowait(None) # type: ignore

        def __init__(self, streams: Set['Queue[Optional[Event]]']) -> None:
            self._queue = Queue() # type: Queue[Optional[Event]]
            self._streams = streams
            self._streams.add(self._queue)

        async def asend(self, value: None) -> 'Event':
            # pylint: disable=unused-argument, missing-docstring; part of API
            event = await cast(Coroutine[object, None, Optional[Event]], self._queue.get())
            if event is None:
                raise StopAsyncIteration()
            return event

        async def athrow(self, typ: Type[BaseException], val: BaseException = None,
                         tb: object = None) -> 'Event':
            # pylint: disable=unused-argument, missing-docstring; part of API
            raise val if val else typ()

        async def aclose(self) -> None:
            # pylint: disable=missing-docstring; part of API
            self._streams.remove(self._queue)
            self._queue.put_nowait(None)

        async def __anext__(self) -> 'Event':
            return await self.asend(None)

    def __init__(self, *, app: Application, pre: Callable[[], None] = None, **data: object) -> None:
        id = cast(str, data['id'])
        super().__init__(id=id, app=app)
        JSONRedisSequence.__init__(self, app.r, '{}.items'.format(id), pre)
        self.post = None # type: Optional[Callable[[Event], None]]
        self.host = None # type: Optional[object]
        self._subscriber_ids = cast('list[str]', data['subscriber_ids'])
        self._streams = set() # type: Set[Queue[Optional[Event]]]

    @property
    def subscribers(self) -> List[User]:
        """List of :class:`User`s who subscribed to the activity."""
        return self.app.r.omget(self._subscriber_ids, default=AssertionError,
                                expect=expect_type(User))

    def publish(self, event: 'Event') -> None:
        """Publish an *event* to the feed.

        All :attr:`subscribers`, except the user who triggered the event, are notified.
        """
        # If the event is published to multiple activity feeds, it is stored (and overwritten)
        # multiple times, but that's acceptable for a more convenient API
        self.r.oset(event.id, event)
        self.r.lpush(self.list_key, event.id)
        for subscriber in self.subscribers:
            if subscriber is not event.user:
                subscriber.notify(event)
        for stream in self._streams:
            stream.put_nowait(event)
        if self.post:
            self.post(event)

    def subscribe(self):
        """See :http:patch:`/api/(activity-url)` (``subscribe``)."""
        if not self.app.user:
            raise error.PermissionError()
        if not self.app.user.id in self._subscriber_ids:
            self._subscriber_ids.append(self.app.user.id)
        self.app.r.oset(self.host.id if self.host else self.id, self.host or self)

    def unsubscribe(self):
        """See :http:patch:`/api/(activity-url)` (``unsubscribe``)."""
        if not self.app.user:
            raise error.PermissionError()
        try:
            self._subscriber_ids.remove(self.app.user.id)
        except ValueError:
            pass
        self.app.r.oset(self.host.id if self.host else self.id, self.host or self)

    def stream(self) -> 'Activity.Stream':
        """Return a live stream of events."""
        return Activity.Stream(self._streams)

    def json(self, restricted: bool = False, include: bool = False, *, rewrite: RewriteFunc = None,
             slice: 'slice' = None) -> Dict[str, object]:
        # pylint: disable=arguments-differ; extension
        return {
            **super().json(restricted=restricted, include=include, rewrite=rewrite),
            **({'subscriber_ids': self._subscriber_ids} if not restricted else {}),
            **({'user_subscribed': self.app.user and self.app.user.id in self._subscriber_ids}
               if restricted else {}),
            **(
                {
                    'items': [event.json(restricted=True, include=True, rewrite=rewrite)
                              for event in self[slice]]
                } if slice else {})
        }

class Event(Object):
    """See :ref:`Event`."""

    @staticmethod
    def create(type: str, object: Optional[Object], detail: Dict[str, object] = {},
               app: Application = None) -> 'Event':
        """Create an event."""
        assert app
        if not app.user:
            raise error.PermissionError()
        if not str_or_none(type):
            raise ValueError('type_empty')
        if any(k.endswith('_id') for k in detail):
            raise ValueError('detail_invalid_key')

        transformed = {}
        for key, value in detail.items():
            if isinstance(value, Object):
                key = key + '_id'
                value = value.id
            transformed[key] = value
        return Event(
            id='Event:' + randstr(), type=type, object=object.id if object else None,
            user=app.user.id, time=datetime.utcnow().isoformat() + 'Z', detail=transformed, app=app)

    def __init__(self, id: str, type: str, object: Optional[str], user: str, time: str,
                 detail: Dict[str, object], app: Application) -> None:
        super().__init__(id, app)
        self.type = type
        self.time = datetime.fromisoformat(time.replace('Z', '+00:00'))
        self._object_id = object
        self._user_id = user
        self._detail = detail

    @property
    def object(self) -> Optional[Union[Object, Gone]]:
        # pylint: disable=missing-docstring; already documented
        return (
            self.app.r.oget(self._object_id, expect=expect_type(Object)) or Gone()
            if self._object_id else None)

    @property
    def user(self) -> User:
        # pylint: disable=missing-docstring; already documented
        return self.app.users[self._user_id]

    @property
    def detail(self) -> Dict[str, builtins.object]:
        # pylint: disable=missing-docstring; already documented
        detail = {}
        for key, value in self._detail.items():
            if key.endswith('_id'):
                assert isinstance(value, str)
                key = key[:-3]
                value = self.app.r.oget(value) or Gone()
            detail[key] = value
        return detail

    def json(self, restricted: bool = False, include: bool = False, *,
             rewrite: RewriteFunc = None) -> Dict[str, builtins.object]:
        obj = self._object_id # type: Optional[Union[str, Dict[str, object]]]
        detail = self._detail
        if include:
            obj = (self.object.json(restricted=restricted, include=include, rewrite=rewrite)
                   if self.object else None)
            detail = {
                k: v.json(restricted=restricted, include=include, rewrite=rewrite)
                   if isinstance(v, (Object, Gone)) else v for k, v in self.detail.items()
            }
        return {
            **super().json(restricted=restricted, include=include, rewrite=rewrite),
            'type': self.type,
            'object': obj,
            'user': self.user.json(restricted=restricted, rewrite=rewrite) if include
                    else self._user_id,
            'time': self.time.isoformat(),
            'detail': detail
        }

    def __str__(self) -> str:
        return '<{} {} on {} by {}>'.format(type(self).__name__, self.type, self._object_id,
                                            self._user_id)
    __repr__ = __str__

class AuthRequest(Object):
    """See :ref:`AuthRequest`."""

    def __init__(self, id: str, app: Application, email: str, code: str) -> None:
        super().__init__(id, app)
        self._email = email
        self._code = code

    def verify(self, code: str) -> None:
        """Verify the secret *code*."""
        self.app.rate_limiter.count(RateLimit(f'{self.id}.verify', 10, timedelta(minutes=10)),
                                    context.client.get())
        if code != self._code:
            raise ValueError('Invalid code')

    def json(self, restricted: bool = False, include: bool = False, *,
             rewrite: RewriteFunc = None) -> Dict[str, object]:
        return {
            **super().json(restricted=restricted, include=include, rewrite=rewrite),
            **({} if restricted else {'email': self._email, 'code': self._code})
        }

class Location:
    """See :ref:`Location`."""

    def __init__(self, name: str, coords: Tuple[float, float] = None) -> None:
        if str_or_none(name) is None:
            raise ValueError('empty_name')
        if coords and not (-90 <= coords[0] <= 90 and -180 <= coords[1] <= 180):
            raise ValueError('out_of_range_coords')
        self.name = name
        self.coords = coords

    @staticmethod
    def parse(data: Dict[str, object]) -> 'Location':
        """Parse the given location JSON *data* into a :class:`Location`."""
        coords_list = Expect.opt(Expect.list(Expect.float))(data.get('coords'))
        if coords_list is None:
            coords = None
        else:
            if len(coords_list) != 2:
                raise TypeError()
            coords = (float(coords_list[0]), float(coords_list[1]))
        return Location(Expect.str(data.get('name')), coords)

    def json(self) -> Dict[str, object]:
        """Return a JSON representation of the location."""
        return {'name': self.name, 'coords': list(self.coords) if self.coords else None}

class InputError(ValueError):
    """See :ref:`InputError`.

    To raise an :exc:`InputError`, apply the following pattern::

       def meow(volume):
           e = InputError()
           if not 0 < volume <= 1:
               e.errors['volume'] = 'out_of_range'
           e.trigger()
           # ...
    """

    def __init__(self, errors: Dict[str, str] = {}) -> None:
        super().__init__('input_invalid')
        self.errors = dict(errors)

    def trigger(self):
        """Trigger the error, i.e. raise it if any *errors* are present.

        If *errors* is empty, do nothing.
        """
        if self.errors:
            raise self

class EmailError(Exception):
    """Raised if communication with the SMTP server fails."""


from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseBadRequest
from django.views.decorators.csrf import csrf_exempt
from .models import *
from common.models import *
import random
from django.contrib.auth import login, authenticate, logout
from django.core.exceptions import PermissionDenied
from common.decorators import logged_in_or_basicauth, require_token_permissions
import json
from django.utils import timezone
from dateutil.relativedelta import relativedelta

def update_rating(user, subject_id, value):
    Rating.objects.filter(user=user, subject_id=subject_id).delete()

    r = Rating(user=user, subject_id=subject_id, value=value)
    r.save()

@logged_in_or_basicauth
@require_token_permissions("can_view_recommendations")
def get(request):
    rec_type = request.GET.get('t', '')
    if len(rec_type) == 0:
        recs = Recommendation.objects.filter(user=request.user)
    else:
        recs = Recommendation.objects.filter(user=request.user, rec_type=rec_type)
    if recs.count() == 0:
        return HttpResponse('No recommendations yet. Try again tomorrow!')
    resp = {rec.rec_type: json.loads(rec.subjects) for rec in recs}
    return HttpResponse(json.dumps(resp), content_type="application/json")

@csrf_exempt
@logged_in_or_basicauth
@require_token_permissions("can_view_recommendations", "can_edit_student_info")
def rate(request):
    batch = request.body
    if len(batch) > 0:
        batch_items = json.loads(batch)
        for item in batch_items:
            try:
                value = int(item['v'])
                if item['s'] == None or len(item['s']) > 10:
                    return HttpResponseBadRequest('
Missing subject ID
')
                if value is None:
                    return HttpResponseBadRequest('
Missing rating value
')
                update_rating(request.user, item['s'], value)
            except:
                return HttpResponseBadRequest('
Bad input
')
        resp = { 'received': True }
    else:
        subject_id = request.POST.get('s', '')
        value = request.POST.get('v', '')
        try:
            value = int(value)
            if subject_id == None or len(subject_id) == 0 or len(subject_id) > 10:
                return HttpResponseBadRequest('
Missing subject ID
')
            if value is None:
                return HttpResponseBadRequest('
Missing rating value
')
            if request.user.username != str(user_id):
                raise PermissionDenied
            update_rating(request.user, subject_id, value)
            resp = { 'u': request.user.username, 's': subject_id, 'v': value, 'received': True }
        except:
            return HttpResponseBadRequest('
Bad input
')
    return HttpResponse(json.dumps(resp), content_type="application/json")


# break 的简易范例
n=0
while n<6:
    if n==3:
        break
    print(n) #印出循环的n
    n+=1
print("最后的n:",n) #印出循环中最后的n
# continue 的简易范例
a=0
for x in [3,4,5,6]:
    if x%2==0: # x是偶数
        continue
    print(x)
    a+=1
print("最后的a:",a)
# else的简易范例
sum=0
for s in range(11):
    sum+=s
else:
    print(sum) # 印出0+1+2+3...+10的结果
# 综合范例:找出整数平方根
c=int(input("请输入一个正整数:"))
for f in range(c): #f从0~c-1
    if f*f==c:
        print("整数平方根:",f)
        break #用break强制结束循环时，不会执行else区块
else:
    print('没有整数平方根')


import sys
import numpy as np
import h5py as h5
import matplotlib.pyplot as plt

def load_puchwein_spectrum( file_name ):
  file = open( file_name, 'r' )
  lines = file.readlines()
  data = []
  counter = 0
  for line in lines:
    line = line.split()
    if line[0] == '#': continue
    if counter == 0:
      #Load the redshift data
      redshift_data = np.array( line, dtype=np.float )
    if counter > 0:
      #Load the spectrum
      data.append( np.array( line, dtype=np.float ) )
    counter += 1

  data = np.array( data )
  lambda_data = data[:,0]
  J_data = data[:,1:]
  # print len(redshift_data)
  # print len(lambda_data)
  return redshift_data, lambda_data, J_data

def load_puchwein_rates( file_name ):
  # Load the HM2012 data extracted from the paper
  table= np.loadtxt( file_name ).T
  data = {
  'redshift': table[0],
  'Info': np.array('Puchwein et al. (2018)   Generated by Cloudy C17',  dtype='|S50'),
  'photoionization':{
    'HI': table[1],
    'HeI': table[3],
    'HeII': table[5],
    },
  'photoheating':{
    'HI': table[2],
    'HeI': table[4],
    'HeII': table[6],
    },
  }
  return data


import os
import logging

from virttest.libvirt_xml import vm_xml
from autotest.client.shared import error
from virttest import utils_libvirtd, virsh
from virttest.utils_test.libvirt import cpus_parser

try:
    from virttest.staging import utils_cgroup
except ImportError:
    # TODO: Obsoleted path used prior autotest-0.15.2/virttest-2013.06.24
    from autotest.client.shared import utils_cgroup

def get_emulatorpin_from_cgroup(params):
    """
    Get a list of domain-specific per block stats from cgroup blkio controller.
    :params: the parameter dictionary
    """

    vm = params.get("vm")

    cpuset_path = \
        utils_cgroup.resolve_task_cgroup_path(vm.get_pid(), "cpuset")

    cpuset_file = os.path.join(cpuset_path, "cpuset.cpus")

    try:
        f_emulatorpin_params = open(cpuset_file, "rU")
        emulatorpin_params_from_cgroup = f_emulatorpin_params.readline()
        f_emulatorpin_params.close()
        return emulatorpin_params_from_cgroup
    except IOError:
        raise error.TestError("Failed to get emulatorpin "
                              "params from %s" % cpuset_file)

def check_emulatorpin(params):
    """
    Check emulator affinity
    :params: the parameter dictionary
    """
    dicts = {}
    vm = params.get("vm")
    vm_name = params.get("main_vm")
    cpu_list = params.get("cpu_list")
    cgconfig = params.get("cgconfig", "on")
    options = params.get("emulatorpin_options")

    result = virsh.emulatorpin(vm_name)
    cmd_output = result.stdout.strip().splitlines()
    logging.debug(cmd_output)
    # Parsing command output and putting them into python dictionary.
    for l in cmd_output[2:]:
        k, v = l.split(':')
        dicts[k.strip()] = v.strip()

    logging.debug(dicts)

    emulator_from_cmd = dicts['*']
    emulatorpin_from_xml = ""

    # To change a running guest with 'config' option, which will affect
    # next boot, if don't shutdown the guest, we need to run virsh dumpxml
    # with 'inactive' option to get guest XML changes.
    if options == "config" and vm and not vm.is_alive():
        emulatorpin_from_xml = \
            vm_xml.VMXML().new_from_dumpxml(vm_name, "--inactive").cputune.emulatorpin
    else:
        emulatorpin_from_xml = \
            vm_xml.VMXML().new_from_dumpxml(vm_name).cputune.emulatorpin

    # To get guest corresponding emulator/cpuset.cpus value
    # from cpuset controller of the cgroup.
    if cgconfig == "on" and vm and vm.is_alive():
        emulatorpin_from_cgroup = get_emulatorpin_from_cgroup(params)
        logging.debug("The emulatorpin value from "
                      "cgroup: %s", emulatorpin_from_cgroup)

    # To check specified cpulist value with virsh command output
    # and/or cpuset.cpus from cpuset controller of the cgroup.
    if cpu_list:
        if vm and vm.is_alive() and options != "config":
            if (cpu_list != cpus_parser(emulator_from_cmd)) or \
                    (cpu_list != cpus_parser(emulatorpin_from_cgroup)):
                logging.error("To expect emulatorpin %s: %s",
                              cpu_list, emulator_from_cmd)
                return False
        else:
            if cpu_list != cpus_parser(emulatorpin_from_xml):
                logging.error("To expect emulatorpin %s: %s",
                              cpu_list, emulatorpin_from_xml)
                return False

        return True

def get_emulatorpin_parameter(params):
    """
    Get the emulatorpin parameters
    :params: the parameter dictionary
    """
    vm_name = params.get("main_vm")
    vm = params.get("vm")
    options = params.get("emulatorpin_options")
    start_vm = params.get("start_vm", "yes")

    if start_vm == "no" and vm and vm.is_alive():
        vm.destroy()

    result = virsh.emulatorpin(vm_name, options=options)
    status = result.exit_status

    # Check status_error
    status_error = params.get("status_error", "no")

    if status_error == "yes":
        if status or not check_emulatorpin(params):
            logging.info("It's an expected : %s", result.stderr)
        else:
            raise error.TestFail("%d not a expected command "
                                 "return value", status)
    elif status_error == "no":
        if status:
            raise error.TestFail(result.stderr)
        else:
            logging.info(result.stdout)

def set_emulatorpin_parameter(params):
    """
    Set the emulatorpin parameters
    :params: the parameter dictionary
    """
    vm_name = params.get("main_vm")
    vm = params.get("vm")
    cpulist = params.get("emulatorpin_cpulist")
    options = params.get("emulatorpin_options")
    start_vm = params.get("start_vm", "yes")

    if start_vm == "no" and vm and vm.is_alive():
        vm.destroy()

    result = virsh.emulatorpin(vm_name, cpulist, options)
    status = result.exit_status

    # Check status_error
    status_error = params.get("status_error")

    # Changing affinity for emulator thread dynamically is
    # not allowed when CPU placement is 'auto'
    placement = vm_xml.VMXML().new_from_dumpxml(vm_name).placement
    if placement == "auto":
        status_error = "yes"

    if status_error == "yes":
        if status or not check_emulatorpin(params):
            logging.info("It's an expected : %s", result.stderr)
        else:
            raise error.TestFail("%d not a expected command "
                                 "return value", status)
    elif status_error == "no":
        if status:
            raise error.TestFail(result.stderr)
        else:
            if check_emulatorpin(params):
                logging.info(result.stdout)
            else:
                raise error.TestFail("The 'cpulist' is inconsistent with"
                                     " 'cpulist' emulatorpin XML or/and is"
                                     " different from emulator/cpuset.cpus"
                                     " value from cgroup cpuset controller")

def run(test, params, env):
    """
    Test emulatorpin tuning

    1) Positive testing
       1.1) get the current emulatorpin parameters for a running/shutoff guest
       1.2) set the current emulatorpin parameters for a running/shutoff guest
    2) Negative testing
       2.1) get emulatorpin parameters for a running/shutoff guest
       2.2) set emulatorpin parameters running/shutoff guest
    """

    # Run test case
    vm_name = params.get("main_vm")
    vm = env.get_vm(vm_name)
    cgconfig = params.get("cgconfig", "on")
    cpulist = params.get("emulatorpin_cpulist")
    status_error = params.get("status_error", "no")
    change_parameters = params.get("change_parameters", "no")

    # Backup original vm
    vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name)
    vmxml_backup = vmxml.copy()

    emulatorpin_placement = params.get("emulatorpin_placement", "")
    if emulatorpin_placement:
        vm.destroy()
        vmxml.placement = emulatorpin_placement
        vmxml.sync()
        vm.start()

    test_dicts = dict(params)
    test_dicts['vm'] = vm

    host_cpus = int(open('/proc/cpuinfo').read().count('processor'))
    test_dicts['host_cpus'] = host_cpus

    cpu_list = None

    if cpulist:
        cpu_list = cpus_parser(cpulist)
        test_dicts['cpu_list'] = cpu_list
        logging.debug("CPU list is %s", cpu_list)

    # If the physical CPU N doesn't exist, it's an expected error
    if cpu_list and max(cpu_list) > host_cpus - 1:
        test_dicts["status_error"] = "yes"

    cg = utils_cgroup.CgconfigService()

    if cgconfig == "off":
        if cg.cgconfig_is_running():
            cg.cgconfig_stop()

    # positive and negative testing #########
    try:
        if status_error == "no":
            if change_parameters == "no":
                get_emulatorpin_parameter(test_dicts)
            else:
                set_emulatorpin_parameter(test_dicts)

        if status_error == "yes":
            if change_parameters == "no":
                get_emulatorpin_parameter(test_dicts)
            else:
                set_emulatorpin_parameter(test_dicts)
    finally:
        # Recover cgconfig and libvirtd service
        if not cg.cgconfig_is_running():
            cg.cgconfig_start()
            utils_libvirtd.libvirtd_restart()
        # Recover vm.
        vmxml_backup.sync()


import collections
import datetime
import itertools
from typing import Final

from django.shortcuts import render
from rest_framework.permissions import AllowAny
from rest_framework.request import Request
from rest_framework.views import APIView

from api.models import Diary, Post

class Statistic(APIView):
    permission_classes = [AllowAny]

    def get(self, request: Request):
        user = request.user
        duration_type = request.query_params['duration']
        now_date = datetime.datetime.now()
        if duration_type == 'week':
            ago_date = now_date - datetime.timedelta(weeks=1)
        elif duration_type == 'month':
            ago_date = now_date - datetime.timedelta(days=30)
        elif duration_type == 'year':
            ago_date = now_date - datetime.timedelta(days=365)
        else:
            ago_date = datetime.datetime.min
        diaries = Diary.objects.filter(user=user, end__lte=now_date, end__gte=ago_date)
        tags = {}
        for diary in diaries:
            for tag in diary.tag.all():
                duration = diary.end - diary.begin
                if tag.content not in tags:
                    tags[tag.content] = round(duration.seconds / 3600, 2)
                else:
                    tags[tag.content] += round(duration.seconds / 3600, 2)
        label = []
        data = []
        for item in sorted(tags.items(), key=lambda kv: kv[1], reverse=True):
            label.append(item[0])
            data.append(item[1])
        return render(request, 'statistic.html', {'data': data, 'label': label})

adcodes: Final = {
    "全国": 100000,
    "北京市": 110000,
    "天津市": 120000,
    "河北省": 130000,
    "山西省": 140000,
    "内蒙古自治区": 150000,
    "辽宁省": 210000,
    "吉林省": 220000,
    "黑龙江省": 230000,
    "上海市": 310000,
    "江苏省": 320000,
    "浙江省": 330000,
    "安徽省": 340000,
    "福建省": 350000,
    "江西省": 360000,
    "山东省": 370000,
    "河南省": 410000,
    "湖北省": 420000,
    "湖南省": 430000,
    "广东省": 440000,
    "广西壮族自治区": 450000,
    "海南省": 460000,
    "重庆市": 500000,
    "四川省": 510000,
    "贵州省": 520000,
    "云南省": 530000,
    "西藏自治区": 540000,
    "陕西省": 610000,
    "甘肃省": 620000,
    "青海省": 630000,
    "宁夏回族自治区": 640000,
    "新疆维吾尔自治区": 650000,
    "台湾省": 710000,
    "香港特别行政区": 810000,
    "澳门特别行政区": 820000
}

class Smap(APIView):
    permission_classes = [AllowAny]

    def get(self, request: Request):
        user = request.user
        locations = Post.objects.filter(user=user).exclude(location__in=['', '局域网']).values_list('location')
        locations_list = itertools.chain.from_iterable(locations)
        province_list = [location.split()[0] for location in locations_list]
        province_count = collections.Counter(province_list)
        sum = 0
        for value in province_count.values():
            sum += value
        data_ancodes = []
        data_colors = dict()
        for key, value in province_count.items():
            data_ancodes.append(adcodes[key])
            data_colors[adcodes[key]] = 'rgb({},{},255)'.format(255 * (sum - value) / sum, 255 * (sum - value) / sum)
        return render(request, 'map.html', {'data_ancodes': data_ancodes, 'data_colors': data_colors})


# 미로탐색 (DFS)
# 7*7 격자판 미로를 탈출하는 경로의 가지수 출력
# (1,1) -> (7,7)
# 1은 벽이고, 0은 통로이다. 상하좌우로만 움직인다.

import sys
# sys.stdin = open("input.txt", 'r')
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]

def DFS(x, y):
    global cnt
    if x==6 and y==6:
        cnt += 1
        print(cnt)
        return
    else:
        for i in range(4):
            x_ = x+dx[i]
            y_ = y+dy[i]
            if 0<=x_<7 and 0<=y_<7 and maze[x_][y_]==0:
                maze[x_][y_]=1
                DFS(x_,y_)
                maze[x_][y_]=0

if __name__=="__main__":
    maze = [list(map(int, input().split())) for _ in range(7)]
    cnt = 0
    maze[0][0]=1
    DFS(0,0)
    print(cnt)


import random
import math

#SECRET WORD
secret_word = "PLAYSTATION"

#LIST TO STORE THE USERS GUESSES
user_guesses_list = []

#USER CHANCES VARIABLE
user_guesses_list_of_indexes = ""

#WORD BOARD
wordBoard = ["_"] * len(secret_word)

#RETURNS A RANDOM ELEMENT FROM THE DEFINED WORDS BELOW.
def random_word_generator():
    return

def show_board():
    #Prints the current board state.
    print(" ".join(wordBoard))

def return_next_index_duplicate(word, letter, board):
    for x in range(len(word)):
        if(board[x] == "_" and word[x] == letter.upper()):
            board[x] = letter.upper()

def check_guess(guessed_letter):
    global user_guesses_list_of_indexes
    #user_guesses_list.append(guessed_letter)

    position_of_found_letter = secret_word.find(guessed_letter.upper())

    if(position_of_found_letter == -1):
        return False

    if(str(position_of_found_letter) in user_guesses_list_of_indexes or secret_word.count(guessed_letter) != 1):
        return_next_index_duplicate(secret_word, guessed_letter, wordBoard)
    else:
        #LOGIC FOR THE CORRECT INDEXING OF WORD BOARD AND REPEATING OF LETTERS IN WORD.
        user_guesses_list_of_indexes += str(position_of_found_letter)
        wordBoard[position_of_found_letter] = guessed_letter

    return guessed_letter in secret_word

#Validate the input is a string helper function.
def validate_the_string(letter):
    return type(letter) == str

#Helpoer function to print message to the user.
def message_function(attempt_passed, chances, letter,word = ""):
    if(attempt_passed == 'No'):
        print(f"I'm sorry but there is no letter {letter.upper()} in the word")
        print(f"You have {chances}s left!")
        if(chances <= 0):
            print(f"You Lost. The secret word was {word}")
        else:
            return

def check_for_board_win(word, board):
    #Using list comprehension to fill the returned list.
    return [letter for letter in word if letter] == board

def init():
    player_win = False
    user_chance_variable = 5
    negative_string = "No"
    while(user_chance_variable > 0 and player_win != True ):
        user_guess = input("Can you guess the secret word? ").upper()
        letter_in_secert_word = check_guess(user_guess.upper())
        dict = {"letter_count": secret_word.count(user_guess.upper())}

        if(letter_in_secert_word == False):
            if(user_guess in user_guesses_list):
                print(f"You already guessed that letter{user_guess.upper()}")
                continue
            user_chance_variable -= 1
            message_function(negative_string,user_chance_variable, user_guess, secret_word)
            # user_guesses_list.append(user_guess.upper())

        else:
            if(user_guess in user_guesses_list):
                print(f"You already guessed that letter{user_guess.upper()}")
                continue
            if(dict["letter_count"] > 1):
                print(f"""Yes there are {dict["letter_count"]} {user_guess.upper()}'s""")
            else:
                print(f"Yes there is a {user_guess}!")
            show_board()

            winner_found = check_for_board_win(secret_word, wordBoard)
            if(winner_found == True):
                player_win = True
                print("You Won the secret word was!")
                print("".join(secret_word))
        user_guesses_list.append(user_guess.upper())

init()


from api.model.order_model import Order
from flask import json
from datetime import datetime
from api.model import db
from api.service import cart_service

def get_all():
    return Order.query.all()

def get_by_id(id):
    return Order.query.get(id)

def create_from(cart_code):
    with db.session.begin():
        cart = cart_service.get_by_code(cart_code)
        now = datetime.utcnow()
        order = Order(products=cart.content,
                      total_amount=sum_total(cart.content),
                      created_at=now, updated_at=now, status='DONE')

        db.session.add(order)
        db.session.delete(cart)

        # Commit a transaction do banco de dados.
        db.session.commit()

        return order

def sum_total(products_json):
    products = json.loads(products_json)
    print(products)
    total_amount = 0
    for product in products:
        print(product)
        total_amount += product['qty'] * product['unitPrice']

    return total_amount


#!/usr/bin/python3
"""

Function Name: add_integer().

Test command: python3 -m doctest -v ./tests/0-add_integer.txt

"""

def add_integer(a, b=98):
    """

    Calculates the sum of it's arguments.

    Return:
    Sum of int a and int b.
    """
    sum = 0

    if not isinstance(a, (int, float)):
        raise TypeError("a must be an integer")
    if not isinstance(b, (int, float)):
        raise TypeError("b must be an integer")

    c = int(a)
    d = int(b)

    sum = c + d
    return (sum)