#%%
import torch
import numpy as np
from tqdm import tqdm
from transformers import AutoModelForSequenceClassification

# model = AutoModelForSequenceClassification.from_pretrained("shahrukhx01/question-vs-statement-classifier")
# model.save_pretrained("./models_store/qc_model/")

def get_valid_questions(loader, device):
    model = AutoModelForSequenceClassification.from_pretrained("./models_store/qc_model/")
    model.to(device)
    valid_questions_idx = []
    batch_size = loader.batch_size
    adder = 0
    ## evaluate data for one epoch
    for batch in tqdm(loader.dataloader):
        ## add batch to GPU
        batch = tuple(t.to(device) for t in batch)
        ## unpack the inputs from our dataloader
        b_input_ids, b_input_mask = batch
        ## avoiding model's computation and storage of gradients -> saving memory and speeding up validation
        with torch.no_grad():
            # forward pass, calculate logit predictions
            logits = model(b_input_ids,
                           token_type_ids=None,
                           attention_mask=b_input_mask)

        ## move logits and labels to CPU
        logits = logits['logits'].detach().cpu().numpy()
        preds = np.argmax(logits, axis=1).flatten()
        # print(preds)
        valid_questions_idx.extend((np.where(preds == 1)[0] + adder).tolist())
        adder += batch_size
    valid_questions = loader.dataset.sentences.iloc[valid_questions_idx]
    del model
    return valid_questions.reset_index(drop=True)

# #%%
# from data.dataset import EmailsDataset
# from data.dataloader import QCDataLoader
# dataset = EmailsDataset("/home/aryanab/IEEE-year-long-project/email-2-faq/email2faq/data/asset/sample_email_dataset_raw.csv")
# loader = QCDataLoader(dataset)
# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# valid_questions = get_valid_questions(loader, device)

# %%


train_datagen = ImageDataGenerator(
        rescale=1./255,
        shear_range=0.2,
        zoom_range=0.2,
        horizontal_flip=True)

test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        'data/train',
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
        'data/validation',
        target_size=(150, 150),
        batch_size=32,
        class_mode='binary')

model.fit_generator(
        train_generator,
        steps_per_epoch=2000,
        epochs=50,
        validation_data=validation_generator,
        validation_steps=800)
# 主要需要采用图片增强的方法，并利用fit_generator产生训练样本的同时进行训练.flow_from_directory可以对整个文件夹中的文件进行数据增强


# -*- coding: utf-8 -*-
from __future__ import absolute_import
import textwrap
from . import _unittest as unittest

import datatest
from datatest.__past__ import api08  # <- MONKEY PATCH!!!

from datatest.differences import NOVALUE
from datatest.validation import _require_sequence

class TestColumns(unittest.TestCase):
    def test_columns(self):
        source = datatest.DataSource([[1, 2]], ('A', 'B'))
        self.assertEqual(source.columns(), ['A', 'B'])

class TestDataSourceConstructors(unittest.TestCase):
    @staticmethod
    def get_table_contents(source):
        connection = source._connection
        table = source._table
        cursor = connection.cursor()
        cursor.execute('SELECT * FROM ' + table)
        return list(cursor)

    def test_from_sequence_rows(self):
        data = [('x', 1),
                ('y', 2),
                ('z', 3)]

        source = datatest.DataSource(data, fieldnames=('A', 'B'))
        table_contents = self.get_table_contents(source)
        self.assertEqual(set(table_contents), set(data))

    def test_duplicate_fieldnames(self):
        regex = 'duplicate column name: A'
        with self.assertRaisesRegex(Exception, regex):
            records = [
                ('A', 'A'),
                ('1', '2'),
                ('1', '2'),
            ]
            source = datatest.DataSource(records)

        regex = 'contains multiple columns where names are empty strings or whitespace'
        with self.assertRaisesRegex(Exception, regex):
            records = [
                ('', ''),
                ('1', '2'),
                ('1', '2'),
            ]
            source = datatest.DataSource(records, fieldnames=['', ''])

    def test_from_dict_rows(self):
        data = [{'A': 'x', 'B': 1},
                {'A': 'y', 'B': 2},
                {'A': 'z', 'B': 3}]

        source = datatest.DataSource(data, fieldnames=['B', 'A'])  # <- Set field order.
        table_contents = self.get_table_contents(source)
        expected = [(1, 'x'), (2, 'y'), (3, 'z')]
        self.assertEqual(set(table_contents), set(expected))

    @staticmethod
    def _get_filelike(string, encoding):
        """Return file-like stream object."""
        import io
        import sys
        filelike = io.BytesIO(string)
        if encoding and sys.version >= '3':
            filelike = io.TextIOWrapper(filelike, encoding=encoding)
        return filelike

    def test_from_csv_file(self):
        csv_file = self._get_filelike(b'A,B\n'
                                      b'x,1\n'
                                      b'y,2\n'
                                      b'z,3\n', encoding='utf-8')
        source = datatest.DataSource.from_csv(csv_file)
        table_contents = self.get_table_contents(source)
        expected = [('x', '1'), ('y', '2'), ('z', '3')]
        self.assertEqual(set(table_contents), set(expected))

    def test_from_multiple_csv_files(self):
        file1 = self._get_filelike(b'A,B\n'
                                   b'x,1\n'
                                   b'y,2\n'
                                   b'z,3\n', encoding='utf-8')

        file2 = self._get_filelike(b'B,C\n'
                                   b'4,j\n'
                                   b'5,k\n'
                                   b'6,l\n', encoding='ascii')

        source = datatest.DataSource.from_csv([file1, file2])
        table_contents = self.get_table_contents(source)

        expected = [('x', '1', ''), ('y', '2', ''), ('z', '3', ''),
                    ('', '4', 'j'), ('', '5', 'k'), ('', '6', 'l')]
        self.assertEqual(set(table_contents), set(expected))

class TestDataQuery(unittest.TestCase):
    def test_callable(self):
        """In 0.9, __call__() was changed to execute()."""
        query = datatest.DataQuery('foo')
        self.assertTrue(hasattr(query, '__call__'), 'should be callable')

class TestRequireSequence(unittest.TestCase):
    def test_return_object(self):
        first = ['aaa', 'bbb', 'ccc']
        second = ['aaa', 'bbb', 'ccc']
        error = _require_sequence(first, second)
        self.assertIsNone(error)  # No difference, returns None.

        first = ['aaa', 'XXX', 'ccc']
        second = ['aaa', 'bbb', 'ccc']
        error = _require_sequence(first, second)
        self.assertIsInstance(error, AssertionError)

    def test_differs(self):
        first = ['aaa', 'XXX', 'ccc']
        second = ['aaa', 'bbb', 'ccc']
        error = _require_sequence(first, second)

        message = """
            Data sequence differs starting at index 1:

              'aaa', 'XXX', 'ccc'
                     ^^^^^
            Found 'XXX', expected 'bbb'
        """
        message = textwrap.dedent(message).strip()
        self.assertEqual(str(error), message)

    def test_missing(self):
        first = ['aaa', 'bbb']
        second = ['aaa', 'bbb', 'ccc']
        error = _require_sequence(first, second)

        message = """
            Data sequence is missing elements starting with index 2:

              ..., 'bbb', ?????
                          ^^^^^
            Expected 'ccc'
        """
        message = textwrap.dedent(message).strip()
        self.assertEqual(str(error), message)

    def test_extra(self):
        first = ['aaa', 'bbb', 'ccc', 'ddd']
        second = ['aaa', 'bbb', 'ccc']
        error = _require_sequence(first, second)

        message = """
            Data sequence contains extra elements starting with index 3:

              ..., 'ccc', 'ddd'
                          ^^^^^
            Found 'ddd'
        """
        message = textwrap.dedent(message).strip()
        self.assertEqual(str(error), message)

    def test_novalue(self):
        with self.assertRaises(ValueError):
            _require_sequence(NOVALUE, [1, 2, 3])

Missing = datatest.Missing
Extra = datatest.Extra
Deviation = datatest.Deviation
ValidationError = datatest.ValidationError
class TestAllowedKey(unittest.TestCase):
    def test_allowed_key(self):
        # Test mapping of differences.
        differences = {'aaa': Missing(1), 'bbb': Missing(2)}
        def function(key):
            return key == 'aaa'

        with self.assertRaises(ValidationError) as cm:
            with datatest.allowed_key(function):  # <- Apply acceptance!
                raise ValidationError(differences, 'some message')

        remaining_diffs = cm.exception.differences
        self.assertEqual(dict(remaining_diffs), {'bbb': Missing(2)})

        # Test mapping of differences with composite keys.
        differences = {('a', 7): Missing(1), ('b', 7): Missing(2)}
        def function(letter, number):
            return letter == 'a' and number == 7

        with self.assertRaises(ValidationError) as cm:
            with datatest.allowed_key(function):  # <- Apply acceptance!
                raise ValidationError(differences, 'some message')

        remaining_diffs = cm.exception.differences
        self.assertEqual(dict(remaining_diffs), {('b', 7): Missing(2)})

        # Test non-mapping container of differences.
        differences = [Missing(1), Extra(2)]
        def function(key):
            if key is not None:
                raise ValueError('key should always be None for non-mapping differences')
            return False  # < Don't match any differences.

        with self.assertRaises(ValidationError) as cm:
            with datatest.allowed_key(function):  # <- Apply acceptance!
                raise ValidationError(differences, 'some message')

        remaining_diffs = cm.exception.differences
        self.assertEqual(list(remaining_diffs), [Missing(1), Extra(2)])

class TestAcceptedArgs(unittest.TestCase):
    def test_accepted_args(self):
        # Single argument.
        differences =  [Missing('aaa'), Missing('bbb'), Extra('bbb')]
        def function(arg):
            return arg == 'bbb'

        with self.assertRaises(ValidationError) as cm:
            with datatest.AcceptedArgs(function):  # <- Apply acceptance!
                raise ValidationError(differences, 'some message')

        remaining_diffs = cm.exception.differences
        self.assertEqual(list(remaining_diffs), [Missing('aaa')])

        # Multiple arguments.
        differences =  [Deviation(+1, 5), Deviation(+2, 5)]
        def function(diff, expected):
            return diff < 2 and expected == 5

        with self.assertRaises(ValidationError) as cm:
            with datatest.AcceptedArgs(function):  # <- Apply acceptance!
                raise ValidationError(differences, 'some message')

        remaining_diffs = cm.exception.differences
        self.assertEqual(list(remaining_diffs), [Deviation(+2, 5)])

if __name__ == '__main__':
    unittest.main()
else:
    raise Exception('This test must be run directly or as a subprocess.')


# Write a function that does the following things: adding, subtracting,
# dividing, or multiplying values. It is simply referred to as variable
# operation variable. Of course, the variables have to be defined, but
# in this challenge, the variable will be defined for you. All you have
#  to do is look at the variable, do some string to integer conversions
#use some if conditionals, and combine them based on the operation.

# The numbers and operation will be given as a string, and you should
#return the value as a string as well.

def operation(a, b, op):

    oper = {"add":"+", "subtract": "-", "divide": "/", "multiply": "*"}
    try:
        return  str(round(eval(a + oper.get(op) + b)))
    except ZeroDivisionError:
        return "undefined"

print(operation("1", "2", "add" ) )
print(operation("4", "5", "subtract"))
print(operation("6", "3", "divide") )
print(operation("9", "0", "divide"))


from http.server import BaseHTTPRequestHandler, HTTPServer
import time
import io
import os
import mimetypes

hostName = "localhost"
serverPort = 8080

class MyServer(BaseHTTPRequestHandler):
    html_file_path = os.path.dirname(os.path.abspath(__file__))+"/static"
    mimetype_ = "text/html"

    def do_GET(self):
        print("self.path", self.path)
        if "?" in self.path:
            sp = self.path.split("?")
            self.path = sp[0]
        if not "." in self.path:
            all = self.getHtml_Data(self.path+".html")
            if "404 not found" in all:
                return self.e404()
            return self.s200(all)
        else:
            all = self.getHtml_Data(self.path)
            if "image" in self.mimetype_:
                return self.s200(all)
            elif "404 not found" in all:
                return self.e404()
            return self.s200(all)

    def e404(self):
        self.send_response(404)
        self.send_header("Content-type", self.mimetype_)
        self.end_headers()
        self.wfile.write(bytes("404 not found", "utf-8"))
        return

    def s200(self, data):
        self.send_response(200)
        self.send_header("Content-type", self.mimetype_)
        self.end_headers()
        if "image" in self.mimetype_:
            self.wfile.write(bytes(data))
        else:
            self.wfile.write(bytes(data, "utf-8"))

    def load_binary(self,filename):
        with open(filename, 'rb') as file_handle:
            return file_handle.read()
    def getHtml_Data(self, fileName, default="404 not found"):
        if not os.path.isfile(self.html_file_path + fileName):
            return default
        self.mimetype_ = mimetypes.guess_type(
            self.html_file_path + fileName)[0]
        if "image" in self.mimetype_:
            return self.load_binary(self.html_file_path + fileName)
        f = io.open(self.html_file_path + fileName,
                    mode="r" )

        return f.read()

if __name__ == "__main__":
    webServer = HTTPServer((hostName, serverPort), MyServer)
    print("Server started http://%s:%s" % (hostName, serverPort))
    try:
        webServer.serve_forever()
    except KeyboardInterrupt:
        pass

    webServer.server_close()
    print("Server stopped.")


from django.shortcuts import render
from .models import book_details,author_details
# Create your views here.
def search(request):
    return render(request,'base.html')

#@login_required
def home(request):
    #if request.method == "POST":
    bookDetail = book_details.objects.all()
    authorDetail = author_details.objects.all()
    data = {
        'books':bookDetail,
        'authors':authorDetail
    }
    return render(request, 'index.html', data)
    #else:
    #   return render(request,'index.html')
def temp(request):
    authorDetail = author_details.objects.all()
    data = {
        'authors':authorDetail
    }
    return render(request, 'home.html', data)

def bookDetails(request,bookId):
    bookDetails = book_details.objects.all().filter(book_id=bookId)
    allBook = book_details.objects.exclude(book_id=bookId)
    print(allBook)
    data = {
        'bookDetail':bookDetails,
        'allbooks':allBook
    }
    return render(request, 'bookdetail.html', data)


#!/usr/bin/env python3

import argparse
import logging
import pathlib
import sys

from builder import Builder

def main():
    logging.basicConfig(format="%(levelname)s - %(message)s", level=logging.INFO)
    parser = argparse.ArgumentParser(description="Build HTML pages from RST sources")
    parser.add_argument(
        "--build-dir",
        required=True,
        type=pathlib.Path,
        help="build directory where HTML files will be stored",
    )
    parser.add_argument("pages", nargs="*", type=pathlib.Path, help="list of RST pages to render")
    args = parser.parse_args()

    if not args.build_dir.is_dir():
        sys.exit(f"Error: '{args.build_dir}' is not a valid build directory")

    builder = Builder(args.pages, args.build_dir)
    builder.run()

if __name__ == "__main__":
    main()


"""
https://open.kattis.com/problems/zamka
Author: https://github.com/smh997/
"""
L = int(input())
D = int(input())
X = int(input())
for i in range(L, D+1):
    if sum([int(a) for a in str(i)]) == X:
        print(i)
        break
for i in range(D, L-1, -1):
    if sum([int(a) for a in str(i)]) == X:
        print(i)
        break


################################################################
# Exploring the dynamics of intracellular decay and
# epigenetic inhertience.
#
# Produces the Figures in 5.2.1
################################################################
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
import seaborn as sns
sns.set(rc={'image.cmap': 'cubehelix'})
import cmocean

import hedges as hds

#######################################################
# The bistable switches.
#######################################################
g_max = 61
DC_hedge = hds.DC_hedge_xy
DCx_hedge = hds.DCx_hedge_xy
DCy_hedge = hds.DCy_hedge_xy
AM_hedge = hds.get_AM_hedge_load(30,30)

#############################################################
# Dynamics of protein decay.
#############################################################
def p(n,t,k,n_0=60):
    p = np.exp(-1*k*n*t) * sp.special.binom(n_0,n) * (1-np.exp(-1*k*t))**(n_0 - n)
    return p

def expectation(t,k,n_0):
    return n_0 * np.exp(-1*k*t)

#Sample a value n, at time t, with rate k, from p.
def sample(t,k,n_0):
    ns = range(0, n_0+1)
    bs = map(lambda x : p(x,t,k,n_0), ns)
    n = np.random.choice(ns,p=bs)
    return n

def getProbAndMols(n_0, t, k, hedge, x_burst, y_burst, phenA = True):
    n = sample(t,k,n_0) #The number of mols remaining.
    if phenA:
        p_A = hedge(n/2. + x_burst, y_burst)
    else:
        p_A = hedge(x_burst, n/2. + y_burst)

    return n,p_A

#############################################################
# The memory hedge. Returns Prob(A | initial conditions), expected.
#############################################################
def probA(t,n_0,k,hedge, x_burst, y_burst):
    prob = 0.0
    for n in range(n_0+1):
        prob+=p(n,t,k,n_0)*hedge(x_burst+n, y_burst)
    return prob

def probDC(t, n_0, k, x_burst, y_burst):
    return probA(t, n_0, k, DC_hedge, x_burst, y_burst)

def probDCx(t, n_0, k, x_burst, y_burst):
    return probA(t, n_0, k, DCx_hedge, x_burst, y_burst)

def probDCy(t, n_0, k, x_burst, y_burst):
    return probA(t, n_0, k, DCy_hedge, x_burst, y_burst)

def probAM(t, n_0, k, x_burst, y_burst):
    return probA(t, n_0, k, AM_hedge, x_burst, y_burst)

#############################################################
# Some useful visualistion
#############################################################
#how the probability changes over time.
def comparing_decay(x_burst = 30, y_burst = 30):
    params = {
    'axes.labelsize': 14,
    'font.size': 14,
    'axes.titlesize': 16,
    'xtick.labelsize': 12,
    'ytick.labelsize': 12,
    'text.usetex': False
    }

    mpl.rcParams.update(params)
    #for the tick label formatting.
    import matplotlib.ticker as mtick
    start = 6.77 * 10**(-10)
    stop = 6.77 * 10**(-3)
    interval = (stop - start) / 20
    max_t = 100. * 60 * 60 * 24.
    t_step = 20. #Increase this values for smoother curves

    number_of_lines = len(np.arange(start, stop, interval))
    #Build the colormap.
    cmap = mpl.cm.Blues
    norm = mpl.colors.LogNorm(vmin=start, vmax = 10.**(-2))
    cm_subsection = np.linspace(0.0, 1.0, number_of_lines)
    colors = [cmap(x) for x in cm_subsection]

    fig = plt.figure()
    hedges = [DC_hedge, DCx_hedge, DCy_hedge, AM_hedge]
    h_names = ["DC Switch", "DCx Switch", "DCy Switch", "AM Switch"]

    ylims_l = [0.0, 0.9, 0.050, 0.0]
    ylims_u = [1.0, 1.0, 0.1, 1.0]
    plt.figure(figsize=(8,8))
    for h in range(len(hedges)):
        ax = plt.subplot(2,2,h+1)
        i = 0
        sig_list = [6.77 * 10**(-k) for k in np.arange(10,3.1,-(10.-3) / 20.)]
        for k in sig_list:
            ts = np.arange(0.0,max_t,max_t/t_step)
            ls = [probA(t, x_burst+y_burst, k, hedges[h], x_burst, y_burst) for t in ts]
            ts = map(lambda t : t/(24. * 60 *60.), ts)
            ax.plot(ts,ls, c= colors[i])
            i+=1

        ax.set_ylim(ylims_l[h], ylims_u[h])
        ax.set_xlim(ts[1], 10**2)
        ax.set_xscale('log')
        ax.set_xlabel('Cell Age (days)')
        ax.set_ylabel('Probability of Phenotype A')
        ax.set_title(h_names[h])

        #Set the number of sig figs on the y axis labels
        ax.yaxis.set_major_formatter(mtick.FormatStrFormatter('%1.3f'))

    # ax = fig.add_axes([0.85, 0.10, 0.015, 0.8])
    # cb1 = mpl.colorbar.ColorbarBase(ax, cmap=cmap, norm=norm, orientation='vertical', format='%.2e')
    # cb1.set_label('Decay rate')
    plt.subplots_adjust(hspace=0.4, wspace=0.3)
    plt.show()

#How the distribution of p changes over time.
def molsprobtimeplot(sigma = 6.77 * 10**(-7)):
    params = {
    'axes.labelsize': 14,
    'text.fontsize': 14,
    'axes.titlesize': 16,
    'xtick.labelsize': 12,
    'ytick.labelsize': 12,
    'text.usetex': False,
    'figure.subplot.wspace' : 0.7,
    'figure.subplot.hspace' : 0.5,
    'figure.subplot.bottom' : 0.25,
    'figure.subplot.right' : 0.80,
    'figure.subplot.top' : 0.85
    }

    mpl.rcParams.update(params)

    start = 0.0
    stop = 56.01
    interval = 3.5

    #Build the colormap.
    cmap = cmocean.cm.ice
    norm = mpl.colors.Normalize(vmin=start, vmax = stop)

    number_of_lines = len(np.arange(start, stop, interval))
    cm_subsection = np.linspace(0.0, 1.0, number_of_lines)
    colors = [cmap(x) for x in cm_subsection]

    fig, ax = plt.subplots()
    i=0
    for t in np.arange(start,stop, interval):
        t2 = t * 60 * 60 * 24
        ls = [p(n,t2,sigma) for n in range(61)]
        plt.plot(ls,c=colors[i])
        i+=1

    plt.xlabel("Number of Molecules (n)")
    plt.ylabel("Probability")
    plt.title("Intracellular Molecular Decay")

    ax = fig.add_axes([0.85, 0.25, 0.015, 0.6])
    cb1 = mpl.colorbar.ColorbarBase(ax, cmap=cmap, norm=norm, orientation='vertical', ticks=np.arange(start, stop, 7.0))
    cb1.set_label('Cell Age (days)')

    plt.show()

#How the expectation changes over time.
def exp_over_time(n0=60):

    params = {
    'axes.labelsize': 14,
    'text.fontsize': 14,
    'axes.titlesize': 16,
    'xtick.labelsize': 12,
    'ytick.labelsize': 12,
    'text.usetex': False,
    'figure.subplot.wspace' : 0.7,
    'figure.subplot.hspace' : 0.5,
    'figure.subplot.bottom' : 0.25,
    'figure.subplot.right' : 0.80,
    'figure.subplot.top' : 0.85
    }
    mpl.rcParams.update(params)

    start = 6.77 * 10**(-10)
    stop = 6.77 * 10**(-3)
    interval = (stop - start) / 20
    max_t = 1000 * 60 * 60 * 24.
    number_of_lines = len(np.arange(start, stop, interval))

    #Build the colormap.
    # cmap = mpl.cm.BuGn
    cmap = cmocean.cm.thermal
    norm = mpl.colors.LogNorm(vmin=start, vmax = stop)
    cm_subsection = np.linspace(0.0, 1.0, number_of_lines)
    colors = [cmap(x) for x in cm_subsection]

    fig, ax = plt.subplots()

    i = 0
    sig_list = [6.77 * 10**(-k) for k in np.arange(10,3, -7./ 20.)]
    for sigma in sig_list:
        es = []
        ts = np.arange(0.0,max_t,60.0)
        for t in ts:
            es.append(expectation(t,sigma,n0))

        ts = map(lambda x : x / (60*60*24), ts)
        plt.plot(ts,es, c = colors[i])
        i+=1

    sigma = 6.77 * 10**(-7)
    es = []
    ts = np.arange(0.0,max_t,60)
    for t in ts:
        es.append(expectation(t,sigma,n0))

    ts = map(lambda x : x / (60*60*24), ts)
    plt.plot(ts,es, c = sns.xkcd_rgb["denim blue"], lw = 3.0, ls='--')

    plt.xlabel("Cell Age (days)")
    plt.ylabel("Expected Number of Internal Molecules")
    plt.title("Dynamics of Intracellular Decay")
    plt.xscale('log')
    plt.xlim((1./(24*60), 1000.))
    # ax = fig.add_axes([0.85, 0.25, 0.015, 0.6])
    # cb1 = mpl.colorbar.ColorbarBase(ax, cmap=cmap, norm=norm, orientation='vertical', format='%.2e')
    # cb1.set_label('Decay Rate')

    plt.show()

def fixed_point(x_b, y_b, k, avg_tr):
    B_0 = x_b + y_b
    return B_0 * (1. / (1-0.5*np.exp(-1 * k * avg_tr)))

#Expectation over multiple reproduction steps.
def exp_reproduction_plot(k, x_b, y_b, T_a, T_b):

    params = {
    'axes.labelsize': 14,
    'text.fontsize': 14,
    'axes.titlesize': 16,
    'xtick.labelsize': 12,
    'ytick.labelsize': 12,
    'text.usetex': False
    }

    mpl.rcParams.update(params)

    num_cycles = 10
    ts_x = [0.0]
    ts_y = [0.0]
    xs = [x_b+y_b]
    ys = [x_b+y_b]

    for cycles in range(num_cycles):
        next_ts = np.arange(0.01,T_a,10.)
        num_x = xs[-1]
        prev_t = ts_x[-1]
        for t in next_ts:
            ts_x.append(prev_t + t)
            xs.append(expectation(t,k, num_x))
        ts_x.append((cycles+1)*T_a)
        xs.append((0.5)*xs[-1]+(x_b+y_b))

    num_cycles = int(T_b * 5 / T_b)
    for cycles in range(num_cycles):
        next_ts = np.arange(0.01,T_b,10.)
        num_y = ys[-1]
        prev_t = ts_y[-1]
        for t in next_ts:
            ts_y.append(prev_t + t)
            ys.append(expectation(t,k,num_y))
        ts_y.append((cycles+1)*T_b)
        ys.append((0.5)*ys[-1]+(x_b+y_b))

    plt.subplot(121)
    plt.plot(ts_x,xs, c=sns.xkcd_rgb["pale red"], label='x molecules')
    fp = fixed_point(x_b, y_b, k, T_a)
    plt.plot((0.0, ts_x[-1]), (fp, fp), 'k--')

    plt.ylim(0,120)
    plt.ylabel("Number of Molecules (One daughter)")

    tickwidth = 3
    tickpos = range(0, int(10*T_a), 60*60*24 * tickwidth)
    ticklabs = map(lambda x : str(int(x/(60*60*24))), tickpos)
    plt.xticks(tickpos, ticklabs)
    plt.xlim(0, 10*T_a)
    plt.xlabel("Time (days)")

    plt.title("Lineage of Phenotype A Individuals")

    plt.subplot(122)
    plt.plot(ts_y,ys, sns.xkcd_rgb["denim blue"],label='y molecules')
    fp = fixed_point(x_b, y_b, k, T_b)
    plt.plot((0.0, ts_y[-1]), (fp, fp), 'k--')
    plt.ylabel("Number of Molecules (One daughter)")
    plt.ylim(0,120)

    tickwidth = 14
    tickpos = range(0, int(5*T_b), 60*60*24 * tickwidth)
    ticklabs = map(lambda x : str(int(x/(60*60*24))), tickpos)
    plt.xticks(tickpos, ticklabs)
    plt.xlim(0, 5*T_b)

    plt.xlabel("Time (days)")
    plt.title("Lineage of Phenotype B Individuals")

    plt.show()

def generations_accumulation_plot(k, x_b, T_r):

    params = {
    'axes.labelsize': 14,
    'text.fontsize': 14,
    'axes.titlesize': 12,
    'xtick.labelsize': 12,
    'ytick.labelsize': 12,
    'text.usetex': False,
    'figure.subplot.wspace' : 0.7,
    'figure.subplot.hspace' : 0.5,
    'figure.subplot.bottom' : 0.25,
    'figure.subplot.right' : 0.80,
    'figure.subplot.top' : 0.85
    }

    mpl.rcParams.update(params)
    dom = range(30) #how many generations of all-x
    def birth_mols(gen):
        B0 = 2*x_b
        a = np.exp(-1 * k * T_r)
        return B0 * ((1.-(0.5*a)**(gen+1))/(1-0.5*a))

    mols = [birth_mols(i) for i in dom]
    plt.plot(dom,mols,marker='o')
    plt.ylabel("Number of Molecules (One daughter, phen A)")
    plt.xlabel("Generations of A")
    plt.show()


import datetime
import funcy
import pytz
import singer

from dateutil.parser import parse

from tap_framework.streams import BaseStream
from tap_framework.config import get_config_start_date
from tap_logmeinrescue.state import get_last_record_value_for_table, \
    incorporate, save_state

from tap_logmeinrescue.logger import LOGGER

class BaseLogMeInRescueStream(BaseStream):

    def convert_key(self, k):
        # replace disallowed characters used in keys
        k = k.replace('–', '')
        k = k.replace('/', '_')

        # replace whitespace and downcase everything
        return k.replace(' ', '_').lower()

    def convert_keys(self, d):
        to_return = {}

        for k, v in d.items():
            to_return[self.convert_key(k)] = v

        return to_return

    def transform_record(self, record):
        converted = self.convert_keys(record)

        return super().transform_record(converted)

class BaseLogMeInRescueReportStream(BaseLogMeInRescueStream):

    REPORT_AREA = None

    def get_url(self):
        return 'https://secure.logmeinrescue.com/API/getReport_v2.aspx'

    def generate_catalog(self, custom_field_schema={}):
        schema = self.get_schema(custom_field_schema=custom_field_schema)
        mdata = singer.metadata.new()

        mdata = singer.metadata.write(
            mdata,
            (),
            'inclusion',
            'available'
        )

        for field_name, field_schema in schema.get('properties').items():
            inclusion = 'available'

            if field_name in self.KEY_PROPERTIES:
                inclusion = 'automatic'

            mdata = singer.metadata.write(
                mdata,
                ('properties', field_name),
                'inclusion',
                inclusion
            )

        return [{
            'tap_stream_id': self.TABLE,
            'stream': self.TABLE,
            'key_properties': self.KEY_PROPERTIES,
            'schema': schema,
            'metadata': singer.metadata.to_list(mdata)
        }]

    def get_schema(self, custom_field_schema={}):
        schema = self.load_schema_by_name(self.TABLE)
        schema['properties'] = funcy.merge(
            custom_field_schema,
            schema['properties'])

        return schema

    def header_to_string_schema(self, header):
        to_return = {}

        for key in header:
            new_key = self.convert_key(key)
            to_return[new_key] = {
                "type": ["string", "null"]
            }

        return to_return

    def get_header(self, response):
        status_removed = '\n'.join(response.splitlines()[2:])
        lines = status_removed.split('|\n')
        header_line = lines[0]
        header = header_line.split('|')

        return header

    def get_stream_data(self, response):
        status_removed = '\n'.join(response.splitlines()[2:])
        lines = status_removed.split('|\n')
        rows = lines[1:]
        header = self.get_header(response)

        to_return = []

        for row in rows:
            to_add = {}
            data = row.split('|')

            if len(data) != len(header):
                continue

            for index, item in enumerate(data):
                to_add[header[index]] = item

            to_return.append(self.transform_record(to_add))

        return to_return

    def sync_data(self, parent_ids, return_first_response=False):
        table = self.TABLE

        if not return_first_response:
            self.write_schema()

        start_date = get_last_record_value_for_table(
            self.state, table, 'start_date')
        technician_id = get_last_record_value_for_table(
            self.state, table, 'technician_id')

        if start_date is None:
            start_date = get_config_start_date(self.config)
        else:
            start_date = parse(start_date)

        if technician_id is None:
            technician_id = 0

        end_date = start_date + datetime.timedelta(days=7)

        self.client.make_request(
            'https://secure.logmeinrescue.com/API/setReportArea_v8.aspx',
            'POST',
            params={'area': self.REPORT_AREA})

        while start_date < datetime.datetime.now(tz=pytz.UTC):
            for index, parent_id in enumerate(parent_ids):
                if parent_id < technician_id:
                    continue

                LOGGER.info(
                    ('Fetching session report for technician {} ({}/{}) '
                     'from {} to {}')
                    .format(parent_id, index + 1, len(parent_ids), start_date, end_date))

                self.client.make_request(
                    'https://secure.logmeinrescue.com/API/setReportDate_v2.aspx',  # noqa
                    'POST',
                    params={
                        'bdate': start_date.strftime('%-m/%-d/%Y %-H:%M:%S'),
                        'edate': end_date.strftime('%-m/%-d/%Y %-H:%M:%S')
                    })

                response = self.client.make_request(
                    self.get_url(),
                    'GET',
                    params={
                        'node': parent_id,
                        'nodetype': 'NODE',
                    })

                if return_first_response:
                    return response

                to_write = self.get_stream_data(response)

                if not return_first_response:
                    with singer.metrics.record_counter(endpoint=table) as ctr:
                        singer.write_records(table, to_write)

                        ctr.increment(amount=len(to_write))

                    self.state = incorporate(
                        self.state, table, 'technician_id', parent_id)
                    self.state = incorporate(
                        self.state, table, 'start_date', start_date)
                    self.state = save_state(self.state)

                elif len(to_write) > 0:
                    return to_write[0]

            start_date = end_date
            end_date = start_date + datetime.timedelta(days=7)
            technician_id = 0

            if not return_first_response:
                self.state = incorporate(
                    self.state, table, 'start_date', start_date)
                self.state = incorporate(
                    self.state, table, 'technician_id', technician_id,
                    force=True)
                self.state = save_state(self.state)


from django.urls import path

from . import views

urlpatterns = [
    path("", views.index, name="index"),
    path("login", views.login_view, name="login"),
    path("logout", views.logout_view, name="logout"),
    path("register", views.register, name="register"),
    path("new-post", views.new_post, name="new-post"),
    path("following/<int:page_id>", views.follow_view, name="following"),
    path("like/<int:post_id>", views.like_post, name="like-post"),
    path("user/<int:user_id>/page/<int:page_no>", views.view_user, name="user-posts"),
    path("edit/<int:post_id>", views.edit_post, name="edit-post"),
    path("page/<int:page_no>", views.page_load, name="page"),
]


from datetime import date, timedelta

from odoo import exceptions, fields
from odoo.tests.common import TransactionCase

class TestAccountDueListDaysOverdue(TransactionCase):
    def setUp(self):
        super().setUp()
        self.overdue_term_model = self.env["account.overdue.term"]
        self.account_user_type = self.env.ref("account.data_account_type_receivable")
        self.revenue_user_type = self.env.ref("account.data_account_type_revenue")
        self.account = self.env["account.account"]
        self.account_move_model = self.env["account.move"]
        self.account_move_line_model = self.env["account.move.line"]
        self.account_journal_model = self.env["account.journal"]
        self.overdue_term_1 = self.env.ref(
            "account_due_list_days_overdue.overdue_term_1"
        )
        self.partner_agrolait = self.env.ref("base.res_partner_1")
        self.currency_usd = self.env.ref("base.USD")
        self.payment_term = self.env.ref("account.account_payment_term_15days")
        self.product = self.env.ref("product.product_product_4")
        self.company = self.env.ref("base.main_company")
        self._create_account_type()
        self.sale_journal = self.account_journal_model.create(
            {
                "name": "Company Sale journal",
                "type": "sale",
                "code": "SALE",
                "company_id": self.company.id,
            }
        )
        # we create an invoice
        inv_date = date.today() - timedelta(days=16)
        inv_data = {
            "name": "invoice to customer",
            "partner_id": self.partner_agrolait.id,
            "currency_id": self.currency_usd.id,
            "invoice_date": fields.Date.to_string(inv_date),
            "journal_id": self.sale_journal.id,
            "move_type": "out_invoice",
            "invoice_payment_term_id": self.payment_term.id,
            "invoice_line_ids": [],
        }
        line_data = {
            "product_id": self.product.id,
            "name": "product that cost 100",
            "account_id": self.sales_account.id,
            "price_unit": 100,
            "quantity": 1,
        }
        inv_data["invoice_line_ids"].append((0, 0, line_data))
        self.invoice = self.account_move_model.create(inv_data)
        self.invoice.action_post()

    def _create_account_type(self):
        # Create receivable and sales test account
        self.receivable_account = self.account.create(
            {
                "name": "Recv - Test",
                "code": "test_recv",
                "user_type_id": self.account_user_type.id,
                "company_id": self.company.id,
                "reconcile": True,
            }
        )
        self.sales_account = self.account.create(
            {
                "name": "Local Sales - Test",
                "code": "test_sales",
                "user_type_id": self.revenue_user_type.id,
                "company_id": self.company.id,
            }
        )

    def test_workflow_open(self):
        self.assertEqual(self.invoice.state, "posted")

    def test_due_days(self):
        for line in self.invoice.line_ids:
            if line.account_id == self.receivable_account:
                self.assertEqual(
                    line.days_overdue,
                    1,
                    "Incorrect calculation of number of days " "overdue",
                )

    def test_overdue_term(self):
        self.account_move_line_model._register_hook()
        for line in self.invoice.line_ids:
            if line.account_id == self.receivable_account:
                self.assertEqual(
                    line[self.overdue_term_1.tech_name],
                    line.amount_residual,
                    "Overdue term 1-30 should contain a due amount",
                )

    def test_ovelapping_overdue_term(self):
        with self.assertRaises(exceptions.ValidationError):
            self.overdue_term_test = self.overdue_term_model.create(
                {"name": "25-30", "from_day": 25, "to_day": 30}
            )


from __future__ import annotations

from typing import List

class LevenshteinAutomata:
    def __init__(self, s: str, n: int = 0):
        self.term = s
        self.max_errors = n

    def __initial_state(self):
        """
        Get the initial state of an automata
        """
        return list(range(len(self.term) + 1))

    def __change_state(self, state: List, c: str):
        """
        Generate the next state of an automata with the given state and character
        """
        res = [state[0] + 1]
        for i in range(len(state) - 1):
            cost = 1
            if self.term[i] == c:
                cost = 0
            res.append(
                min(res[i] + 1, state[i] + cost, state[i + 1] + 1)
            )
        return [min(x, self.max_errors + 1) for x in res]

    def __can_match(self, state: List):
        """
        Return True if the given state is a match of the initial term's state
        """
        return min(state) <= self.max_errors

    def is_similar(self, s: str, max_errors: int = 0):
        """
        Check whether a given string is a match to the initial term
        """
        if not s:
            return False
        if len(s) < len(self.term) // 2:
            return False
        self.max_errors = max_errors
        state = self.__initial_state()
        for i in range(len(s)):
            state = self.__change_state(state, s[i])
        return self.__can_match(state)


import requests
import datetime
from tqdm import tqdm
import time

with open('token.txt', 'r') as file_object:
    token = file_object.read().strip()

class VKUserPhotos:

    URL = 'https://api.vk.com/method/photos.get'

    def __init__(self, token, version):
        self.params = {
            'access_token': token,
            'v': version
        }

    def get_photo(self, owner_id, album_id, extended, count):
        """
        Функция для получения json-файла с информацией о фотографиях
        """
        params_get_photo = {
            'owner_id': owner_id,
            'album_id': album_id,
            'extended': extended,
            'count': count
        }
        res = requests.get(self.URL, params={**self.params, **params_get_photo}).json()
        return res['response']['items']

    def json_for_saved(self, res):
        """
        Функция создает новый словарь, с необходимой нам информацией о фотографиях
        """
        final_json = []
        for i in res:
            my_dict = {}
            for j in i['sizes']:
                if j == i['sizes'][-1]:
                    my_dict['date'] = i['date']
                    my_dict['name'] = f'{i["likes"]["count"]}.jpg'
                    my_dict['url'] = j['url']
                    my_dict['size'] = j['type']
            final_json.append(my_dict)

        return final_json

    def creat_file(self, file, json_file):
        '''
        Функция создает папку на ПК с информацией по скаченным фотографиям
        '''
        self.file = file
        self.json_file = json_file
        with open(f'{self.file}.txt', 'wt') as f:
            f.write(str(json_file))

class YandexDiskUser:

    def __init__(self, token, folder):
        self.token = token
        self.ya_url = 'https://cloud-api.yandex.net/v1/disk/resources/upload?'
        self.folder = folder

    def get_headers(self):
        return {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'Authorization': f'OAuth {self.token}'
        }

    def creat_folder(self):
        URL = 'https://cloud-api.yandex.net/v1/disk/resources/'
        headers = self.get_headers()
        params = {'path': self.folder}
        result = requests.put(URL, params=params, headers=headers).json()

    def get_list_namefiles(self):
        """
        Функция возвращает нам список с названиями файлов, которые уже есть на Яндекс диске
        """
        URL = 'https://cloud-api.yandex.net/v1/disk/resources/files/'
        headers = self.get_headers()
        params = {'path': self.folder}
        list_name = []
        result = requests.get(URL, params=params, headers=headers).json()
        for i in result['items']:
            list_name.append(i['name'])
        return list_name

    def upload_file(self, json_list):
        """
        Функция загружает на ЯД фотографии по URL из полученного json-файла
        показывает прогресс загрузки фотографий на ЯД
        """
        replace = False
        headers = self.get_headers()
        for i in tqdm(json_list):
            list_name = self.get_list_namefiles()
            name_photo = i['name']
            if name_photo in list_name:
                timestamp = i['date']
                value = datetime.datetime.fromtimestamp(timestamp)
                value.strftime('%Y-%m-%d%H.%M.%S')
                value = str(value)
                value = value[0:11]
                i['name'] = f'{value}{i["name"]}'
                params = {
                    'path': f'{self.folder}/{i["name"]}',
                    'url': f'{(i["url"])}',
                    'overwrite': f'{replace}'
                }
                response = requests.post(self.ya_url, params=params, headers=headers)
                time.sleep(3)
            else:
                params = {
                    'path': f'{self.folder}/{i["name"]}',
                    'url': f'{(i["url"])}',
                    'overwrite': f'{replace}'
                }
                response = requests.post(self.ya_url, params=params, headers=headers)
                time.sleep(3)

if __name__ == '__main__':
    vk_client = VKUserPhotos(token, '5.131')
    result = (vk_client.get_photo(
        input('введите id VK: '),
        'profile',
        '1',
        input('Введите количество фотографий для скачивания: ')
    ))
    result = vk_client.json_for_saved(result)
    vk_client.creat_file(input('Введите название файла для сохранения информации: '), result)
    Ynd_disk = YandexDiskUser(input('Введите токен Яндекс Диск: '), input('Введите название папки для фотографий: '))
    Ynd_disk.creat_folder()
    Ynd_disk.upload_file(result)


from __future__ import annotations

import numpy as np

from manimlib.constants import BLUE_B, BLUE_D, BLUE_E, GREY_BROWN, WHITE
from manimlib.mobject.mobject import Mobject
from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.mobject.types.vectorized_mobject import VMobject
from manimlib.utils.rate_functions import smooth

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Callable, List, Iterable
    from manimlib.typing import ManimColor, Vect3, Self

"""
screen_rect = ScreenRectangle()
self.add(AnimatedBoundary(screen_rect, colors=[RED, YELLOW, GREEN]))
self.add(20)

效果：screen_rect的边界从红色到黄色到绿色，然后再从绿色到红色，循环往复
"""
class AnimatedBoundary(VGroup):
    '''
    动态变化的边界
    '''
    def __init__(
        self,
        vmobject: VMobject,
        colors: List[ManimColor] = [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
        max_stroke_width: float = 3.0,
        cycle_rate: float = 0.5,
        back_and_forth: bool = True,
        draw_rate_func: Callable[[float], float] = smooth,
        fade_rate_func: Callable[[float], float] = smooth,
        **kwargs
    ):
        super().__init__(**kwargs)
        self.vmobject: VMobject = vmobject
        self.colors = colors
        self.max_stroke_width = max_stroke_width
        self.cycle_rate = cycle_rate
        self.back_and_forth = back_and_forth
        self.draw_rate_func = draw_rate_func
        self.fade_rate_func = fade_rate_func

        self.boundary_copies: list[VMobject] = [
            vmobject.copy().set_style(
                stroke_width=0,
                fill_opacity=0
            )
            for x in range(2)
        ]
        self.add(*self.boundary_copies)
        self.total_time: float = 0
        self.add_updater(
            lambda m, dt: self.update_boundary_copies(dt)
        )

    def update_boundary_copies(self, dt: float) -> Self:
        # Not actual time, but something which passes at
        # an altered rate to make the implementation below
        # cleaner
        time = self.total_time * self.cycle_rate
        growing, fading = self.boundary_copies
        colors = self.colors
        msw = self.max_stroke_width
        vmobject = self.vmobject

        index = int(time % len(colors))
        alpha = time % 1
        draw_alpha = self.draw_rate_func(alpha)
        fade_alpha = self.fade_rate_func(alpha)

        if self.back_and_forth and int(time) % 2 == 1:
            bounds = (1 - draw_alpha, 1)
        else:
            bounds = (0, draw_alpha)
        self.full_family_become_partial(growing, vmobject, *bounds)
        growing.set_stroke(colors[index], width=msw)

        if time >= 1:
            self.full_family_become_partial(fading, vmobject, 0, 1)
            fading.set_stroke(
                color=colors[index - 1],
                width=(1 - fade_alpha) * msw
            )

        self.total_time += dt
        return self

    def full_family_become_partial(
        self,
        mob1: VMobject,
        mob2: VMobject,
        a: float,
        b: float
    ) -> Self:
        family1 = mob1.family_members_with_points()
        family2 = mob2.family_members_with_points()
        for sm1, sm2 in zip(family1, family2):
            sm1.pointwise_become_partial(sm2, a, b)
        return self

"""
c = Dot()
p = TracedPath(c.get_center, stroke_color=TEAL, time_traced=1)
self.add(c, p)
self.play(c.animate.shift(200 * RIGHT), run_time=20)
"""
"""
这里衍生出一个思考:
1.所有的动画的发起都是通过play函数
2.play函数会改变一些mobject对象的一些属性
3.如果有另一些mobject对象的属性是依赖于这些属性的，那么这些mobject对象的属性也会发生改变

mobjet_a(play) ---> mobject_a属性改变 ---> mobject_b(add_updater) ---> mobject_b属性改变
"""
class TracedPath(VMobject):
    """
    记录路径
    """
    def __init__(
        self,
        traced_point_func: Callable[[], Vect3], # 一般为 ``mob.get_center``
        time_traced: float = np.inf, # 追踪时间
        time_per_anchor: float = 1.0 / 15, # 采样时间间隔？
        stroke_width: float | Iterable[float] = 2.0,
        stroke_color: ManimColor = WHITE,
        fill_opacity: float = 0.0,
        **kwargs
    ):
        super().__init__(
            stroke_width=stroke_width,
            stroke_color=stroke_color,
            fill_opacity=fill_opacity,
            **kwargs
        )
        self.traced_point_func = traced_point_func
        self.time_traced = time_traced
        self.time_per_anchor = time_per_anchor
        self.time: float = 0
        self.traced_points: list[np.ndarray] = []
        # 每一帧都会自动调动update_path函数
        # dt是帧间隔
        # 1s有30帧，dt=1/30(和渲染质量有关)
        self.add_updater(lambda m, dt: m.update_path(dt))

    def update_path(self, dt: float) -> Self:
        if dt == 0:
            return self
        # traced_point_func一般为mob.get_center，所以这里是mob.get_center().copy()
        point = self.traced_point_func().copy()
        self.traced_points.append(point)

        """
        self.time_traced: 1
        dt: 1/30
        n_relevant_points: 30
        n_tps: 1...301, 31...301, 31...301
        """
        if self.time_traced < np.inf:
            n_relevant_points = int(self.time_traced / dt + 0.5)
            n_tps = len(self.traced_points)
            #print("+_"*100, "\n")
            #print(self.traced_points)
            #print(n_tps)
            #print(n_relevant_points)
            #print(dt)

            if n_tps < n_relevant_points:
                points = self.traced_points + [point] * (n_relevant_points - n_tps)
            else:
                points = self.traced_points[n_tps - n_relevant_points:]
            # Every now and then refresh the list
            if n_tps > 10 * n_relevant_points:
                self.traced_points = self.traced_points[-n_relevant_points:]
        else:
            points = self.traced_points

        #print("-"*100, "\n", points, len(points))

        if points:
            self.set_points_smoothly(points)

        self.time += dt
        #print(self.time)
        # 通过打印self.time可以断定，update_path函数每帧执行一次，每帧执行的时间间隔为dt。

        return self

class TracingTail(TracedPath):
    """
    自动减淡的轨迹

    需要注意：
    stroke_width和stroke_opacity的默认参数是tuple
    执行会报错
    """
    def __init__(
        self,
        mobject_or_func: Mobject | Callable[[], np.ndarray],
        time_traced: float = 1.0,
        stroke_width: float | Iterable[float] = (0, 3),
        stroke_opacity: float | Iterable[float] = (0, 1),
        stroke_color: ManimColor = WHITE,
        **kwargs
    ):
        if isinstance(mobject_or_func, Mobject):
            func = mobject_or_func.get_center
        else:
            func = mobject_or_func
        super().__init__(
            func,
            time_traced=time_traced,
            stroke_width=stroke_width,
            stroke_opacity=stroke_opacity,
            stroke_color=stroke_color,
            **kwargs
        )


import json

data = {
    'sales': [
        {'day' : 'Monday', 'revenue' : 100},
        {'day' : 'Tuesday', 'revenue' : 50},
        {'day' : 'Wednesday', 'revenue' : 80},
        {'day' : 'Thursday', 'revenue' : 120},
        {'day' : 'Friday', 'revenue' : 20},
        {'day' : 'Saturday', 'holiday' : True},
        {'day' : 'Sunday', 'holiday' : True},
    ],
    'expense': 14.99,
    'fires' : None
}

# write data
s = json.dumps(data)
print(s)

# read data
read_data = json.loads(s)
print(read_data)
# print(sum([day for day in read_data['sales'] if 'revenue' in day.keys()]))


"""
使用transformers库进行情感分析
"""
from torch.utils.data import Dataset, DataLoader
import pandas as pd

class SentimentDataset(Dataset):
	def __init__(self, path_to_file):
		self.dataset = pd.read_csv(path_to_file, sep="\t", names=["text", "label"])
	def __len__(self):
		return len(self.dataset)
	def __getitem__(self, idx):
		text = self.dataset.loc[idx, "text"]
		label = self.dataset.loc[idx, "label"]
		sample = {"text": text, "label": label}
		return sample

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from transformers import BertTokenizer
from transformers import BertForSequenceClassification, BertModel
from torch.utils.data import DataLoader
from transformers import AdamW

# 超参数
hidden_dropout_prob = 0.5
num_labels = 2
learning_rate = 1e-5
weight_decay = 1e-2
epochs = 5
max_len = 100
batch_size = 16
class_num = 2

base_path = "/content/drive/My Drive/Colab Notebooks/"
vocab_file = base_path + "PyTorch_Pretrained_Model/chinese_wwm_pytorch/vocab.txt"

# 使用GPU
# 然后通过model.to(device)的方式使用
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

data_path = base_path + "/data/sentiment/"
# 加载数据集
sentiment_train_set = SentimentDataset(data_path + "sentiment.train.data")
sentiment_train_loader = DataLoader(sentiment_train_set, batch_size=batch_size, shuffle=True, num_workers=2)

sentiment_valid_set = SentimentDataset(data_path + "sentiment.valid.data")
sentiment_valid_loader = DataLoader(sentiment_valid_set, batch_size=batch_size, shuffle=False, num_workers=2)

# 加载模型
# transformers也可以从网络下载预训练模型
# 如果是用本地路径确认3个文件的名称为config.json，pytorch_model.bin和vocab.txt
model = BertForSequenceClassification.from_pretrained("bert-base-uncased")

# 定义优化器和损失函数
# Prepare optimizer and schedule (linear warmup and decay)
no_decay = ['bias', 'LayerNorm.weight']
optimizer_grouped_parameters = [
        {'params': [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': weight_decay},
        {'params': [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
]
#optimizer = AdamW(model.parameters(), lr=learning_rate)
optimizer = AdamW(optimizer_grouped_parameters, lr=learning_rate)
criterion = nn.CrossEntropyLoss()
tokenizer = BertTokenizer(vocab_file)

def convert_text_to_ids(tokenizer, text, max_len=100):
	if isinstance(text, str):
		tokenized_text = tokenizer.encode_plus(text, max_length=max_len, add_special_tokens=True)
		input_ids = tokenized_text["input_ids"]
		token_type_ids = tokenized_text["token_type_ids"]
	elif isinstance(text, list):
		input_ids = []
		token_type_ids = []
		for t in text:
			tokenized_text = tokenizer.encode_plus(t, max_length=max_len, add_special_tokens=True)
			input_ids.append(tokenized_text["input_ids"])
			token_type_ids.append(tokenized_text["token_type_ids"])
	else:
		print("Unexpected input")
	return input_ids, token_type_ids

def seq_padding(tokenizer, X):
	pad_id = tokenizer.convert_tokens_to_ids("[PAD]")
	if len(X) <= 1:
		return torch.tensor(X, dtype=torch.long)
	L = [len(x) for x in X]
	ML = max(L)
	X = torch.tensor([x + [pad_id] * (ML - len(x)) if len(x) < ML else x for x in X], dtype=torch.long)
	return X

def train(model, iterator, optimizer, criterion, device):
	model.to(device)
	model.train()
	epoch_loss = 0
	epoch_acc = 0
	for i, batch in enumerate(iterator):
		label = batch["label"]
		text = batch["text"]
		input_ids, token_type_ids = convert_text_to_ids(tokenizer, text)
		input_ids = seq_padding(tokenizer, input_ids)
		token_type_ids = seq_padding(tokenizer, token_type_ids)
		# 标签形状为 (batch_size, 1)
		label = label.unsqueeze(1)
		# 需要 LongTensor
		# input_ids, token_type_ids, label = input_ids.long(), token_type_ids.long(), label.long()
		# 梯度清零
		optimizer.zero_grad()
		# 迁移到GPU
		input_ids, token_type_ids, label = input_ids.to(device), token_type_ids.to(device), label.to(device)
		# (loss), logits, (hidden_states), (attentions)
		# (hidden_states), (attentions) 不一定存在
		output = model(input_ids=input_ids, token_type_ids=token_type_ids, labels=label)
		y_pred_prob = output[1]
		y_pred_label = y_pred_prob.argmax(dim=1)
		# 计算loss
		#loss = criterion(y_pred_prob.view(-1, 2), label.view(-1))
		loss = output[0]
		# 计算acc
		acc = ((y_pred_label == label.view(-1)).sum()).item()
		# 反向传播
		loss.backward()
		optimizer.step()
		# epoch 中的 loss 和 acc 累加
		epoch_loss += loss.item()
		epoch_acc += acc
		if i % 100 == 0:
			print("current loss:", epoch_loss / (i+1), "\t", "current acc:", epoch_acc / ((i+1)*len(label)))
	return epoch_loss / len(iterator), epoch_acc / len(iterator.dataset.dataset)

def evaluate(model, iterator, criterion, device):
	model.to(device)
	model.eval()
	epoch_loss = 0
	epoch_acc = 0
	with torch.no_grad():
		for _, batch in enumerate(iterator):
			label = batch["label"]
			text = batch["text"]
			input_ids, token_type_ids = convert_text_to_ids(tokenizer, text)
			input_ids = seq_padding(tokenizer, input_ids)
			token_type_ids = seq_padding(tokenizer, token_type_ids)
			# 标签形状为 (batch_size, 1)
			label = label.unsqueeze(1)
			# 需要 LongTensor
			# input_ids, token_type_ids, label = input_ids.long(), token_type_ids.long(), label.long()
			# 迁移到GPU
			input_ids, token_type_ids, label = input_ids.to(device), token_type_ids.to(device), label.to(device)
			output = model(input_ids=input_ids, token_type_ids=token_type_ids, labels=label)
			y_pred_label = output[1].argmax(dim=1)
			loss = output[0]
			acc = ((y_pred_label == label.view(-1)).sum()).item()
			epoch_loss += loss.item()
			epoch_acc += acc
	return epoch_loss / len(iterator), epoch_acc / len(iterator.dataset.dataset)

for i in range(epochs):
	train_loss, train_acc = train(model, sentiment_train_loader, optimizer, criterion, device)
	valid_loss, valid_acc = evaluate(model, sentiment_valid_loader, criterion, device)
	print("\n")
	print("train loss: ", train_loss, "\t", "train acc:", train_acc)
	print("valid loss: ", valid_loss, "\t", "valid acc:", valid_acc, end="\n\n")

# 保存
import os
saved_model = "./saved_model"
saved_tokenizer = "./saved_tokenizer"
os.makedirs(saved_model)
os.makedirs(saved_tokenizer)
model.save_pretrained(saved_model)
tokenizer.save_pretrained(saved_tokenizer)


#%% Import modules
from src.figures import Plotting

settings = Plotting()
settings.set_rc_params(fontfamily='Arial', small=8, medium=10, big=12)
plot, axs = settings.get_figures(rows=1, cols=2, unit='cm', figwidth=10, figheight=10, sharex=True, sharey=True)

axs[0].plot([1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10])
axs[0].set_xlabel('x')
axs[0].set_ylabel('y')
axs[0].set_title('title')
axs[0].set_xlim(0, 11)
axs[0].set_ylim(0, 11)
axs[0].set_xticks([0, 5, 10])
axs[0].set_yticks([0, 5, 10])
axs[0].set_xticklabels(['0', '5', '10'])
axs[0].set_yticklabels(['0', '5', '10'])

axs[1].plot([1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10])
axs[1].set_xlabel('x')
axs[1].set_ylabel('y')
axs[1].set_title('title')
axs[1].set_xlim(0, 11)
axs[1].set_ylim(0, 11)
axs[1].set_xticks([0, 5, 10])
axs[1].set_yticks([0, 5, 10])
axs[1].set_xticklabels(['0', '5', '10'])
axs[1].set_yticklabels(['0', '5', '10'])

plot.set_style(offleft=5, offbottom=5, spinewidth=1.4)
plot.savefig('test.pdf')

# %%


import matplotlib.pyplot as py
import random
x = range(50)
y_shanghai= [random.uniform(15,18) for i in x]
y_beijing= [random.uniform(1,10) for i in x]

figure,axes = py.subplots(nrows = 1,ncols=2 )

axes[0].plot(x,y_shanghai,color='r',label="shanghai")
axes[1].plot(x,y_beijing,color='b',label="beijing")

#修改xy刻度
x_label = [ "11点{}分".format(i) for i in x]   #注意用{}
axes[0].set_xticks(x[::5],x_label[::5])
axes[0].set_yticks(range(0,40,5))
axes[1].set_xticks(x[::5],x_label[::5])
axes[1].set_yticks(range(0,40,5))

#comment
axes[0].grid(True)
axes[0].set_title("summary")
axes[0].set_xlabel("x-scale")
axes[0].set_ylabel("y-scale")
axes[0].legend(loc="upper right")   #显示图例
axes[1].grid(True)
axes[1].set_title("summary")
axes[1].set_xlabel("x-scale")
axes[1].set_ylabel("y-scale")
axes[1].legend(loc="upper right")   #显示图例

py.show()


from manimlib import *

"""
updater如果不使用dt参数，是很明显的被动触发方式
animation --> updater
执行动画的过程中，animation会作用于一个mob
mob的某些属性会在每一帧发生变化
而updater可以跟踪这些属性变化

updater如果使用dt参数，是很明显的主动触发方式
可以精确控制每一帧的行为
"""
class test(Scene):
    def construct(self):
        circle = Circle()
        dot = Dot()
        self.add(circle, dot)

        def circle_updater(mob, dt):
            mob.shift(dt * RIGHT)

        dot.add_updater(circle_updater)
        self.add(dot)  # Don't forget to add the dot to the scene if it hasn't been added already.

        self.wait(5)  # The updater will work during this time.

        dot.remove_updater(circle_updater)  # Optionally, remove the updater if it's no longer needed.

"""
链式触发
animation(circle) --> updater(dot) --> updater(text)
"""
class test1(Scene):
    def construct(self):
        circle = Circle()
        dot = Dot()
        text = Text("hello world")
        self.add(circle, dot, text)

        def dot_updater(mob):
            mob.move_to(circle.get_edge_center(RIGHT))

        def text_updater(mob):
            mob.move_to(dot.get_center() + UP * 2)

        dot.add_updater(dot_updater)
        text.add_updater(text_updater)

        self.add(dot)  # Don't forget to add the dot to the scene if it hasn't been added already.

        self.play(circle.animate.move_to(RIGHT * 4), run_time=3)  # The updater will work during this time.

        dot.remove_updater(dot_updater)  # Optionally, remove the updater if it's no longer needed.

# 对于updater来说，play和wait是一样的

"""
如何理解Transform类
需要注意，animation都是作用于一个mob，会在动画的过程中改变mob的属性
Transform类会传入两个参数，不过第一个参数是作用对象mob
随着动画的进行，mob的属性会发生变化
可以使用updater追踪mob的属性变化
"""
class test2(Scene):
    def construct(self):
        circle = Circle()
        square = Square().shift(RIGHT*3).set_color(BLUE_E)
        dot = Dot()
        text = Text("hello world")
        self.add(circle, dot, text)

        def dot_updater(mob):
            mob.move_to(circle.get_edge_center(RIGHT))

        def text_updater(mob):
            mob.move_to(dot.get_center() + UP * 2)

        dot.add_updater(dot_updater)
        text.add_updater(text_updater)

        # 非常有启发
        self.play(Transform(circle, square), run_time=3)  # The updater will work during this time.
        #self.play(TransformFromCopy(circle, square), run_time=3)

        dot.remove_updater(dot_updater)  # Optionally, remove the updater if it's no longer needed.


# Escreva um programa para aprovar o empréstimo bancário para a compra de uma casa.
# Pergunte o valor da casa, o salário do comprador e em quantos anos ele vai pagar.
# A prestação mensal não pode exceder 30% do salário ou então o empréstimo será negado.

print('\033[4;35mOlá, como vai?\033[m')
v = float(input('\nQual o valor da casa? R$'))
s = float(input('Qual o seu salário? R$'))
anos = int(input('Em quantos anos você vai pagar? '))
mensalidade = v / (anos * 12)
por_sal = s * 30 / 100
if mensalidade > por_sal:
    print('\n\033[1;31mO empréstimo foi negado, pois a mensalidade de {:.2f} é maior que 30% do seu salário.\033[m'.format(mensalidade))
else:
    print('\n'
          '\033[1;36mParabéns, seu empréstimo foi aprovado! '
          'Você terá que pagar R${:.2f} por {} anos.\033[m'.format(mensalidade, anos))


# -*- coding: utf-8 -*-
"""
Created on Sat Feb  1 15:20:43 2020

@author: magut
"""
import datetime as dt
import time
from app import db, Config
from app.main import bp
from flask import render_template, flash, redirect, url_for, request, Response
from app.main.forms import LoginForm, FilterTable
from flask_login import current_user, login_user, logout_user, login_required
from app.tables_test import User, Position, LogMessage
from app.util import (calculate_performance_user, get_positions_from_splits,
                    get_positions_dti, sort_positions, get_positions_dto,
                    get_positions_groi, get_positions_roi, filter_positions_date)
from werkzeug.urls import url_parse

@bp.route('/', methods=['GET', 'POST'])
@bp.route('/index', methods=['GET', 'POST'])
@login_required
def index():
    if not  current_user.isadmin:
        positions = get_positions_from_splits(current_user)
        # get datetimes of positions
        dti_positions = get_positions_dti(positions)
        # get performance
        performance = calculate_performance_user(current_user, positions, dti_positions)
        # get indexes ordered
        idx_ordered = sort_positions(dti_positions)
        # log login event
        current_user.log_event("LOGIN")
        # render page
        return render_template('index.html', title='Home', performance=performance, vector_pos=idx_ordered)
    else: return redirect('/admin')

@bp.route('/profile/<username>', methods=['GET'])
@login_required
def profile(username):
    # user = User.query.filter_by(username=username).first_or_404()
    return render_template('profile.html', title='Profile')

@bp.route('/dashboard/<username>', methods=['GET','POST'])
@login_required
def dashboard(username):
    if current_user.username!=username:
        return redirect(url_for('main.login'))
    user = User.query.filter_by(username=username).first_or_404()
    filterform = FilterTable()
    #get user's splits
    positions = get_positions_from_splits(user)
    # get opening datetimes of positions
    dti_positions = get_positions_dti(positions)
    if not filterform.validate_on_submit():
        # GET request
        start_date = None
        end_date = None
    else:
        # POST request
        today = dt.datetime.today()
        tomorrow = today+dt.timedelta(days=1)
        yesterday = today-dt.timedelta(days=1)
        if filterform.dates.data=='today':
            start_date = dt.datetime(today.year, today.month, today.day)
            end_date = dt.datetime(tomorrow.year, tomorrow.month, tomorrow.day)
        elif filterform.dates.data=='yesterday':
            start_date = dt.datetime(yesterday.year, yesterday.month, yesterday.day)
            end_date = dt.datetime(today.year, today.month, today.day)
        elif filterform.dates.data=='week':
            first_weekday = today-dt.timedelta(days=today.weekday())
            start_date = dt.datetime(first_weekday.year, first_weekday.month, first_weekday.day)
            end_date = dt.datetime(tomorrow.year, tomorrow.month, tomorrow.day)
            print(today.weekday())
        elif filterform.dates.data=='month':
            #first_monthday = today-dt.timedelta(days=today.day-1)
            start_date = dt.datetime(today.year, today.month, 1)
            end_date = dt.datetime(tomorrow.year, tomorrow.month, tomorrow.day)
        elif filterform.dates.data=='YTD':
            start_date = dt.datetime(today.year, 1, 1)
            end_date = dt.datetime(tomorrow.year, tomorrow.month, tomorrow.day)
        else:
            start_date = None
            end_date = None
            flash('Not implemented yet')

        # start_date_str='2020.01.06 00:00:00'
        # end_date_str='2020.01.06 04:00:00'
    print(start_date)
    print(end_date)
    filtered_dtis = filter_positions_date(positions, dti_positions,
                                        start_date=start_date,
                                        end_date=end_date)
    dti_positions_filtered = [dti_positions[i] for i in filtered_dtis]
    positions_filtered = [positions[i] for i in filtered_dtis]
    # get closing datetimes of positions
    dto_positions = get_positions_dto(positions_filtered)
    # get closing datetimes of positions
    groi_positions = get_positions_groi(positions_filtered)
    # get closing datetimes of positions
    roi_positions = get_positions_roi(positions_filtered)
    # get indexes ordered
    idx_ordered = sort_positions(dti_positions_filtered)
    return render_template('dashboard.html', title=username+"'s Dashboard", positions=positions_filtered,
                            vector_pos=idx_ordered, dti_positions=dti_positions_filtered, dto_positions=dto_positions,
                            groi_positions=groi_positions, roi_positions=roi_positions, filterform=filterform)

@bp.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password')
            return redirect(url_for('main.login'))
        login_user(user, remember=form.remember_me.data)
        next_page = request.args.get('next')
        if not next_page or url_parse(next_page).netloc != '':
            next_page = url_for('main.index')
        current_user.log_event("LOGIN")
        return redirect(next_page)
    return render_template('login.html', title='Sign In', form=form)

@bp.route('/logout')
def logout():
    current_user.log_event("LOGOUT")
    logout_user()
    return redirect(url_for('main.index'))

@bp.route('/favicon.ico', methods=['GET'])
#@login_required
def icon():
    return "ICON NOT ADDED"

# @bp.route('/streamNetwork')
# def streamNetwork():
#     def eventStream():
#         ass_idx = -1
#         assets = Config.ASSETS
#         indx_assets = Config.indx_assets
#         while True:
#             ass_idx = (ass_idx+1) % len(assets)
#             asset = assets[indx_assets[ass_idx]]
#             yield 'data: {}/{}\n\n'.format(get_log_network(asset), asset)
#     return Response(eventStream(), mimetype="text/event-stream")

# @bp.route('/streamTrader')
# def streamTrader():
#     def eventStream():
#         ass_idx = -1
#         assets = Config.ASSETS
#         indx_assets = Config.indx_assets
#         while True:
#             # update asset index
#             ass_idx = (ass_idx+1) % len(assets)
#             asset = assets[indx_assets[ass_idx]]
#             # wait for source data to be available, then push it
#             yield "data: {}/{}\n\n".format(get_log_trader(asset), asset)
#     return Response(eventStream(), mimetype="text/event-stream")

# def get_log_trader(asset):
#     '''this could be any function that blocks until data is ready'''

#     time.sleep(0.1)
#     # user = User.query.filter_by(username="kaissandra").first()
#     global tradeLogMsg
#     logmessage = LogMessage.query.filter_by(origin="TRADER", asset=asset).first()
#     if not logmessage:
#         return "WAITING FOR CONNECTION"
#     else:
#         return tradeLogMsg#logmessage.message

# def get_log_network(asset):
#     '''this could be any function that blocks until data is ready'''
#     time.sleep(0.1)
#     # user = User.query.filter_by(username="kaissandra").first()
#     global netLogMsg
#     logmessage = LogMessage.query.filter_by(origin="NETWORK", asset=asset).first()
#     if not logmessage:
#         return "WAITING FOR CONNECTION"
#     else:
#         return netLogMsg#logmessage.message
#         # s = time.ctime(time.time())


'''
Components that are not abstract but are still used between cogs.
'''

import logging
from discord.ui import Modal, InputText, Button
import discord.ui.view as core_view
from discord.ext.commands import Bot
from database.models import Game
from utils import config
from utils.errors import ValueAccessError

logger = logging.getLogger(f'civviebot.{__name__}')

class View(core_view.View):
    '''
    View class with overridden timeout parameters.
    '''

    def __init__(self, *args, **kwargs):
        '''
        Constructor that forces view timeout to None.
        '''
        kwargs['timeout'] = None
        super().__init__(*args, **kwargs)

class NotifyIntervalInput(InputText):
    '''
    Input text for setting a game's notify interval.

    Uses custom_id 'notify_interval'.
    '''

    def __init__(self, *args, remind_interval: int = None, **kwargs):
        '''
        The notify interval to set. Will be the global config if not passed in.
        '''
        if kwargs.get('value', None) is None:
            kwargs['value'] = str(
                config.REMIND_INTERVAL
                if remind_interval is None
                else remind_interval
            )
        if kwargs.get('label', None) is None:
            kwargs['label'] = 'Seconds between re-pings (use 0 to disable):'
        kwargs['custom_id'] = 'notify_interval'
        super().__init__(*args, **kwargs)

class MinTurnsInput(InputText):
    '''
    Input text for setting a game's minimum turns.

    Uses custom_id 'min_turns'.
    '''

    def __init__(self, *args, min_turns: int = None, **kwargs):
        '''
        The minimum turns to set. Will get the global config if not passed in.
        '''
        if kwargs.get('value', None) is None:
            kwargs['value'] = (
                config.MIN_TURNS
                if min_turns is None
                else str(min_turns)
            )
        if kwargs.get('label', None) is None:
            kwargs['label'] = 'Start notifying after turn:'
        super().__init__(custom_id='min_turns', *args, **kwargs)

class ChannelAwareModal(Modal):
    '''
    A component that stores a channel_id and bot.
    '''

    def __init__(self, channel_id: int, bot: Bot, *args, **kwargs):
        '''
        Constructor; sets the channel_id and bot.
        '''
        self._channel_id = channel_id
        self._bot = bot
        super().__init__(*args, **kwargs)

    @property
    def channel_id(self) -> int:
        '''
        The channel this URL interaction was initiated from.
        '''
        return self._channel_id

    @property
    def bot(self) -> Bot:
        '''
        Instance of CivvieBot passed in from the interaction.
        '''
        return self._bot

    def get_child_value(self, custom_id: str):
        '''
        Gets the value of a child component by its custom_id.
        '''
        try:
            component = next(
                filter(lambda c: c.custom_id == custom_id, self.children)
            )
        except StopIteration as error:
            raise IndexError(
                f'Accessed a non-existent component by custom_id {custom_id}'
            ) from error
        if component.value is None:
            raise ValueAccessError(
                (
                    f'Accessed value of component by custom_id {custom_id} '
                    'before it was set'
                )
            )
        return component.value

class GameAwareButton(Button):
    '''
    Button component that stores a Game.
    '''

    def __init__(self, game: Game, *args, **kwargs):
        '''
        Constructor; sets the game_id, channel_id and bot.
        '''
        self._game = game.id
        self._channel_id = game.webhookurl.channelid
        super().__init__(*args, **kwargs)

    @property
    def game(self) -> int:
        '''
        The game this button is tracking.
        '''
        return self._game

    @property
    def channel_id(self) -> int:
        '''
        The channel this button is in.
        '''
        return self._channel_id


from abc import (
    ABCMeta,
    abstractmethod,
)
from typing import (
    Iterable,
    List,
    Union,
)

class PxdNode(metaclass=ABCMeta):
    indent: str = "    "

    def __str__(self):
        return "\n".join(self.lines())

    @abstractmethod
    def lines(self) -> List[str]:
        pass

class IdentifierType(PxdNode):
    __slots__ = ("name", "type_name")
    name: str
    type_name: str

    def __init__(self, name, type_name):
        self.name = name or ""
        self.type_name = type_name

    def lines(self) -> List[str]:
        if self.name:
            return [f"{self.type_name} {self.name}"]
        return [self.type_name]

class Function(PxdNode):
    __slots__ = ("return_type", "name", "args")

    return_type: str
    name: str
    args: List[PxdNode]

    def __init__(self, return_type: str, name: str, args: List[PxdNode]):
        self.return_type = return_type
        self.name = name
        self.args = args

    def argstr(self) -> str:
        arguments_list = []
        for arg in self.args:
            lines = arg.lines()
            assert len(lines) == 1
            arguments_list.append(lines[0])
        return ", ".join(arguments_list)

    def lines(self) -> List[str]:
        return [f"{self.return_type} {self.name}({self.argstr()})"]

class Ptr(IdentifierType):
    __slots__ = ("node",) + IdentifierType.__slots__

    node: PxdNode

    def __init__(self, node: IdentifierType, quals: Iterable[str] = ()):
        self.node = node
        if isinstance(node, Function):
            type_name = node.return_type
        else:
            type_name = self.node.type_name
        type_name = f"{type_name}*"
        # Cython supports const and volatile C type qualifiers
        for qual in ("const", "volatile"):
            if qual in quals:
                type_name = f"{type_name} {qual}"
        super().__init__(self.node.name, type_name)

    def lines(self) -> List[str]:
        if isinstance(self.node, Function):
            f = self.node
            args = f.argstr()
            return [f"{f.return_type} (*{f.name})({args})"]
        return super().lines()

class Array(IdentifierType):
    __slots__ = ("node", "dimensions")

    node: PxdNode
    dimensions: List[int]

    def __init__(self, node: PxdNode, dimensions: Union[None, List[int]] = None):
        if dimensions is None:
            dimensions = [1]
        self.node = node
        self.dimensions = dimensions
        if self.dimensions:
            name = self.node.name + "[" + "][".join([str(dim) for dim in self.dimensions]) + "]"
        else:
            name = self.node.name
        super().__init__(name, self.node.type_name)

class Type(PxdNode):
    __slots__ = ("node",)

    node: PxdNode

    def __init__(self, node: PxdNode):
        self.node = node

    def lines(self) -> List[str]:
        lines = self.node.lines()
        lines[0] = "ctypedef " + lines[0]
        return lines

class Block(PxdNode):
    __slots__ = ("name", "fields", "kind", "statement")

    name: str
    fields: List[PxdNode]
    kind: str
    statement: str

    def __init__(self, name: str, fields: List[PxdNode], kind: str, statement: str = "cdef"):
        self.name = name
        self.fields = fields
        self.kind = kind
        self.statement = statement

    def lines(self) -> List[str]:
        rv = [f"{self.statement} {self.kind} {self.name}"]
        if self.fields:
            rv[0] += ":"
        for field in self.fields:
            for line in field.lines():
                rv.append(self.indent + line)
        return rv

class Enum(PxdNode):
    __slots__ = ("name", "items", "statement")

    name: str
    items: List[str]
    statement: str

    def __init__(self, name, items: List[str], statement="cdef"):
        self.name = name
        self.items = items
        self.statement = statement

    def lines(self) -> List[str]:
        rv = []
        if self.name:
            rv.append(f"{self.statement} enum {self.name}:")
        else:
            rv.append("cpdef enum:")
        for item in self.items:
            rv.append(self.indent + item)
        return rv