import numpy as np
import torch
from sklearn.model_selection import train_test_split
import time
import pickle
from tqdm.notebook import trange, tqdm

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning or DeprecationWarning)

#############data preprocessing###########
#level of latent variables
k=5

#load data
data=np.load("Code_python/testdata/data_latents_for_{}.npy".format(k),allow_pickle=True)

X=[]
Y=[]

for i,elem in enumerate(data):
    x,label=elem
    if label["gender"]=="female":
        X.append(x)
        Y.append(0)
    elif label["gender"]=="male":
        X.append(x)
        Y.append(1)

coord=[]
for x1 in X:
  ze=[]
  for x2 in x1:
    ze+=torch.flatten(x2).tolist()
  coord.append(ze)

x_train = []
y_train = []

x_test = []
y_test = []

x_train, x_test, y_train, y_test= train_test_split(coord, Y, train_size=0.8)

BATCH_SIZE = 64

train_iterator = data.DataLoader(x_train,y_train,
                                 shuffle=True,
                                 batch_size=BATCH_SIZE)

test_iterator = data.DataLoader(x_test,y_test,
                                batch_size=BATCH_SIZE)

valid_iterator = data.DataLoader(x_test,y_test,
                                 batch_size=BATCH_SIZE)

########## MLP ###################
import torch
from torch import nn

class Net(nn.Module):
  def __init__(self,input_shape):
    super(Net,self).__init__()
    self.fc1 = nn.Linear(input_shape,80)
    self.fc2 = nn.Linear(80,40)
    self.fc3 = nn.Linear(40,10)
    self.fc4 = nn.Linear(10,1)

  def forward(self,x):
    batch_size = x.shape[0]

    x = x.view(batch_size, -1)
    x = torch.relu(self.fc1(x))
    x = torch.relu(self.fc2(x))
    x = torch.relu(self.fc3(x))
    x = torch.sigmoid(self.fc4(x))
    return x

model = Net(input_shape=len(x_test[0]))

learning_rate = 0.01
epochs = 3
criterion = nn.CrossEntropyLoss()

optimizer = torch.optim.Adam(model.parameters())
loss_fn = nn.BCELoss()
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)
criterion = criterion.to(device)
# Run the training loop
losses = []
accur = []
validation_score=[]

def score_test(x,y):
  prediction = model(torch.tensor(x,dtype=torch.float32))
  acc = (prediction.reshape(-1).detach().numpy().round() == y).mean()
  return acc

t1=time.time()

def train(model, iterator, optimizer, criterion, device):

  for (x, y) in zip(x_train,y_train):

    epoch_loss = 0
    epoch_acc = 0

    model.train()

    optimizer.zero_grad()
    y_pred= model(x)
    loss = criterion(y_pred, y)
    acc = score_test(y_pred, y)

    loss.backward()
    optimizer.step()

    epoch_loss += loss.item()
    epoch_acc += acc.item()

    return epoch_loss/len(iterator), epoch_acc / len(iterator)

def evaluate(model, iterator, criterion, device):

    epoch_loss = 0
    epoch_acc = 0

    model.eval()

    with torch.no_grad():

        for (x, y) in tqdm(iterator, desc="Evaluating", leave=False):

            x = x.to(device)
            y = y.to(device)

            y_pred, _ = model(x)

            loss = criterion(y_pred, y)

            acc = calculate_accuracy(y_pred, y)

            epoch_loss += loss.item()
            epoch_acc += acc.item()

    return epoch_loss / len(iterator), epoch_acc / len(iterator)

def calculate_accuracy(y_pred, y):
    top_pred = y_pred.argmax(1, keepdim=True)
    correct = top_pred.eq(y.view_as(top_pred)).sum()
    acc = correct.float() / y.shape[0]
    return acc

with open('graph/loss.npy',"wb") as f:
  pickle.dump(losses,f)

with open('graph/accuracy.npy',"wb") as f:
  pickle.dump(accur,f)

with open('graph/validaton_score.npy',"wb") as f:
  pickle.dump(validation_score,f)

def epoch_time(start_time, end_time):
    elapsed_time = end_time - start_time
    elapsed_mins = int(elapsed_time / 60)
    elapsed_secs = int(elapsed_time - (elapsed_mins * 60))
    return elapsed_mins, elapsed_secs

EPOCHS = 10

best_valid_loss = float('inf')

for epoch in trange(EPOCHS):

    start_time = time.monotonic()

    train_loss, train_acc = train(model, train_iterator, optimizer, criterion, device)
    valid_loss, valid_acc = evaluate(model, valid_iterator, criterion, device)

    if valid_loss < best_valid_loss:
        best_valid_loss = valid_loss
        torch.save(model.state_dict(), 'tut1-model.pt')

    end_time = time.monotonic()

    epoch_mins, epoch_secs = epoch_time(start_time, end_time)

    print(f'Epoch: {epoch+1:02} | Epoch Time: {epoch_mins}m {epoch_secs}s')
    print(f'\tTrain Loss: {train_loss:.3f} | Train Acc: {train_acc*100:.2f}%')
    print(f'\t Val. Loss: {valid_loss:.3f} |  Val. Acc: {valid_acc*100:.2f}%')

t2=time.time()
print("Temps=",int(t2-t1),"s")


__author__ = 'arthurb'

import treq

from twisted.python import log
from jsonschema import validate

class ReCaptcha:
    def __init__(self, private_key, public_key):
        """

        :param private_key:
        :type private_key: str
        :param public_key:
        :type public_key: str
        """
        self.private_key, self.public_key = private_key, public_key

    def verify(self, remote_ip, challenge, response):
        """

        :param remote_ip:
        :param challenge:
        :param response:
        :returns: Deferred
        :raises: Exception
        """

        def handle_response(response):

            def parse_content(content):
                if response.code != 200:
                    log.msg('Received code: %d from Google for recaptcha' % response.code)
                    raise Exception("Recaptacha returned code: %d" % response.code)
                c = content.splitlines().append('') #being cheeky here
                if len(c) < 2:
                    log.err("Received unexpected response from recaptcha: %s" % content)
                    raise Exception("Received unexpected response from recaptcha: %s" % content)
                return [c[0] == "true", c[1]] #eheh c[1] always exist

            return response.content().addCallback(parse_content)

        d = treq.post(self.url, data={
            'privatekey': self.private_key,
            'remoteip': remote_ip,
            'challenge': challenge,
            'response': response}, timeout=5)

        d.addCallback(handle_response)
        return d


from tkinter import *
from tkinter import messagebox

window = Tk()

window.title("Tic-Tac-Toe ")
window.geometry("400x300")

label = Label(window, text="Gracz X")
label.grid(row=0, column=0)

label = Label(window, text="Gracz O")
label.grid(row=1, column=0)

turn = 1

def clickedprocessing():

    global turn
    if turn == 1:
        turn = 2
        return "X"
    elif turn == 2:
        turn = 1;
        return "O"

def clicked1():

    global turn
    if button1["text"] == " ":
        button1["text"] = clickedprocessing()
        check()

def clicked2():

    global turn
    if button2["text"] == " ":
        button2["text"] = clickedprocessing()
        check()

def clicked3():

    global turn
    if button3["text"] == " ":
        button3["text"] = clickedprocessing()
        check()

def clicked4():

    global turn
    if button4["text"] == " ":
        button4["text"] = clickedprocessing()
        check()

def clicked5():

    global turn
    if button5["text"] == " ":
        button5["text"] = clickedprocessing()
        check()

def clicked6():

    global turn
    if button6["text"] == " ":
        button6["text"] = clickedprocessing()
        check()

def clicked7():

    global turn
    if button7["text"] == " ":
        button7["text"] = clickedprocessing()
        check()

def clicked8():

    global turn
    if button8["text"] == " ":
        button8["text"] = clickedprocessing()
        check()

def clicked9():

    global turn
    if button9["text"] == " ":
        button9["text"] = clickedprocessing()
        check()

def check():

    b1 = button1["text"]
    b2 = button2["text"]
    b3 = button3["text"]
    b4 = button4["text"]
    b5 = button5["text"]
    b6 = button6["text"]
    b7 = button7["text"]
    b8 = button8["text"]
    b9 = button9["text"]
    flag += 1

    if b1 == b2 and b1 == b3 and b1 == "O" or b1 == b2 and b1 == b3 and b1 == "X":
        win(b1)
    if b4 == b5 and b4 == b6 and b4 == "O" or b4 == b5 and b4 == b6 and b4 == "X":
        win(b4)
    if b7 == b8 and b7 == b9 and b7 == "O" or b7 == b8 and b7 == b9 and b7 == "X":
        win(b7)
    if b1 == b4 and b1 == b7 and b1 == "O" or b1 == b4 and b1 == b7 and b1 == "X":
        win(b1)
    if b2 == b5 and b2 == b8 and b2 == "O" or b2 == b5 and b2 == b8 and b2 == "X":
        win(b2)
    if b3 == b6 and b3 == b9 and b3 == "O" or b3 == b6 and b3 == b9 and b3 == "X":
        win(b3)
    if b1 == b5 and b1 == b9 and b1 == "O" or b1 == b5 and b1 == b9 and b1 == "X":
        win(b1)
    if b7 == b5 and b7 == b3 and b7 == "O" or b7 == b5 and b7 == b3 and b7 == "X":
        win(b7)
    elif flag == 9:
        messagebox.showinfo("DRAW !", "DRAW")
        window.destroy()

    def win(player):
        ans = "Zwyciezyl" +player
        messagebox.showinfo("Wygrana", ans)
        window.destroy()

button1 = Button(window, text=" ", bg="green", fg="Black", width=3, height=1, font=('Helvetica', '20'), command=clicked1)
button1.grid(column=1, row=1)
button2 = Button(window, text=" ", bg="green", fg="Black", width=3, height=1, font=('Helvetica', '20'), command=clicked2)
button2.grid(column=2, row=1)
button3 = Button(window, text=" ", bg="green", fg="Black", width=3, height=1, font=('Helvetica', '20'), command=clicked3)
button3.grid(column=3, row=1)
button4 = Button(window, text=" ", bg="green", fg="Black", width=3, height=1, font=('Helvetica', '20'), command=clicked4)
button4.grid(column=1, row=2)
button5 = Button(window, text=" ", bg="green", fg="Black", width=3, height=1, font=('Helvetica', '20'), command=clicked5)
button5.grid(column=2, row=2)
button6 = Button(window, text=" ", bg="green", fg="Black", width=3, height=1, font=('Helvetica', '20'), command=clicked6)
button6.grid(column=3, row=2)
button7 = Button(window, text=" ", bg="green", fg="Black", width=3, height=1, font=('Helvetica', '20'), command=clicked7)
button7.grid(column=1, row=3)
button8 = Button(window, text=" ", bg="green", fg="Black", width=3, height=1, font=('Helvetica', '20'), command=clicked8)
button8.grid(column=2, row=3)
button9 = Button(window, text=" ", bg="green", fg="Black", width=3, height=1, font=('Helvetica', '20'), command=clicked9)
button9.grid(column=3, row=3)

flag = 0

window.mainloop()


import warnings
from collections import Counter
import numpy as np
import pickle
from sklearn.exceptions import NotFittedError
from sklearn.utils.validation import check_is_fitted
import networkx
data_path = "data/"
from six import iteritems
from six import itervalues
from scipy.sparse import csr_matrix
from utils import grid_search, results_to_csv, plot_graphs, insert_zeros
from kernel import Kernel
from VertexHistogram import VertexHistogram
from tqdm import tqdm

class WeisfeilerLehman(Kernel):
    """Compute the Weisfeiler Lehman Kernel.

    Parameters
    ----------
    n_iter : int, default=5
        The number of iterations.
    """

    def __init__(self,
                 normalize=False, n_iter=5, base_graph_kernel=VertexHistogram):
        """Initialise a `weisfeiler_lehman` kernel."""
        super(WeisfeilerLehman, self).__init__(normalize=normalize)

        self.n_iter = n_iter
        self.base_graph_kernel = base_graph_kernel
        self._initialized.update({"n_iter": False, "base_graph_kernel": False})

    def initialize(self):
        """Initialize all transformer arguments, needing initialization."""
        super(WeisfeilerLehman, self).initialize()
        if not self._initialized["base_graph_kernel"]:

            base_graph_kernel, params = VertexHistogram, dict()
            params["normalize"] = False
            self._base_graph_kernel = base_graph_kernel
            self._params = params
            self._initialized["base_graph_kernel"] = True

        if not self._initialized["n_iter"]:
            self._n_iter = self.n_iter + 1
            self._initialized["n_iter"] = True

    def pre_calc_matrix(self, X):
        """Get_ready to compute matrix.
        Parameters
        ----------
        X : iterable of graphs
        Returns
        -------
        base_graph_kernel : object
        Returns base_graph_kernel.
        """
        if hasattr(self, '_X_diag'):
            # Clean _X_diag value
            delattr(self, '_X_diag')

        nx = 0
        Gs_ed, L, distinct_values, extras = dict(), dict(), set(), dict()
        for (idx, x) in enumerate(iter(X)):
            el = networkx.get_edge_attributes(x, 'labels')
            if el is None:
                extra = tuple()
            else:
                extra = (el, )
            # import pdb; pdb.set_trace()
            Gs_ed[nx] = networkx.to_dict_of_dicts(x)
            Gs_ed[nx] = {k1: {k2: v2['labels'][0] for k2, v2 in v1.items()} for k1, v1 in Gs_ed[nx].items()}

            L[nx] = networkx.get_node_attributes(x, 'labels')
            L[nx] = {key: value[0] for key, value in L[nx].items()}
            extras[nx] = extra
            distinct_values |= set(itervalues(L[nx]))
            nx += 1
        if nx == 0:
            raise ValueError('parsed input is empty')

        # Save the number of "fitted" graphs.
        self._nx = nx

        # get all the distinct values of current labels
        WL_labels_inverse = dict()

        # assign a number to each label
        label_count = 0
        for dv in sorted(list(distinct_values)):
            WL_labels_inverse[dv] = label_count
            label_count += 1

        # Initalize an inverse dictionary of labels for all iterations
        self._inv_labels = dict()
        self._inv_labels[0] = WL_labels_inverse

        def generate_graphs(label_count, WL_labels_inverse):
            new_graphs = list()
            for j in range(nx):
                new_labels = dict()
                for k in L[j].keys():
                    new_labels[k] = WL_labels_inverse[L[j][k]]
                L[j] = new_labels
                # add new labels
                new_graphs.append((Gs_ed[j], new_labels) + extras[j])
            yield new_graphs

            for i in range(1, self._n_iter):
                label_set, WL_labels_inverse, L_temp = set(), dict(), dict()
                for j in range(nx):
                    # Find unique labels and sort
                    # them for both graphs
                    # Keep for each node the temporary
                    L_temp[j] = dict()
                    for v in Gs_ed[j].keys():
                        credential = str(L[j][v]) + "," + \
                            str(sorted([L[j][n] for n in Gs_ed[j][v].keys()]))
                        L_temp[j][v] = credential
                        label_set.add(credential)

                label_list = sorted(list(label_set))
                for dv in label_list:
                    WL_labels_inverse[dv] = label_count
                    label_count += 1

                # Recalculate labels
                new_graphs = list()
                for j in range(nx):
                    new_labels = dict()
                    for k in L_temp[j].keys():
                        new_labels[k] = WL_labels_inverse[L_temp[j][k]]
                    L[j] = new_labels
                    # relabel
                    new_graphs.append((Gs_ed[j], new_labels) + extras[j])
                self._inv_labels[i] = WL_labels_inverse
                yield new_graphs

        base_graph_kernel = {i: self._base_graph_kernel(**self._params) for i in range(self._n_iter)}
        import pdb; pdb.set_trace()
        if self._method_calling == 1:
            for (i, g) in tqdm(enumerate(generate_graphs(label_count, WL_labels_inverse))):
                base_graph_kernel[i].fit(g)
        elif self._method_calling == 2:
            graphs = generate_graphs(label_count, WL_labels_inverse)
            values = [
                base_graph_kernel[i].fit_transform(g) for (i, g) in enumerate(graphs)
            ]
            K = np.sum(values, axis=0)

        if self._method_calling == 1:
            return base_graph_kernel
        elif self._method_calling == 2:
            return K, base_graph_kernel

    def fit_transform(self, X, y=None):
        """Fit and transform, on the same dataset.
        Parameters
        ----------
        X : graphs iterable

        Returns
        -------
        K : numpy array, shape = [n_targets, n_input_graphs]
            corresponding to the kernel matrix, a calculation between
            all pairs of graphs between target an features
        """
        self._method_calling = 2
        self._is_transformed = False
        self.initialize()
        km, self.X = self.pre_calc_matrix(X)

        self._X_diag = np.diagonal(km)
        if self.normalize:
            old_settings = np.seterr(divide='ignore')
            km = np.nan_to_num(np.divide(km, np.sqrt(np.outer(self._X_diag, self._X_diag))))
            np.seterr(**old_settings)
        return km

    def transform(self, X):
        """Calculate the kernel matrix, between given and fitted dataset.
        Parameters
        ----------
        X : iterable of graphs

        Returns
        -------
        K : numpy array, shape = [n_targets, n_input_graphs]
            corresponding to the kernel matrix, a calculation between
            all pairs of graphs between target an features
        """
        self._method_calling = 3
        # Check is fit had been called
        check_is_fitted(self, ['X', '_nx', '_inv_labels'])
        nx = 0
        distinct_values = set()
        Gs_ed, L = dict(), dict()
        for (i, x) in enumerate(iter(X)):
            Gs_ed[nx] = networkx.to_dict_of_dicts(x)
            Gs_ed[nx] = {k1: {k2: v2['labels'][0] for k2, v2 in v1.items()} for k1, v1 in Gs_ed[nx].items()}
            L[nx] = networkx.get_node_attributes(x, 'labels')
            L[nx] = {key: value[0] for key, value in L[nx].items()}
            # Hold all the distinct values
            distinct_values |= set(
                v for v in itervalues(L[nx])
                if v not in self._inv_labels[0])
            nx += 1

        nl = len(self._inv_labels[0])
        WL_labels_inverse = {dv: idx for (idx, dv) in
                             enumerate(sorted(list(distinct_values)), nl)}

        def generate_graphs(WL_labels_inverse, nl):
            # calculate the kernel matrix for the 0 iteration
            new_graphs = list()
            for j in range(nx):
                new_labels = dict()
                for (k, v) in iteritems(L[j]):
                    if v in self._inv_labels[0]:
                        new_labels[k] = self._inv_labels[0][v]
                    else:
                        new_labels[k] = WL_labels_inverse[v]
                L[j] = new_labels
                # produce the new graphs
                new_graphs.append([Gs_ed[j], new_labels])
            yield new_graphs

            for i in range(1, self._n_iter):
                new_graphs = list()
                L_temp, label_set = dict(), set()
                nl += len(self._inv_labels[i])
                for j in range(nx):
                    # Find unique labels and sort them for both graphs
                    # Keep for each node the temporary
                    L_temp[j] = dict()
                    for v in Gs_ed[j].keys():
                        credential = str(L[j][v]) + "," + \
                            str(sorted([L[j][n] for n in Gs_ed[j][v].keys()]))
                        L_temp[j][v] = credential
                        if credential not in self._inv_labels[i]:
                            label_set.add(credential)

                # Calculate the new label_set
                WL_labels_inverse = dict()
                if len(label_set) > 0:
                    for dv in sorted(list(label_set)):
                        idx = len(WL_labels_inverse) + nl
                        WL_labels_inverse[dv] = idx

                # Recalculate labels
                new_graphs = list()
                for j in range(nx):
                    new_labels = dict()
                    for (k, v) in iteritems(L_temp[j]):
                        if v in self._inv_labels[i]:
                            new_labels[k] = self._inv_labels[i][v]
                        else:
                            new_labels[k] = WL_labels_inverse[v]
                    L[j] = new_labels
                    # Create the new graphs with the new labels.
                    new_graphs.append([Gs_ed[j], new_labels])
                yield new_graphs

        graphs = generate_graphs(WL_labels_inverse, nl)
        values = [self.X[i].transform(g) for (i, g) in enumerate(graphs)]
        K = np.sum(values, axis=0)

        self._is_transformed = True
        if self.normalize:
            X_diag, Y_diag = self.diagonal()
            old_settings = np.seterr(divide='ignore')
            K = np.nan_to_num(np.divide(K, np.sqrt(np.outer(Y_diag, X_diag))))
            np.seterr(**old_settings)

        return K

    def diagonal(self):
        """Calculate the kernel matrix diagonal for fitted data.

        Parameters
        ----------
        None.
        Returns
        -------
        X_diag : np.array
        Y_diag : np.array

        """
        # Check if fit had been called
        check_is_fitted(self, ['X'])
        try:
            check_is_fitted(self, ['_X_diag'])
            if self._is_transformed:
                Y_diag = self.X[0].diagonal()[1]
                for i in range(1, self._n_iter):
                    Y_diag += self.X[i].diagonal()[1]
        except NotFittedError:
            # Calculate diagonal of X
            if self._is_transformed:
                X_diag, Y_diag = self.X[0].diagonal()
                # X_diag is considered a mutable and should not affect the kernel matrix itself.
                X_diag.flags.writeable = True
                for i in range(1, self._n_iter):
                    x, y = self.X[i].diagonal()
                    X_diag += x
                    Y_diag += y
                self._X_diag = X_diag
            else:
                # case sub kernel is only fitted
                X_diag = self.X[0].diagonal()
                # X_diag is considered a mutable and should not affect the kernel matrix itself.
                X_diag.flags.writeable = True
                for i in range(1, self._n_iter):
                    x = self.X[i].diagonal()
                    X_diag += x
                self._X_diag = X_diag

        if self._is_transformed:
            return self._X_diag, Y_diag
        else:
            return self._X_diag


from data_structures.stacks.stack import Stack

bracket_map = {
    '(': ')',
    '{': '}',
    '[': ']'
}

def balanced_parentheses(example):
    is_balanced = True
    stack = Stack()
    for char in example:
        if char in ['(', '{', '[']:
            stack.push(char)
        elif char in [')', '}', ']']:
            # print(stack)
            if stack.is_empty() or bracket_map[stack.pop()] != char:
                is_balanced = False
                break

    return is_balanced and stack.is_empty()

if __name__ == "__main__":
    examples = ["((()))", "((())", "(()))", "()[]{}", "()", "", "(]", "{[]}"]
    print("Balanced parentheses demonstration:\n")
    for example in examples:
        print(example + ": " + str(balanced_parentheses(example)))


from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.dispatcher import FSMContext

class ScriptsInsert(StatesGroup):
    name = State()
    description = State()
    price = State()
    img = State()

class ScriptLeafThrough(StatesGroup):
    throuhgt = State()

class Form(StatesGroup):
    name = State()
    phone = State()

class Cooperation(StatesGroup):
    what = State()
    other = State()
    answer = State()
    phone = State()

class Refactor(StatesGroup):
    refactor = State()
    choose = State()
    name = State()
    description = State()
    price = State()
    img = State()

class AdminAllMess(StatesGroup):
    messAll = State()

class AdminOneMess(StatesGroup):
    call = State()
    messOne = State()

class AdminInsertProjects(StatesGroup):
    name = State()
    img = State()

class CollabWithUser(StatesGroup):
    doc = State()

class AdminJokes(StatesGroup):
    name = State()
    joke = State()

class ChatGPT(StatesGroup):
    mess = State()


#!/usr/bin/env python3
# coding: utf8
# 20170220 anChaOs

# general
import json, time, random
import requests # for http requests
from datetime import datetime, timedelta, date
from fractions import Fraction

# wraps
from functools import wraps

# flask
from flask import Flask, request, session, jsonify, current_app, g
from flask import redirect, url_for, render_template, make_response, render_template_string

# flask-login
from flask_login import login_user, logout_user, current_user
from flask_login import login_required, LoginManager

# db
from app import db

# config
import config as config

# database functions
import app.database.api as dbapi

# error
import app.error as error
ApiError = error.ApiError

# normal functions
from .apis import *
from app.admin.apis import refund_income

import app.tool as tool
from app.tool import dbg                    # for print
from app.tool import print_exception_info   # for traceback

# wechat
import app.third_party.wechatsdk as wechatsdk
import app.third_party.wechatpay as wechatpay

# blueprint
from . import api_blueprint

import os

from flask_expects_json import expects_json
from werkzeug.exceptions import BadRequest
from urllib.parse import quote

from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmp

from app.database.model import Duration, Hall, Light, OrderItem, HallOrder

import binascii

@api_blueprint.errorhandler(ApiError)
def handle_api_error(current_error):
    dbg((current_error.msg, current_error.error_no))
    error_info = error.get_error_info(current_error.error_no)
    return make_response(jsonify({'code':current_error.error_no, 'msg':error_info[0]}), error_info[1])

@api_blueprint.errorhandler(Exception)
def handle_base_exception(current_error):
    print_exception_info()
    db.session.rollback()
    return make_response(json.dumps({'code':99, 'msg':'server error'}), 500)

def check_openid(func):
    @wraps(func)
    def wrapper(*args, **kws):
        if 'has_openid' in session:
            session.pop('has_openid')
        # session['openid'] = 'oEMOtjoiv9HBQiAC7x7BnSDAUuVw'
        user_agent = request.headers.get('User-Agent')
        if 'MicroMessenger' in user_agent:
            if 'openid' not in session:
                raise ApiError('do not login', error.ERROR_NO_LOGIN)
            wechat_user = dbapi.get_wechat_user(openid=session['openid'])
            if not wechat_user:
                raise ApiError('do not login', error.ERROR_NO_LOGIN)
            g.wechat_user = wechat_user
            g.oauth_user = wechat_user
        else:
            if 'ali_user_id' not in session:
                raise ApiError('do not login', error.ERROR_NO_LOGIN)
            ali_user = dbapi.get_ali_user(ali_user_id=session['ali_user_id'])
            if not ali_user:
                raise ApiError('do not login', error.ERROR_NO_LOGIN)
            g.oauth_user = ali_user
            g.wechat_user = None
        return func(*args, **kws)
    return wrapper

@api_blueprint.route('/wallet/fetch', methods=["POST"])
@check_openid
def get_wallet_balance():
    dbg('get_wallet_balance')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)

    user_id = g.oauth_user.user_id
    wallet = dbapi.get_wallet(role=0, user_id=user_id, agent_id=device.owner_agent_id)

    agent_setting = dbapi.get_agent_setting(agent_id=device.agent_id)
    wallet_pay_enable = 1
    if agent_setting and agent_setting.wallet_pay_enable == 0:
        wallet_pay_enable = 0

    reply['data'] = {
        'balance': wallet.balance,
        'wallet_pay_enable': wallet_pay_enable
    }
    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/wechat_info/fetch', methods=["POST"])
@check_openid
def get_wechat_info():
    dbg('get_wechat_info')
    reply ,status_code = {'code': 0, 'msg': ''}, 200
    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    wechat_config   = dbapi.get_wechat_config(imei=imei)
    appid           = wechat_config.appid
    appsecret       = wechat_config.appsecret
    openid          = session['openid']
    qrcode_img      = wechat_config.qrcode_img

    wechat_info     = wechatsdk.get_wechat_user_info(appid, appsecret, openid)

    reply['data']   = {
        'subscribe': wechat_info['subscribe'],
        'qrcode'   : url_for('static', filename=qrcode_img)
    }
    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/title/fetch', methods=["POST"])
def get_wechat_title():
    dbg('get_wechat_title')
    reply ,status_code = {'code': 0, 'msg': ''}, 200
    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    wechat_config   = dbapi.get_wechat_config(imei=imei)
    title = ''
    if wechat_config:
        title = wechat_config.name

    reply['data'] = {
        'title': title
    }
    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/ysq_test', methods=["GET"])
def ysq_test():
    reply ,status_code = {'code': 0, 'msg': ''}, 200
    try:
        ...
        # data = request.get_json()
        # imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)
    reply['data'] = {
        'temples': [ {
                'name': '大雄宝殿',
                'id': 1,
                'lights': [{
                    'name': '健康灯',
                    'id': 1,
                    'price': 0.1,
                }, {
                    'name': '亲情灯',
                    'id': 2,
                    'price': 0.15,
                }, {
                    'name': '爱情灯',
                    'id': 3,
                    'price': 0.22,
                }, {
                    'name': '友情灯',
                    'id': 4,
                    'price': 0.05,
                }, {
                    'name': '事业灯',
                    'id': 5,
                    'price': 0.25,
                }, {
                    'name': '学业灯',
                    'id': 6,
                    'price': 0.12,
                }, {
                    'name': '幸运灯',
                    'id': 7,
                    'price': 0.23,
                }, {
                    'name': '桃花灯',
                    'id': 8,
                    'price': 0.17,
                }, {
                    'name': '随便灯',
                    'id': 9,
                    'price': 0.32,
                }]
            },{
                'name': '大雷音寺',
                'id': 2,
                'lights': [],
            }, {
                'name': '小雷音寺',
                'id': 3,
            }
        ],
        'packages': [{
            'id': 0,
            'name': '日供',
            'rate': 1,
        }, {
            'id': 1,
            'name': '七日',
            'rate': 7,
        }, {
            'id': 2,
            'name': '一月',
            'rate': 30,
        }, {
            'id': 3,
            'name': '七七四十九',
            'rate': 49,
        }]
    }

    resp = make_response(jsonify(reply), status_code)
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

@api_blueprint.route('/device_status', methods=["POST"])
@check_openid
def get_device_status():
    reply ,status_code = {'code': 0, 'msg': ''}, 200
    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    status = dbapi.get_cws_device_status(imei)
    online = -1
    if status:
        online = status.get('online', -1)
    latest_report = dbapi.get_device_latestreport(imei)
    if not latest_report:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)

    # dbg((datetime.now().timestamp(), latest_report['time'].timestamp()))
    if datetime.now().timestamp() - latest_report['time'].timestamp() > 150:
        if online == 1:
            online = 0

    alipay_available = 0
    if check_alipay_available(imei):
        alipay_available = 1

    reply['data'] = {
        'status': online,   # 0离线 1在线 4关机
        'alipay_available': alipay_available,
        'user_id': g.oauth_user.user_id
    }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/wallet_products', methods=["POST"])
def get_wallet_products():
    dbg('get_wallet_products')
    reply ,status_code = {'code': 0, 'msg': ''}, 200
    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('get products error', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)

    agent_id = device.owner_agent_id

    products = dbapi.get_product(agent_id=agent_id, cat=99)
    data = []
    if products:
        for product in products:
            dic = {
                'id'   : product.id,
                'title': product.title,
                'body' : product.body,
                'value': product.value,
                'price': product.price,
            }
            data.append(dic)
    reply['data'] = data

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/products', methods=["POST"])
def get_products():
    dbg('get_products')
    reply ,status_code = {'code': 0, 'msg': ''}, 200
    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('get products error', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)
    device_products = dbapi.get_device_product(device_id=device.id)
    data = []
    if device_products:
        for device_product in device_products:
            product = dbapi.get_product(product_id=device_product.product_id)
            if not product:
                continue
            dic = {
                'id'   : product.id,
                'title': product.title,
                'body' : product.body,
                'value': product.value,
                'price': product.price,
                'unit' : device.product_unit,
                'number': str(Fraction(product.value, device.product_unit_pluse)),
            }
            data.append(dic)
    reply['data'] = data

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/ad/fetch', methods=["POST"])
def get_ads():
    dbg('get_ads')
    reply ,status_code = {'code': 0, 'msg': ''}, 200
    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)
    ads = dbapi.get_ad(agent_id=device.owner_agent_id, using=1)
    data = []
    for ad in ads:
        info = {
            'id': ad.id,
            'name': ad.name,
            'desc': ad.desc,
            'img' : url_for('static', filename=ad.img),
            'url' : ad.url
        }
        data.append(info)

    reply['data'] = data

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/game_coin/fetch')
@check_openid
def fetch_game_coin():
    dbg('fetch_game_coin')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    user_id = g.oauth_user.user_id
    game_coin = dbapi.get_game_coin(user_id=user_id)
    if not game_coin:
        game_coin = dbapi.make_new_game_coin(user_id)
        db.session.commit()
    coin = game_coin.coin

    reply['data'] = {
        'coin': coin
    }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/pay', methods=["PUT"])
@check_openid
def make_pay():
    dbg('make_pay')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        product_id = data['product_id']
        trade_type = data['trade_type']
        pay_mode = data['pay_mode']
        imei       = data['imei']
        if pay_mode == 2:
            cur_url  = data['url']
    except:
        print_exception_info()
        raise ApiError('pay error', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)

    agent_id = device.owner_agent_id

    user_id = g.oauth_user.user_id
    max_id  = dbapi.get_max_pay_id()
    str_max_id = '%04d' % max_id
    if len(str_max_id) > 4:
        str_max_id = str_max_id[-4:]
    trade_no = str(pay_mode) + datetime.now().strftime("%Y%m%d%H%M%S") + str_max_id
    product = dbapi.get_product(product_id=product_id)

    if pay_mode == 1:
        # 微信支付
        data = wechat_make_new_pay(product, trade_no, agent_id, user_id, imei, trade_type)
    elif pay_mode == 2:
        # 支付宝支付
        data = ali_make_new_pay(product, trade_no, agent_id, user_id, imei, cur_url)
    elif pay_mode == 3:
        if product.cat == 99:
            raise ApiError('not support')
        # 钱包支付
        data = wallet_make_new_pay(product, trade_no, agent_id, user_id, imei)
    else:
        raise ApiError('ERROR_PAY', error.ERROR_PAY)

    reply['data'] = data

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/pay/query', methods=["POST"])
@check_openid
def get_pay():
    dbg('get_pay')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        pay_id = data['pay_id']
    except:
        print_exception_info()
        raise ApiError('get pay error', error.ERROR_PARAM)

    pay = dbapi.get_pay(pay_id=pay_id)
    if not pay:
        raise ApiError('get pay error', error.ERROR_PAY_NOT_FOUND)

    data = {
        'trade_no': pay.trade_no,
        'title': pay.title,
        'body': pay.body,
        'total_fee': pay.total_fee,
        'time': int(pay.utime.timestamp()),
        'status': pay.status
    }
    reply['data'] = data

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/machine/restart', methods=['PATCH'])
@check_openid
def machine_restart():
    dbg('machine_restart')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        record_id = data['record_id']

    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    record = dbapi.get_record(record_id=record_id)
    if not record:
        dbg('record_id: %d' % record_id)
        raise ApiError('ERROR_RECORD_NOT_FOUND', error.ERROR_RECORD_NOT_FOUND)

    # pay = dbapi.get_pay(pay_id=record.pay_id)
    # imei = pay.imei

    # delta = record.etime - datetime.now()
    # value = delta.seconds
    # if value > 0:
    #     succ, result = launch_relay_signal_deivce_v2(imei, value)

    reply['data'] = {
            'id': record.id,
            'stime': int(record.stime.timestamp()),
            'etime': int(record.etime.timestamp()),
            'now': int(time.time())
        }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/machine/start', methods=['PUT'])
@check_openid
def machine_start():
    dbg('machine_start')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        cat  = data['cat']
        pay_id = data['pay_id']

    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    pay = dbapi.get_pay(pay_id=pay_id)
    if not pay:
        raise ApiError('machine start error', error.ERROR_PAY_NOT_FOUND)

    imei = pay.imei

    if pay.status != 1:
        raise ApiError('machine start error', error.ERROR_NOT_PAY)

    product = dbapi.get_product(product_id=pay.product_id)
    if not product:
        raise ApiError('can not find product: %s' % pay.product_id)

    if product.cat == 99:
        raise ApiError('ERROR_PAY_NOT_FOUND', error.ERROR_PAY_NOT_FOUND)

    if product.cat == 0:
        # 倒计时限制同时支付
        last_pay = dbapi.get_last_pay(pay.imei, not_include_pay_id=pay_id)
        if last_pay and datetime.now().timestamp() - last_pay.utime.timestamp() < 5:
            # 退款
            pay_refund(pay, product)
            raise ApiError('ERROR_PAY_TOO_FREQUENCE', error.ERROR_PAY_TOO_FREQUENCE)

    dbg('cat: %s' % cat)
    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)

    value = product.value
    record = dbapi.get_record(pay_id=pay.id)
    if record:
        raise ApiError('machine start error', error.ERROR_ALREADY_START)

    high = device.high
    low = device.low
    cat = product.cat
    dbg('imei: %s, value: %d, cat: %d, high: %d, low: %d' % (imei, value, cat, low, high))

    cat = product.cat
    if cat in (0,):
        device_type = device.cat
        succ, result = launch_relay_signal_deivce_v2(imei, value, device_type)
    if cat in (2,):
        print('sending uart message..')
        succ, result = launch_uart_deivce_v2(imei, product.uart_val, product.uart_exp)

    elif cat == 1:
        if imei == '868575023189139' and value == 3:
            succ, result = launch_pulse_signal_deivce(imei, 2, high, low)
            time.sleep(2)
            succ, result = launch_pulse_signal_deivce(imei, 1, high, low)
        else:
            succ, result = launch_pulse_signal_deivce(imei, value, high, low)

    else:
        raise ApiError('not support')

    if not succ:
        # 退款
        pay_refund(pay, product)

        try:
            data = json.loads(result)
            dbg((data['code'], data['msg']))
            reply['code'] = data['code']
            reply['msg']  = data['msg']
            status_code = 422
        except:
            print_exception_info()
            reply['code'] = 99
            reply['msg']  = 'server error'
            status_code = 500
        return make_response(jsonify(reply), status_code)

    if cat == 0:
        # 按摩椅需要记录开始记录
        pay_way = pay.pay_mode
        stime = datetime.now()
        etime = stime + timedelta(seconds=product.value)
        record = dbapi.make_new_record(pay.id, pay_way, pay.user_id,
            product.agent_id, pay.product_id, stime, etime)

    else:
        # 投币器也需要记录开始记录
        pay_way = pay.pay_mode
        stime = datetime.now()
        etime = stime
        record = dbapi.make_new_record(pay.id, pay_way, pay.user_id,
            product.agent_id, pay.product_id, stime, etime)

    db.session.commit()

    reply['data'] = {
            'id': record.id,
            'stime': int(record.stime.timestamp()),
            'etime': int(record.etime.timestamp()),
            'now': int(time.time()),
            'user_id': record.user_id,
        }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/machine/stop', methods=["PATCH"])
@check_openid
def mahcine_stop():
    dbg('machine_stop')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('get machine record error', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)

    record = dbapi.get_record(imei=imei, recent=1)
    if not record:
        raise ApiError('ERROR_RECORD_NOT_FOUND', error.ERROR_RECORD_NOT_FOUND)

    succ, result = launch_relay_signal_deivce_v2(imei, 0, device.cat)
    if not succ:
        try:
            data = json.loads(result)
            dbg((data['code'], data['msg']))
            reply['code'] = data['code']
            reply['msg']  = data['msg']
            status_code = 422
        except:
            print_exception_info()
            reply['code'] = 99
            reply['msg']  = 'server error'
            status_code = 500
        return make_response(jsonify(reply), status_code)

    try:
        record = dbapi.update_record(record, status=1)
        db.session.commit()
    except:
        db.session.rollback()
        raise

    now = int(time.time())
    etime = int(record.etime.timestamp())
    if record.status == 1:
        etime = now
    reply['data'] = {
        'id': record.id,
        'stime': int(record.stime.timestamp()),
        'etime': etime,
        'now': now,
        'user_id': record.user_id,
    }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/nopay/count', methods=["POST"])
@check_openid
def get_nopay_count():
    dbg('get_nopay_count')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        imei  = data['imei']
    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)
    if not device.nopay:
        raise ApiError('ERROR_DEVICE_NOT_SUP_NOPAY', error.ERROR_DEVICE_NOT_SUP_NOPAY)

    user_id = g.oauth_user.user_id

    user_start_counter = dbapi.get_user_start_counter(user_id=user_id, imei=imei)
    if not user_start_counter:
        user_start_counter = dbapi.make_new_user_start_counter(user_id, imei)
        db.session.commit()

    user_start_counters = dbapi.get_user_start_counter(user_id=user_id, address_id=device.address_id)
    count = 0
    for user_start_counter in user_start_counters:
        count += user_start_counter.count

    reply['data'] = {
        'count': count,
        'nopay': device.nopay
    }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/machine/start_nopay', methods=['PUT'])
@check_openid
def machine_start_nopay():
    dbg('machine_start_nopay')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        cat  = data['cat']
        imei  = data['imei']
        value = data['value']

    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)
    if not device.nopay:
        raise ApiError('ERROR_DEVICE_NOT_SUP_NOPAY', error.ERROR_DEVICE_NOT_SUP_NOPAY)

    user_id = g.oauth_user.user_id

    user_start_counter = dbapi.get_user_start_counter(user_id=user_id, imei=imei)
    if not user_start_counter:
        user_start_counter = dbapi.make_new_user_start_counter(user_id, imei)
        db.session.commit()

    user_start_counters = dbapi.get_user_start_counter(user_id=user_id, address_id=device.address_id)
    count = 0
    for counter in user_start_counters:
        count += counter.count

    if count >= device.nopay:
        raise ApiError('ERROR_NOPAY_NOT_ENOUGH', error.ERROR_NOPAY_NOT_ENOUGH)

    agent_id = device.agent_id

    wechat_config   = dbapi.get_wechat_config(imei=imei)
    appid           = wechat_config.appid
    appsecret       = wechat_config.appsecret
    openid          = session['openid']

    wechat_info     = wechatsdk.get_wechat_user_info(appid, appsecret, openid)

    if not wechat_info or wechat_info['subscribe'] != 1:
        raise ApiError('ERROR_WECHAT_NOT_SUBSCRIBE', error.ERROR_WECHAT_NOT_SUBSCRIBE)

    if cat in (0, 2, 3):
        device_type = device.cat
        succ, result = launch_relay_signal_deivce_v2(imei, value, device_type)
        if not succ:
            try:
                data = json.loads(result)
                dbg((data['code'], data['msg']))
                reply['code'] = data['code']
                reply['msg']  = data['msg']
                status_code = 422
            except:
                print_exception_info()
                reply['code'] = 99
                reply['msg']  = 'server error'
                status_code = 500
            return make_response(jsonify(reply), status_code)

        stime = datetime.now()
        etime = stime + timedelta(seconds=value)
        # 1. 记录开始记录
        record = dbapi.make_new_record_no_pay(imei, user_id,
            agent_id, stime, etime)
        db.session.commit()

        reply['data'] = {
            'id': record.id,
            'stime': int(record.stime.timestamp()),
            'etime': int(record.etime.timestamp())
        }

    elif cat == 1:
        # 娃娃机
        value = 1

        succ, result = launch_pulse_signal_deivce(imei, value, device.high, device.low)
        if not succ:
            try:
                data = json.loads(result)
                dbg((data['code'], data['msg']))
                reply['code'] = data['code']
                reply['msg']  = data['msg']
                status_code = 422
            except:
                print_exception_info()
                reply['code'] = 99
                reply['msg']  = 'server error'
                status_code = 500
            return make_response(jsonify(reply), status_code)

        stime = datetime.now()
        etime = stime
        # 1. 记录开始记录
        record = dbapi.make_new_record_no_pay(imei, user_id,
            agent_id, stime, etime)
        db.session.commit()

    # 记录启动次数
    count = user_start_counter.count + 1
    user_start_counter = dbapi.update_user_counter(user_start_counter, count=count)
    db.session.commit()

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/machine/start_coupon_nopay', methods=['PUT'])
@check_openid
def machine_start_coupon_nopay():
    dbg('machine_start_coupon_nopay')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        cat  = data['cat']
        imei  = data['imei']
        value = data['value']

    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)
    if not device.coupon:
        raise ApiError('ERROR_DEVICE_NOT_SUP_NOPAY', error.ERROR_DEVICE_NOT_SUP_NOPAY)

    user_id = g.oauth_user.user_id

    receipt = None
    coupon_receipts = dbapi.get_coupon_receipt(device_id=device.id, user_id=user_id)
    for coupon_receipt in coupon_receipts:
        if coupon_receipt.started == 0:
            receipt = coupon_receipt

    if not receipt:
        raise ApiError('ERROR_NOPAY_NOT_ENOUGH', error.ERROR_NOPAY_NOT_ENOUGH)

    agent_id = device.agent_id

    if cat in (0, 2, 3):
        device_type = device.cat
        succ, result = launch_relay_signal_deivce_v2(imei, value, device_type)
        if not succ:
            try:
                data = json.loads(result)
                dbg((data['code'], data['msg']))
                reply['code'] = data['code']
                reply['msg']  = data['msg']
                status_code = 422
            except:
                print_exception_info()
                reply['code'] = 99
                reply['msg']  = 'server error'
                status_code = 500
            return make_response(jsonify(reply), status_code)

        stime = datetime.now()
        etime = stime + timedelta(seconds=value)
        # 1. 记录开始记录
        record = dbapi.make_new_record_coupon_no_pay(imei, user_id,
            agent_id, stime, etime)
        db.session.commit()

        reply['data'] = {
            'id': record.id,
            'stime': int(record.stime.timestamp()),
            'etime': int(record.etime.timestamp())
        }

    elif cat == 1:
        # 娃娃机
        value = 1

        succ, result = launch_pulse_signal_deivce(imei, value, device.high, device.low)
        if not succ:
            try:
                data = json.loads(result)
                dbg((data['code'], data['msg']))
                reply['code'] = data['code']
                reply['msg']  = data['msg']
                status_code = 422
            except:
                print_exception_info()
                reply['code'] = 99
                reply['msg']  = 'server error'
                status_code = 500
            return make_response(jsonify(reply), status_code)

        stime = datetime.now()
        etime = stime
        # 1. 记录开始记录
        record = dbapi.make_new_record_coupon_no_pay(imei, user_id,
            agent_id, stime, etime)
        db.session.commit()

    # 记录优惠券已启动
    receipt = dbapi.update_coupon_receipt(receipt, started=1)
    db.session.commit()

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/machine/record', methods=["POST"])
def get_machine_record():
    dbg('get_machine_record')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('get machine record error', error.ERROR_PARAM)

    record = dbapi.get_record(imei=imei, recent=1)
    if not record:
        raise ApiError('ERROR_RECORD_NOT_FOUND', error.ERROR_RECORD_NOT_FOUND)

    now = int(time.time())
    etime = int(record.etime.timestamp())
    if record.status == 1:
        etime = now
    reply['data'] = {
        'id': record.id,
        'stime': int(record.stime.timestamp()),
        'etime': etime,
        'now': now,
        'user_id': record.user_id,
    }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/machine/record_nopay', methods=["POST"])
def get_machine_record_nopay():
    dbg('get_machine_record_nopay')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        record_id = data['record_id']
    except:
        print_exception_info()
        raise ApiError('get machine record error', error.ERROR_PARAM)

    dbg((record_id, type(record_id)))
    record = dbapi.get_record_no_pay(id=record_id)
    if not record:
        raise ApiError('ERROR_RECORD_NOT_FOUND', error.ERROR_RECORD_NOT_FOUND)

    reply['data'] = {
        'id': record.id,
        'stime': int(record.stime.timestamp()),
        'etime': int(record.etime.timestamp())
    }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/machine/record_coupon_nopay', methods=["POST"])
def get_machine_record_coupon_nopay():
    dbg('get_machine_record_coupon_nopay')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        record_id = data['record_id']
    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    dbg((record_id, type(record_id)))
    record = dbapi.get_record_coupon_no_pay(id=record_id)
    if not record:
        raise ApiError('ERROR_RECORD_NOT_FOUND', error.ERROR_RECORD_NOT_FOUND)

    reply['data'] = {
        'id': record.id,
        'stime': int(record.stime.timestamp()),
        'etime': int(record.etime.timestamp())
    }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/coupon_receipt/random', methods=["POST"])
@check_openid
def random_get_coupon_receipt():
    dbg('random_get_coupon_receipt')
    reply ,status_code = {'code': 0, 'msg': ''}, 200

    try:
        data = request.get_json()
        imei = data['imei']
    except:
        print_exception_info()
        raise ApiError('get machine record error', error.ERROR_PARAM)

    device = dbapi.get_device(imei=imei)
    if not device:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)

    coupons = dbapi.get_coupon(device_id=device.id, paid=1, inventory=1)
    if coupons:
        coupon = random.choice(coupons)
        coupon_id = coupon.id
        code = gen_coupon_receipt_code(coupon_id, coupon.prefix)
        user_id = g.oauth_user.user_id
        coupon_receipt = dbapi.make_new_coupon_receipt(coupon_id, user_id, code)
        product = dbapi.get_product(product_id=coupon.product_id)
        if product.inventory <= 0:
            raise ApiError('ERROR_COUPON_NO_AVAILABLE', error.ERROR_COUPON_NO_AVAILABLE)
        product = dbapi.update_product(product, inventory=product.inventory-1)
        color   = get_boxed_reverse_color(coupon.img)
        reply['data'] = {
            'img': url_for('static', filename=coupon.img),
            'code': code,
            'color': color
        }
    else:
        raise ApiError('ERROR_COUPON_NO_AVAILABLE', error.ERROR_COUPON_NO_AVAILABLE)

    db.session.commit()

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/pay/notify/', methods=['POST'])
def pay_notify():
    dbg('pay_notify')
    reply = {"return_code": 'SUCCESS', "return_msg": ""}

    try:
        data_info = request.data.decode('utf8')
        data = wechatpay.xml_to_dict(data_info)
        nofity_res = json.dumps(data)
        dbg('data_info: %s' % data_info)

        return_code = None
        result_code = None

        if "result_code" in data and "return_code" in data:
            return_code = data["return_code"]
            result_code = data["result_code"]

        if return_code != "SUCCESS":
            raise ApiError('wechat interface return error')

        # return_code为`SUCCESS`, 则包含out_trade_no

        # 检查订单
        trade_no = data['out_trade_no']
        pay  = dbapi.get_pay(trade_no=trade_no)
        if not pay:
            raise ApiError('can not find pay: %s' % trade_no)

        # 如果已经支付，则直接返回Ok
        if pay.status > 0:
            return make_response(wechatpay.dict_to_xml(reply).encode('utf-8'))

        # 检查签名
        sign = data['sign']
        data.pop('sign')
        wechat_config = dbapi.get_wechat_config(agent_id=pay.agent_id)
        key = wechat_config.mchkey
        local_sign = wechatpay.params_sign(data, key)
        data['sign'] = sign
        dbg((sign, local_sign))
        if sign != local_sign:
            raise ApiError('sign error')

        if result_code != "SUCCESS":
            '''
                ``` 支付失败, 更新支付异常
            '''
            dbapi.update_pay(pay, status=2, nofity_res=nofity_res)

            try:
                db.session.commit()
            except:
                print_exception_info()
                db.session.rollback()
                raise ApiError('pay_notify error')

        else:
            '''
                ``` 支付成功, 并更新支付成功
            '''
            cash_fee = data['cash_fee']
            dbapi.update_pay(pay=pay, status=1, cash_fee=cash_fee, nofity_res=nofity_res)
            product = dbapi.get_product(product_id=pay.product_id)
            if product.cat == 99:
                # 钱包充值
                user_id = pay.user_id
                wallet = dbapi.get_wallet(role=0, user_id=user_id, agent_id=pay.agent_id)
                wallet_id = wallet.id
                trade_type = dbapi.WALLET_TRADE_TYPE_DEPOSIT
                receipt = product.value
                withdrawable_receipt = 0
                remark = product.title
                wallet_receipt = dbapi.make_new_wallet_receipt(user_id, pay.agent_id, wallet_id,
                                                               trade_type, receipt,
                                                               withdrawable_receipt, remark)

                balance = wallet.balance + receipt
                wallet = dbapi.update_wallet(wallet, balance=balance)

            if product.cat == dbapi.PRODUCT_CAT_COUPON:
                # 优惠券
                coupon = dbapi.get_coupon(pay_id=pay.id)
                if coupon:
                    coupon = dbapi.update_coupon(coupon, paid=1)

            wechat_pay_receipt(pay)

            try:
                db.session.commit()
            except:
                print_exception_info()
                db.session.rollback()
                raise ApiError('wechat_pay_notify error', error.ERROR_DATABASE_COMMIT)

    except ApiError as e:
        # if e.msg != 'wechat pay failed':
        print_exception_info()
        reply['return_code'] = "FAIL"
        reply['return_msg']  = e.msg
    except:
        print_exception_info()
        reply['return_code'] = "FAIL"
        reply['return_msg']  = 'server error'

    res_data = wechatpay.dict_to_xml(reply).encode('utf-8')
    print(res_data)
    return make_response(res_data)

@api_blueprint.route('/ali_notify', methods=["GET", 'POST'])
def ali_notify():
    dbg('ali_notify')

    reply = {"return_code": 'SUCCESS', "return_msg": ""}

    try:
        trade_no = request.form['out_trade_no']
        nofity_res = {}
        for k in request.form:
            print(k, request.form[k])
            nofity_res[k] = request.form[k]
        trade_status = request.form['trade_status']
        ali_trade_no = request.form['trade_no']
        buyer_id = request.form['buyer_id']
        buyer_logon_id = request.form['buyer_logon_id']
        cash_fee = request.form.get('buyer_pay_amount') or request.form['total_amount']
    except:
        print_exception_info()
        raise ApiError('ERROR_PARAM', error.ERROR_PARAM)

    # 检查订单
    pay = dbapi.get_pay(trade_no=trade_no)
    if not pay:
        raise ApiError('can not find pay: %s' % trade_no)

    # 签名
    # sign = request.form['sign']
    # ali_config = dbapi.get_ali_config(agent_id=int(agent_id))
    # key_path   = ali_config.pub_path

    # 如果已经支付，则直接返回Ok
    if pay.status > 0:
        return make_response('success')

    if trade_status == 'TRADE_CLOSED':
        dbapi.update_pay(pay, user_id=pay.user_id, status=2, nofity_res=json.dumps(nofity_res))
        try:
            db.session.commit()
        except:
            print_exception_info()
            db.session.rollback()
            raise ApiError('ali_notify error')

    elif trade_status == 'TRADE_SUCCESS':
        dbapi.update_pay(pay=pay, user_id=pay.user_id, status=1, cash_fee=cash_fee, nofity_res=json.dumps(nofity_res), ali_trade_no=ali_trade_no)
        product = dbapi.get_product(product_id=pay.product_id)
        if product.cat == 99:
            # 钱包充值
            user_id = pay.user_id
            wallet = dbapi.get_wallet(role=0, user_id=user_id, agent_id=pay.agent_id)
            wallet_id = wallet.id
            trade_type = dbapi.WALLET_TRADE_TYPE_DEPOSIT
            receipt = product.value
            withdrawable_receipt = 0
            remark = product.title
            wallet_receipt = dbapi.make_new_wallet_receipt(user_id, pay.agent_id, wallet_id,
                                                           trade_type, receipt,
                                                           withdrawable_receipt, remark)

            balance = wallet.balance + receipt
            wallet = dbapi.update_wallet(wallet, balance=balance)

        wechat_pay_receipt(pay)

        try:
            db.session.commit()
        except:
            print_exception_info()
            db.session.rollback()
            raise ApiError('ali_notify error', error.ERROR_DATABASE_COMMIT)

    return make_response('success')

# @api_blueprint.route('/test_refund', methods=["POST"])
# def test_refund():
#     dbg('test_refund')
#     reply ,status_code = {'code': 0, 'msg': ''}, 200

#     data = request.get_json()
#     pay_id = data['pay_id']

#     pay = dbapi.get_pay(pay_id=pay_id)
#     product = dbapi.get_product(product_id=pay.product_id)

#     # 按摩椅退款
#     appid = config.WECHAT_LIST[product.agent_id].APP_ID
#     mchid = config.WECHAT_LIST[product.agent_id].WECHAT_MCH_ID

#     max_id  = dbapi.get_max_refund_id()
#     str_max_id = '%04d' % max_id
#     if len(str_max_id) > 4:
#         str_max_id = str_max_id[-4:]
#     refund_no = 'R' + str(int(time.time())) + str_max_id

#     refund = dbapi.make_new_refund(pay.user_id, pay.id, refund_no,
#         pay.total_fee, pay.total_fee)
#     db.session.commit()
#     if refund.id:
#         kwargs = {
#             'appid': appid,
#             'mchid': mchid,
#             'trade_no': pay.trade_no,
#             'refund_no': refund_no,
#             'total_fee': pay.total_fee,
#             'refund_fee': pay.total_fee,
#             'agent_id': product.agent_id
#         }
#         err, res = wechatpay.refund_request(**kwargs)
#         if not err:
#             refund = dbapi.update_refund(refund, nofity_res=res, status=1)
#         else:
#             dbg(err)
#             refund = dbapi.update_refund(refund, nofity_res=res, status=2)
#         db.session.commit()
    # return make_response(jsonify(reply), status_code)

# @api_blueprint.route('/test_pay_to_user')
# def test_refund():
#     dbg('test_refund')
#     reply ,status_code = {'code': 0, 'msg': ''}, 200

#     kwargs = {
#         'openid': 'oEMOtjoiv9HBQiAC7x7BnSDAUuVw',
#         'trade_no': 'paytest123',
#         'money': 100,
#         'desc': '取现',
#         'agent_id': 1
#     }
#     err, result = wechatpay.pay_to_user(**kwargs)
#     print(err)
#     print(result)

#     return make_response(jsonify(reply), status_code)

# @api_blueprint.route('/test_wechat_pay')
# def test_pay():
#     dbg('test_pay')
#     reply ,status_code = {'code': 0, 'msg': ''}, 200

#     params = {
#         'body': 'body',
#         'out_trade_no': 'test%d' % int(time.time()),
#         'total_fee': 1,
#         'spbill_create_ip': current_app.config['SELF_IP'],
#         'notify_url': 'http://wxapp.mafu.shop/api/test_wechat_notify',
#         'trade_type': "NATIVE",
#         'agent_id': "1"
#     }
#     err, payRequest = wechatpay.get_native_pay_request(**params)
#     if not err:
#         reply['data'] = payRequest
#     else:
#         reply['data'] = err

#     return make_response(jsonify(reply), status_code)

# @api_blueprint.route('/test_wechat_notify', methods=['POST'])
# def test_wechat_notify():
#     dbg('test_wechat_notify')
#     reply = {"return_code": 'SUCCESS', "return_msg": ""}

#     data_info = request.data.decode('utf8')
#     data = wechatpay.xml_to_dict(data_info)
#     nofity_res = json.dumps(data)
#     dbg('data_info: %s' % data_info)

#     res_data = wechatpay.dict_to_xml(reply).encode('utf-8')
#     print(res_data)
#     return make_response(res_data)

# @api_blueprint.route('/test_recalc_income')
# def test_recalc_income():
#     dbg('test_recalc_income')
#     reply ,status_code = {'code': 0, 'msg': ''}, 200

#     pays = dbapi.db.session.query(dbapi.Pay).filter(dbapi.Pay.status==1)

#     for pay in pays:
#         wechat_pay_receipt(pay)
#     db.session.commit()

#     return make_response(jsonify(reply), status_code)

# @api_blueprint.route('/test_init_device_distribution')
# def test_init_device_distribution():
#     dbg('test_init_device_distribution')
#     reply ,status_code = {'code': 0, 'msg': ''}, 200

#     query = dbapi.db.session.query(dbapi.Device)
#     count = query.count()
#     devices = query.all()
#     print(count)

#     for device in devices:
#         dbapi.make_new_device_distribution(device.id, device.imei,
#             0, device.agent_id)
#         if device.agent_id != device.owner_agent_id:
#             dbapi.make_new_device_distribution(device.id, device.imei,
#                 device.agent_id, device.owner_agent_id)
#     dbapi.db.session.commit()

#     return make_response(jsonify(reply), status_code)

@api_blueprint.errorhandler(BadRequest)
def handle_bad_request(err):
    return err.get_response()

def openid_required(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            g.openid = request.headers['authorization']
            wechat_user = dbapi.get_wechat_user(openid=g.openid)
            if not wechat_user:
                raise ApiError('do not login', error.ERROR_NO_LOGIN)
            g.wechat_user = wechat_user
            g.oauth_user = wechat_user
        except:
            raise ApiError('ERROR_NO_LOGIN', error.ERROR_NO_LOGIN)
        return func(*args, **kwargs)
    return wrapper

global_defs = {
    'definitions': {
        'imei': {
            'type': 'integer',
            'minimum': 100000000000000,
            'maximum': 999999999999999,
        },
        'url': {
            'pattern': r'^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$',
        }
    }
}

def cors(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        resp = func(*args, **kwargs)
        resp.headers['Access-Control-Allow-Origin'] = '*'
        resp.headers['Access-Control-Allow-Methods'] = 'HEAD, OPTIONS, GET, POST, DELETE, PUT'
        resp.headers['Access-Control-Allow-Headers'] = 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild'
        return resp
    return wrapper

@api_blueprint.route('/v2/user', methods=["OPTIONS"])
@cors
def v2_user_options():
    return make_response(jsonify({'Allow': '*'}), 200)

@api_blueprint.route('/v2/user' ,methods=["POST"])
@expects_json({
    'type': 'object',
    'properties': {
        'type': {'enum': ['code', 'openid']},
        'imei': {'$ref': '#/definitions/imei'},
    },
    'required': ['imei', 'type'],
    'anyOf': [
        {
            'properties': {
                'type': {'enum': ['code']},
                'next_url': {'$ref': '#/definitions/url'},
            },
            'required': ['next_url'],
            'maxProperties': 3,
        },
        {
            'properties': {
                'type': {'enum': ['openid']},
                'code': {'type': 'string'},
            },
            'required': ['code'],
            'maxProperties': 3,
        },
    ],
    **global_defs,
}, force=True)
@cors
def v2_user():
    # 前端判断jwt里是否有openid
    # 0111WHv92Ld0mM0nyQv92dOEv921WHvR
    reply ,status_code = {'code': 0}, 200
    device = dbapi.get_device(imei=g.data['imei'])
    wechat_config = dbapi.get_wechat_config(agent_id=device.agent_id)

    if g.data['type'] == 'code':
        reply['data'] = {
            'url': wechatsdk.gen_auth_url(wechat_config.appid, quote(g.data['next_url']), '')
        }
    elif g.data['type'] == 'openid':

        print('openid')

        succ, res = wechatsdk.get_auth_user_info(
            wechat_config.appid,
            wechat_config.appsecret,
            g.data['code']
        )
        # {'openid': 'ogy6jxJ_eKR7zJfockfTC1-ZLgwk', 'nickname': 'Tapio', 'sex': 1, 'language': 'zh_CN', 'city': '', 'province': '', 'country': '安道尔', 'headimgurl': 'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTIMibbUV6ehdupkKlkczjXFD1NclAfmlXed0BroOibeXkmeAZZhWmm8sgXQja0x1Nb0WibqdgCQVQzQw/132', 'privilege': []}
        if not succ:
            ApiError('ERROR_USER_CANNOT_LOGIN', error.ERROR_USER_CANNOT_LOGIN)

        print(res)

        wechat_user = dbapi.get_wechat_user(openid=res['openid'])
        if not wechat_user:
            user = dbapi.make_new_user(wechat_body=res)
            db.session.commit()

            user_info['user_id'] = user.id
            user_info['admin_uid'] = 0
            wechat_user = dbapi.make_new_wechat_user(res)
            db.session.commit()
        else:
            user = dbapi.get_user(user_id=wechat_user.user_id)
            if user.nickname == "":
                wechat_user = dbapi.update_wechat_user(wechat_user, wechat_body=user_info)
                user = dbapi.update_user(user, wechat_body=user_info)
                db.session.commit()

        reply['data'] = {
            'openid': res['openid'],

        }

    else:
        assert(False)

    #方案1: 直接url填写后端url，后端获取到openid再302跳转到前端

    #方案2: 直接url填写前端url，前端再次调用后端api，后端返回openid
    return make_response(jsonify(reply), status_code)

# def json_to_g(func):
#     @wraps(func)
#     def wrapper(*arg, **kwargs):
#         g.data = jsonify()
#         func(*arg, **kwargs)
#     return wrapper

@api_blueprint.route('/v2/temple', methods=["OPTIONS"])
@cors
def v2_temple_options():
    return make_response(jsonify({'Allow': '*'}), 200)

@api_blueprint.route('/v2/temple',  methods=["POST"])
@expects_json({
    'type': 'object',
    'properties': {
        'imei': {'$ref': '#/definitions/imei'}
    },
    'required': ['imei'],
    **global_defs,
}, force=True)
@cors
def v2_temple():
    print('GET temple')
    reply ,status_code = {'code': 0}, 200
    device = dbapi.get_device(imei=g.data['imei'])
    temple = device.hall.temple

    reply['data'] = {
        'name': temple.name,
        'img': os.path.join(config.V2_IMG_URL, temple.image),
        'halls': [{
            'id': hall.id,
            'name': hall.name,
            'lights': [{
                'id': light.id,
                'name': light.name,
                'price': light.price,
                'img': os.path.join(config.V2_IMG_URL, light.image),
            } for light in hall.lights],
            'durations': [{
                'id': duration.id,
                'name': duration.name,
                'rate': duration.duration,
            } for duration in hall.durations],
            **({'current': True} if device.hall == hall else {}),
        } for hall in temple.halls],
        # 'recents': [{
        #     'customer': order.customer,
        #     'lights': [item.light.name for item in order.items],
        #     **({'hall': order.items[0].light.hall.name} if len(order.items) > 0 else {}),
        # } for order in temple.orders[:5]]
    }

    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/v2/order', methods=["OPTIONS"])
@cors
def v2_order_options():
    return make_response(jsonify({'Allow': '*'}), 200)

@api_blueprint.route('/v2/order',  methods=["POST"])
@cors
@expects_json({
    'type': 'object',
    'properties': {
        'customer': {
            'type': 'object',
            'properties': {
                'name': {
                    'type': 'string',
                    'minLength': 1,
                    'maxLength': 64,
                },
                'sex': {
                    'type': 'string',
                    'enum': ['F', 'M'],
                },
                'desire': {
                    'type': 'string',
                    'maxLength': 255,
                },
                'phone': {
                    'type': ['string', 'null'],
                    'pattern': r'^1[3-9]\d{9}$',
                    'default': None,
                }
            },
            'required': ['name', 'sex', 'desire'],
        },
        'hall': {
            'type': 'object',
            'properties': {
                'id': {'type': 'integer'},
            },
            'required': ['id'],
        },
        'duration': {
            'type': 'object',
            'properties': {
                'id': {'type': 'integer'},
            },
            'required': ['id'],
        },
        'lights': {
            'type': 'array',
            'items': {
                'type': 'object',
                'properties': {
                    'id': {'type': 'integer'},
                    'num': {
                        'type': 'integer',
                        'minimum': 1,
                        'default': 1,
                    },
                },
                'required': ['id'],
            },
            'minItems': 1,
        },
    },
    'required': ['customer', 'duration', 'lights'],
    **global_defs,
}, fill_defaults=True, force=True)
@openid_required
def v2_order():
    dbg('POST order')
    reply ,status_code = {'code': 0}, 200

    hall = Hall.query.get(g.data['hall']['id'])
    duration = Duration.query.get(g.data['duration']['id'])
    if duration not in hall.durations:
        print('duration error')
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)

    lights = [{
        'num': light['num'],
        'obj': Light.query.get(light['id']),
    } for light in g.data['lights']]
    if(any([light['obj'] not in hall.lights for light in lights])):
        print('light error')
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)

    total = duration.duration * sum([light['obj'].price * light['num'] for light in lights])
    print('total price:', total)

    device = hall.device
    print('openid', g.oauth_user.openid)
    data = wechat_make_new_pay(
        device.link.product,
        f'1{datetime.now().strftime("%Y%m%d%H%M%S")}{f"{dbapi.get_max_pay_id()}"[-4:]}',
        device.agent_id,
        g.oauth_user.user_id,
        device.imei,
        'JSAPI',
        openid=g.oauth_user.openid,
        price=total * 100
    )

    customer = g.data['customer']
    order = HallOrder(
        customer=customer['name'],
        sex=customer['sex'],
        telephone=customer['phone'],
        desire=customer['desire'],
        duration_id=g.data['duration']['id'],
        pay_id=data['id']
    )
    for item in g.data['lights']:
        order.items.append(
            OrderItem(
                num=item['num'],
                light_id=item['id'],
            )
        )
    hall.orders.append(order)
    db.session.commit()

    data['id'] = order.id

    # 前端 -> /v2/api/pay
    # 后端 <- pay_request
    # 前端 -> WeixinJSBridge.invoke('getBrandWCPayRequest')

    # 创建订单 -> [查询订单是否成功] -> 订单完成后启动设备

    # data.data.id => TODO
    ## 会在localstorg里放入pay_id
    ## 通过/api/pay/query 查询支付结果
    ## 如果确定支付成功则调用启动设备(启动设备也需要pay_id作为参数)

    reply['data'] = data
    return make_response(jsonify(reply), status_code)

@api_blueprint.route('/v2/service', methods=["OPTIONS"])
@cors
def v2_service_options():
    return make_response(jsonify({'Allow': '*'}), 200)

@api_blueprint.route('/v2/service',  methods=["POST"])
@expects_json({
    'type': 'object',
    'properties': {
        'order_id': {'type': 'integer'},
    },
    'required': ['order_id'],
}, force=True)
@openid_required
@cors
def v2_service():
    dbg('POST service')
    order = HallOrder.query.get(g.data['order_id'])
    if not order:
        raise ApiError('ERROR_DEVICE_NOT_FOUND', error.ERROR_DEVICE_NOT_FOUND)
    device = order.hall.device
    # TODO: 根据订单信息替换模板字符串

    rendered = render_template_string(device.v2.template, **{
        'customer': order.customer,
        'desire': order.desire,
        'totalLightsNum': sum([item.num for item in order.items]),
        'duration': int(order.duration.duration),
        'newline': '\r\n'
    }).replace(r'\r', '\r').replace(r'\n', '\n')

    target = binascii.hexlify(rendered.encode('gb2312')).decode('utf-8')
    exp_resp = binascii.hexlify(device.v2.exp_resp.encode('gb2312')).decode('utf-8')
    print('rendered', rendered)
    print('target', target)

    succ, result = launch_uart_deivce_v2(device.imei, target, exp_resp)
    if not succ:
        #TODO 退款
        ...

    return jsonify(result)


# 1236. Web Crawler
class Solution:
    def crawl(self, startUrl: str, htmlParser: 'HtmlParser') -> List[str]:
        #find host
        idx = startUrl[7:].find("/")
        hostname = 'http://' + startUrl[7:idx]

        stack=[startUrl]
        result=set([startUrl]) #set is faster

        while stack:
            link = stack.pop()
            urls = htmlParser.getUrls(link)
            for url in urls:
                if url.find(hostname)!=-1 and url not in result:
                    stack.append(url)
                    result.add(url)
        return result


from django.urls import path

from . import views

urlpatterns = [
    path('', views.login_view, name='login'),
    path('', views.logout_view, name='logout'),

    # INDEX PAGE
    path('index/', views.index, name='stu'),

    # STAFF CRUD
    path('staff/staff', views.staff, name='staff'),
    path('staff/staff-update/<id>', views.staff, name='update_staff'),
    path('staff/staff-delete/<id>', views.staff_delete, name='delete_staff'),
    path('staff/staff-list', views.staff_list, name='staff_list'),

    path('register', views.register, name='register'),

    path('patient/patient', views.patient, name='patient'),
    path('patient/patient-update/<id>', views.patient, name='patient_update'),
    path('patient/delete-patient/<id>', views.delete_patient, name='patient_delete'),
    path('shared/patient-list', views.patient_list, name='patient_list'),

    # DEPARTMENT CRUD
    path('shared/department', views.department, name='department'),
    path('shared/dept-update/<id>', views.department, name='dept-update'),
    path('shared/dept-delete/<id>', views.delete_department, name='dept-delete'),
    path('shared/department-list', views.department_list, name='department_list'),

    # ROLE CRUD
    path('shared/role', views.role, name='role'),
    path('shared/role-update/<id>', views.role, name='role-update'),
    path('shared/role-delete/<id>', views.delete_role, name='role-delete'),
    path('shared/role-list', views.role_list, name='role_list'),

    # INVENTORY CRUD
    path('druginventory/druginventory', views.drug_inventory, name='druginventory'),
    path('druginventory/update/<id>', views.drug_inventory, name='update'),
    path('druginventory/delete/<id>', views.drug_inventory_delete, name='delete'),
    path('druginventory/druginventory-list', views.drug_inventory_list, name='druginventory_list'),

    # BILLING
    path('billing/billing', views.billing, name='billing'),
    path('billing/edit-bill/<id>', views.billing, name='update_bills'),
    path('billing/delete-bill/<id>', views.delete_bills, name='delete_bills'),
    path('billing/list', views.billing_list, name='billing_list'),

    # LAB
    path('lab/lab', views.lab, name='lab'),
    path('lab/edit-lab/<id>', views.lab, name='update_lab'),
    path('lab/delete-lab/<id>', views.delete_bills, name='delete_lab'),
    path('lab/list', views.lab_list, name='lab_list'),
]


import numpy as np
import pandas as pd
import pyro
import pyro.util
from pyro.infer import SVI, Trace_ELBO
from pyro.infer.autoguide import AutoNormal
from pyro.optim import Adam
import scipy.stats
from sklearn.model_selection import KFold
import sys
import torch
import tqdm

import expt
import cross_val
import helpers
import model_helpers as modeling

# This script applies BMT model to the PDO-PDX dataset.

# Values for parameter k in PDO-PDX setting
K_LIST = [1, 2, 3, 4, 5, 6, 7, 8, 9]
N_MODELS = 5
N_SPLITS = 5

# Save parameters to file.
def save_params(method, data_fn, write_dir, fold_fn, split_seed, n_steps):
    params = {}
    params['method'] = method
    params['data_fn'] = data_fn
    params['write_dir'] = write_dir
    params['fold_fn'] = fold_fn
    params['split_seed'] = split_seed
    params['n_steps'] = n_steps
    helpers.write_pickle(params, write_dir + '/params.pkl')

# Check validity of parameters passed in.
def check_params(method, data_fn, write_dir, fold_fn, split_seed, n_steps):
    assert method in ['raw', 'transfer']
    assert fold_fn != ""
    assert split_seed in range(0, 18)
    assert n_steps >= 5

# Read in parameters.
def get_args(args, n):
    if len(args) != n + 1:
        print('Error! Expected ' + str(n + 1) + ' arguments but got ' + str(len(args)))
        return
    method = args[1].split("=")[1]
    data_fn = args[2].split("=")[1]
    write_dir = args[3].split("=")[1]
    fold_fn = args[4].split("=")[1]
    split_seed = int(args[5].split("=")[1])
    n_steps = int(args[6].split("=")[1])
    # verify that params are valid for method given and save
    check_params(method, data_fn, write_dir, fold_fn, split_seed, n_steps)
    save_params(method, data_fn, write_dir, fold_fn, split_seed, n_steps)
    return method, data_fn, write_dir, fold_fn, split_seed, n_steps

# Compute spearman correlation between predictions and dataframe column.
def spearman_corr(predictions, df, col):
    test = df[col].to_numpy()
    assert len(predictions) == len(test)
    res = scipy.stats.spearmanr(test, predictions)
    return res.correlation

# Main function for running the PDO-PDX experiment.
def main():
    source_col = 'log10_ic50_(uM)'
    target_col = 'T_C'
    split_type = 'sample_split'
    holdout_frac = -1
    method, data_fn, write_dir, fold_fn, split_seed, n_steps = get_args(sys.argv, 6)
    # Split target dataset into train and test, get number of samples and drugs in dataset
    target_train_df, target_test_df, n_samp, n_drug = helpers.get_target(data_fn, fold_fn, target_col, split_seed, holdout_frac, split_type)
    # Make predictions by method.
    assert method in ['raw', 'transfer']
    if method == 'raw':
        target_train_sd = helpers.get_sample_drug_ids(target_train_df)
        target_test_sd = helpers.get_sample_drug_ids(target_test_df)
        source_df = helpers.get_source(data_fn, source_col)
        train_predictions, test_predictions = expt.predict_raw(source_df, source_col, target_train_sd, target_test_sd)
        train_corr = expt.evaluate_correlation(train_predictions, target_train_df, target_col)
        test_corr = expt.evaluate_correlation(test_predictions, target_test_df, target_col)
    else:
        s_idx_test, d_idx_test = helpers.get_sample_drug_indices(target_test_df)
        source_df = helpers.get_source(data_fn, source_col)
        # Choose k
        k = expt.choose_k('transfer', target_train_df, target_col, split_type, n_samp, n_drug, n_steps, source_df, source_col)
        helpers.write_pickle(k, write_dir + '/k.pkl')
        # With k fixed, fit model.
        train_predict_list, test_predict_list = expt.predict_transfer_wrapper(source_df, source_col, target_train_df, target_col, s_idx_test, d_idx_test, n_samp,
            n_drug, n_steps, k)
        train_corr, test_corr, train_predictions, test_predictions = expt.evaluate(train_predict_list, test_predict_list, target_train_df, target_test_df, target_col)
    # Evaluate predictions and save.
    helpers.write_pickle(train_corr, write_dir + '/train.pkl')
    helpers.write_pickle(test_corr, write_dir + '/test.pkl')
    expt.save_predictions(write_dir + '/train_predictions.pkl', train_predictions, target_train_df)
    expt.save_predictions(write_dir + '/test_predictions.pkl', test_predictions, target_test_df)
    # Save spearman correlation
    train_spear = spearman_corr(train_predictions, target_train_df, target_col)
    test_spear = spearman_corr(test_predictions, target_test_df, target_col)
    helpers.write_pickle(train_spear, write_dir + '/train_spearman.pkl')
    helpers.write_pickle(test_spear, write_dir + '/test_spearman.pkl')

    print('train_corr: ' + str(train_corr))
    print('test_corr: ' + str(test_corr))

if __name__ == "__main__":
    main()


# -*- coding: utf-8 -*-
"""
Takes a 2-dimensional matrix of size [m, n] and returns m vectors of length n.
"""

from UserScript import UserOperation, ScriptIOData
import numpy as np

class ds_user_script(UserOperation):
    """Creates a user script of the operation type."""

    name = 'Slice Rows'
    tooltip = 'Slices a 2D matrix and returns the rows'
    nDimension = 2
    nDataSets = 1

    def operation(self, DataOut, DataIn, Meta):
        """The generic 'main' function of an operation type user script."""
        # self.DataIn is a list of ScriptIOData types. We want the first (or
        # slice [0]) of this list.
        dataInputObject = DataIn[0]
        # The first slice of this list contains a matrix attribute, that
        # should be a numpy array.
        dataInputArray = dataInputObject.matrix
        if not isinstance(dataInputArray, np.ndarray):
            raise TypeError('Is not an array!')
        else:
            rows, cols = dataInputArray.shape
            for row in range(rows):
                Meta['Progress'] = round((row/rows)*100)
                dataOutputObject = ScriptIOData()
                dataOutputObject.matrix = dataInputArray[row]
                DataOut.append(dataOutputObject)


from github import Github
import requests
import base64
import yaml
from pymongo import MongoClient

db = MongoClient().pypo

search_text = input('What would you like to search for?')

def already_exists(repository):
    return db.pypo.find({'repo_name': '{}'.format(repository)}).count() > 0

if search_text != '':

    with open("settings.yaml", 'r') as ymlfile:
        cfg = yaml.load(ymlfile)

    g = Github(cfg['user'], cfg['password'], timeout=200, per_page=30)

    results = g.search_repositories('"{}+ "+in:readme'.format(search_text))

    print('there are %d total results' % results.totalCount)

    string_match = '{}'.format(search_text)

    regex_string = '^.*%s\s.*$' % (string_match)

    for repo in results:

        if not(already_exists(repo.full_name)):

            print('{} being added...'.format(repo.full_name))

            r = requests.get(repo.html_url)
            if r.headers.get('status') is not int(404):
                print('url is ', repo.html_url)

                readme = repo.get_readme()
                data = base64.b64decode(readme.content)

                result = db.pypo.insert_one({
                    "url": repo.html_url,
                    "repo_id": repo.id,
                    "repo_name": repo.full_name,
                    "pull_request_submitted": False,
                    "pull_request_merged": False,
                    "pull_request_number": 0,
                    "forks_deleted": False,
                    "search_text": search_text,
                    "fullText": data
                })
                print("Saved [{}].".format(result.inserted_id))
                print('\n\n\n\n')

        else:
            print('already found {}, skipping...'.format(repo.full_name))

    print('\n\n\nall done!')

else:
    print('alright, nevermind then...')


import matplotlib.pyplot as plt
from nn_alg import *

fname_train = 'heart_train.arff.txt'
fname_test  = 'heart_test.arff.txt'
# fname_train = 'lymph_train.arff.txt'
# fname_test  = 'lymph_test.arff.txt'

LRATE = .1
N_HIDDEN = 20
NUM_EPOCHS = [1, 10, 100, 500]
SAMPLE_SIZE = 20

# load data
# X_train, Y_train = loadData(fname_train)
# X_test, Y_test   = loadData(fname_test)
X_train, Y_train, feature_mean, feature_std = loadData(fname_train, True)
X_test, Y_test, _,_ = loadData(fname_test, False, feature_mean, feature_std)

# preallocate
err_train = np.zeros(len(NUM_EPOCHS))
err_test = np.zeros(len(NUM_EPOCHS))

# loop over max training epochs
for i in range(len(NUM_EPOCHS)):
    TP_train = np.zeros(SAMPLE_SIZE); FP_train = np.zeros(SAMPLE_SIZE)
    TN_train = np.zeros(SAMPLE_SIZE); FN_train = np.zeros(SAMPLE_SIZE)
    TP_test = np.zeros(SAMPLE_SIZE);  FP_test = np.zeros(SAMPLE_SIZE)
    TN_test = np.zeros(SAMPLE_SIZE);  FN_test = np.zeros(SAMPLE_SIZE)

    # train/test the model several several times
    for s in range(SAMPLE_SIZE):
        # train the model
        weights = trainModel(X_train, Y_train, N_HIDDEN, LRATE, NUM_EPOCHS[i], 0)
        # evalute on the training set
        TP_train[s], TN_train[s], FP_train[s], FN_train[s], _ = testModel(X_train, Y_train, weights, 0)
        # evalute on the test set
        TP_test[s], TN_test[s], FP_test[s], FN_test[s], _ = testModel(X_test, Y_test, weights, 0)

    # compute the mean
    TP_train_mean = np.mean(TP_train); TN_train_mean = np.mean(TN_train)
    FP_train_mean = np.mean(FP_train); FN_train_mean = np.mean(FN_train)
    TP_test_mean = np.mean(TP_test); TN_test_mean = np.mean(TN_test)
    FP_test_mean = np.mean(FP_test); FN_test_mean = np.mean(FN_test)
    # compute the error
    err_train[i] = 1.0 - computeAccuracy(TP_train_mean, TN_train_mean, FP_train_mean, FN_train_mean)
    err_test[i] = 1.0 - computeAccuracy(TP_test_mean, TN_test_mean, FP_test_mean, FN_test_mean)

# plot the training versus test set error
plt.figure(1)
LW = 2.0
plt.plot(NUM_EPOCHS, err_train, marker='o', linewidth=LW)
plt.plot(NUM_EPOCHS, err_test, marker='x', linewidth=LW)
plt.legend(['training set error', 'test set error'], loc='upper right')
plt.title('Number of hidden units = %d\n Data = %s' % (N_HIDDEN, fname_test))
plt.ylabel('Error'); plt.xlabel('Training Epochs')
plt.ylim(ymax = .5, ymin = 0)
plt.show()


import asyncio
import traceback
from typing import Any, Dict, List, Tuple, Type

import discord
import discord.ext.tasks

from ..backend.db import BaseDB
from ..backend.services import BaseService
from ..backend.storage import BaseStorage
from . import chatwheel, lolapi, manager, proxy, quiz, counter
from .routing import Pattern, RoutingList

DEFAULT_ROUTING = RoutingList(
    [
        Pattern(r"^\.vw ", chatwheel.PATTERNS),
        Pattern(r"^\.lol ", lolapi.PATTERNS),
        Pattern(r"^\.proxy ", proxy.PATTERNS),
        Pattern(r"^\.quiz", quiz.PATTERNS),
        Pattern(r"^\.data$", manager.user_data),
        Pattern(r"^\.counter", counter.PATTERNS),
        Pattern(r".*", manager.manage),
    ]
)

class BotClient(discord.Client):
    def __init__(
        self,
        db_type: Type[BaseDB] = BaseDB,
        service_type: Type[BaseService] = BaseService,
        storage_type: Type[BaseStorage] = BaseStorage,
    ) -> None:
        super().__init__(intents=discord.Intents.all())
        self.db_type = db_type
        self.service_type = service_type
        self.storage_type = storage_type
        self.db_callback_buffer: List[Tuple[str, Dict[str, Any]]] = []

    async def on_ready(self) -> None:
        print(f"Logged on as {self.user}\n{'=' * 79}")
        self.db = self.db_type(self.db_callback)
        self.service = self.service_type()
        self.storage = self.storage_type()
        self.ready = False
        self.run_db_callbacks.start()
        self.ready = True

    async def on_message(self, message: discord.Message) -> None:
        if not self.ready:
            return
        if message.author == self.user:
            return

        if message.content == ".index":
            res = ""
            leaf_patterns = DEFAULT_ROUTING.generate_leaf_patterns()
            for leaf_pattern in leaf_patterns:
                res += (
                    f"{leaf_pattern.description}\n```re\n"
                    f"{leaf_pattern.match}```"
                )
            await message.channel.send(res)
            return

        trace, endpoint, groups = DEFAULT_ROUTING.forward(message)
        if endpoint is None:
            return
        print(f"Author: {message.author.name} ({message.author.id})")
        print(f"Channel: {message.channel.id}")
        print(f"Message: {message.content}")
        print(f"Attachments: {[f.filename for f in message.attachments]}")
        print(
            "\n".join(
                f"{i}: {pattern.match}" for i, pattern in enumerate(trace)
            )
        )
        print(f"-> {endpoint.name}")
        print("=" * 79)
        try:
            await endpoint(self, message, groups)
        except Exception:
            traceback.print_exc()
            await message.channel.send(
                "Oops, something broke! Please try again."
            )

    @discord.ext.tasks.loop(seconds=0.5)
    async def run_db_callbacks(self) -> None:
        if not self.ready:
            return
        if self.db_callback_buffer:
            try:
                await self.db_callback_async(*self.db_callback_buffer.pop())
            except Exception as e:
                print(f"DB Callback failed on {e}")

    def db_callback(self, event: str, data: Dict[str, Any]) -> None:
        # Important note: this method can be called from other threads!
        self.db_callback_buffer.append((event, data))

    async def db_callback_async(
        self, event: str, data: Dict[str, Any]
    ) -> None:
        while not self.ready:
            await asyncio.sleep(0.1)
        print(f"DB Callback: {event}")
        print(f"Data: {data}")
        # This is just a PoC for now
        if event == "message":
            target: str = data["target"]
            content: str = data["content"]
            channel = await self.fetch_channel(int(target))
            await channel.send(content)
        print("=" * 79)


# daTA_FRAME perspectvie, ik-index, predicted, actual
import pandas
import regex
import pickle
import os
from ik_index_plot import plot_ik_index

file_path = 'results/ik-ml-1001_20210421_0029.txt'

with open( file_path ) as result_file:
    lines = result_file.readlines()[1:]

results = {}
for result in lines:
    result = regex.findall( r'(?:.*?\s\s+)|.*$', result )
    if result[1].strip() == '1.0':
        perspective = '1E PERS'
    else:
        perspective = '3E PERS'
    results[ result[0].strip() ] = [ perspective, float( result[2] ) ]
    data_frame = pandas.DataFrame.from_dict( results, orient="index", columns=[ 'perspective', 'ik-index' ] )

meta_package = { 'data_frame': data_frame, 'equation': 'ML', 'measurement_title': 'ml prediction' }
file_path = '{}.pickle'.format( os.path.splitext( file_path )[0] )
meta_package[ 'file_path' ] = file_path
with open( file_path, 'wb' ) as pickle_file:
     pickle.dump( meta_package, pickle_file )
plot_ik_index( meta_package, True )


# -*- coding: utf-8 -*-

class Hangman:

    def __init__(self, word, chance=5):

        assert isinstance(word, str)
        assert isinstance(chance, int)

        self.answer = word.lower()
        self.remains = chance
        self.history = []
        self._marker = ['_ '] * len(self.answer)
        self._status = True

        self._mrkstr = ''.join(self._marker)
        self.messege = self._mrkstr + '(Chance: {})'.format(self.remains)

        print(self.messege)

    def guess(self, character):

        assert isinstance(character, str)
        assert len(character) == 1

        character = character.lower()

        if character in self.history:

            print('Already spoken!')

        else:

            self.remains -= 1
            self.history.append(character)

            if character in self.answer:

                print('Correct!')
                idx = [i for i, x in enumerate(self.answer) if x == character]
                for i in idx:
                    self._marker[i] = self.answer[i] + ' '
                    self._mrkstr = ''.join(self._marker)
                    self.messege = (self._mrkstr +
                                    '(Chance: {})'.format(self.remains))
                print(self.messege)

            else:
                print('Wrong!')
                self.messege = (self._mrkstr +
                                '(Chance: {})'.format(self.remains))
                print(self.messege)

            if '_ ' not in list(self._marker):

                self._status = False
                print('\nYou win!')
                print('Answer: ' + self.answer)

            elif self.remains == 0:

                self._status = False
                print('\nYou lose!')
                print('Answer: ' + self.answer)

#%%  Start

print("Hangman 게임을 시작합니다.")
givenWord = str(input("영어 단어를 입력하세요 :"))
chanceNum = int(input("게임 횟수를 입력하세요 (default: 5): "))

gameBoard = Hangman(givenWord, chanceNum)

while gameBoard._status:
    guessWord = str(input("영어 알파벳을 입력하세요."))
    gameBoard.guess(guessWord)

input("게임이 끝났습니다. Enter 키를 누르면 프로그램이 종료됩니다.")