from .exceptions import InvalidMovement, GameOver
import string

# internal helpers
def _position_is_empty_in_board(position, board):
    """
    Checks if given position is empty ("-") in the board.

    :param position: Two-elements tuple representing a
                     position in the board. Example: (0, 1)
    :param board: Game board.

    Returns True if given position is empty, False otherwise.
    """
    whats_there = board[position[0]][position[1]]

    return whats_there == "-"

def _position_is_valid(position):
    """
    Checks if given position is a valid. To consider a position as valid, it
    must be a two-elements tuple, containing values from 0 to 2.
    Examples of valid positions: (0,0), (1,0)
    Examples of invalid positions: (0,0,1), (9,8), False

    :param position: Two-elements tuple representing a
                     position in the board. Example: (0, 1)

    Returns True if given position is valid, False otherwise.
    """
    if isinstance(position, int):
        return False
    elif len(position) > 2:
        return False
    if position[0] > 2 or position[0] < 0:
        return False
    if position[1] > 2 or position[1] < 0:
        return False

    return True

def _board_is_full(board):
    """
    Returns True if all positions in given board are occupied.

    :param board: Game board.
    """
    for row in board:
        for place in row:
            if place == '-':
                return False
    return True

def _is_winning_combination(board, combination, player):
    """
    Checks if all 3 positions in given combination are occupied by given player.

    :param board: Game board.
    :param combination: Tuple containing three position elements.
                        Example: ((0,0), (0,1), (0,2))

    Returns True of all three positions in the combination belongs to given
    player, False otherwise.
    """

    for position in combination:
        if board['board'][position[0]][position[1]] != player:
            return False

    return True

def _check_winning_combinations(board, player):
    """
    There are 8 posible combinations (3 horizontals, 3, verticals and 2 diagonals)
    to win the Tic-tac-toe game.
    This helper loops through all these combinations and checks if any of them
    belongs to the given player.

    :param board: Game board.
    :param player: One of the two playing players.

    Returns the player (winner) of any of the winning combinations is completed
    by given player, or None otherwise.
    """
    horizontal = True
    vertical = True
    diagonal = True

    for row in board:
        for spot in row:
            if spot != player:
                horizontal = False
        if horizontal:
            return player
        horizontal = True

    for i in range(len(board)):
        for row in board:
            if row[i] != player:
                vertical = False
        if vertical:
            return player
        vertical = True

    for i in range(len(board)):
        piece = board[i][i]
        if piece != player:
            diagonal = False
    if diagonal:
        return player

    diagonal = True

    for i in range(len(board)):
        piece = board[i][len(board) - 1 - i]
        if piece != player:
            diagonal = False
    if diagonal:
        return player

# public interface
def start_new_game(player1, player2):
    """
    Creates and returns a new game configuration.
    """
    #size = 3

    game = {}
    game['player1'] = player1
    game['player2'] = player2
    game['board'] = [['-'] * 3 for i in range(3)]
    game['next_turn'] = player1
    game['winner'] = None

    return game

def get_winner(game):
    """
    Returns the winner player if any, or None otherwise.
    """
    return game['winner']

def move(game, player, position):
    """
    Performs a player movement in the game. Must ensure all the pre requisites
    checks before the actual movement is done.
    After registering the movement it must check if the game is over.
    """
    if game['winner'] == None and _board_is_full(game['board']):
        raise InvalidMovement("Game is over.")
    elif game['winner'] != None:
        raise InvalidMovement("Game is over.")
    elif game['next_turn'] != player:
        raise InvalidMovement('"{}" moves next.'.format(game['next_turn'])) #issue formatting the error message
    elif _position_is_valid(position) == False:
        raise InvalidMovement('Position out of range.')

    elif _position_is_empty_in_board(position, game['board']) == False:
        raise InvalidMovement('Position already taken.')
    else:
        game['board'][position[0]][position[1]] = player #since we receive the player as 'X' or 'O' i changed this from game[player]
        if _check_winning_combinations(game['board'], player):
            game['winner'] = player
            raise GameOver('"{}" wins!'.format(player))
        elif _board_is_full(game['board']):
            game['winner'] = None
            raise GameOver('Game is tied!')
        else:
            if game['next_turn'] == game['player2']:
                game['next_turn'] = game['player1']
            else:
                game['next_turn'] = game['player2']

def get_board_as_string(game):
    """
    Returns a string representation of the game board in the current state.
    """

    game_string = '\n'

    horizontal_lines = len(game['board']) - 1

    for row in game['board']:
        game_string += ('%s  |  %s  |  %s\n' % (row[0], row[1], row[2]))
        if horizontal_lines > 0:
            game_string += '--------------\n'
            horizontal_lines -= 1
    return game_string

def get_next_turn(game):
    """
    Returns the player who plays next, or None if the game is already over.
    """
    if game['winner']:
        return None

    return game['next_turn']


"""
Version:
UCT with DP and prohibit path
5 components
analytical evaluation

Feature:
updated DP with configurable basic weight for not in approved paths
"""
import csv
import datetime
import os

from utils.util import mkdir, get_args, generate_depth_list
from Algorithms.SerialUCF import serial_UCF_test
from Algorithms.test_analytic_simulator import anay_read_test
from Algorithms.PlannerTest import Planner_test
from Algorithms.BruteForce import brute_force
from Algorithms.RandomSearch import random_search
from Algorithms.GeneticSearch import genetic_search
from Viz.VIZ import viz_test

def generate_traj_lists(trajectories, test_number):
    traj_lists = []
    for traj in trajectories:
        # TODO for multi size test
        # if traj < 800:

        try_times = test_number
        traj_list = []
        for _ in range(try_times):
            traj_list.append(traj)
        traj_lists.append(traj_list)
    return traj_lists

def main(name):
    # target_lists = []
    target_vout_list = [-200, 50, 200]
    target_vout_list = [50]

    # trajectories = [15,20]
    test_number = 2
    # trajectories = [600, 500, 400, 300, 200, 100]
    trajectories = [100, 300, 600]
    # trajectories = [10,15]
    # test_number = 4
    # trajectories = [10000]
    # test_number = 1
    # [[600,600,600,600],[500,500,500,500]]
    traj_lists = generate_traj_lists(trajectories, test_number)
    anal_output_results = [[] for i in range(test_number + 1)]
    simu_output_results = [[] for i in range(test_number + 1)]

    for try_target_vout in target_vout_list:
        for traj_list in traj_lists:
            # for try_traj in traj_list:
            mkdir("figures")
            mkdir("Results")
            # simulation_root_folder = "simu_analy/" + date_str +'-'+str(os.getpid())
            # mkdir(simulation_root_folder)
            configs = {}
            args_file_name = "config"
            get_args(args_file_name, configs)

            # try_target_vout = 50
            configs["target_vout"] = try_target_vout
            configs["fix_paras"] = None

            configs["min_vout"] = configs["target_vout"] - configs["range_percentage"] * abs(configs["target_vout"])
            configs["max_vout"] = configs["target_vout"] + configs["range_percentage"] * abs(configs["target_vout"])
            depth_list = generate_depth_list(configs["dep_start"], configs["dep_end"], configs["dep_step_len"])
            # traj_list = generate_traj_List(configs["traj_start"], configs["traj_end"], configs["traj_step_len"])
            date_str = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
            if configs["algorithm"] == "UCF" or configs["algorithm"] == "UCT":
                serial_UCF_test(depth_list, traj_list, configs, date_str)
            elif configs["algorithm"] == "GeneticSearch" or configs["algorithm"] == "GS":
                anal_results, simu_results = genetic_search(traj_list, configs, date_str)
                for i in range(test_number + 1):
                    anal_output_results[i].extend(anal_results[i])
                    simu_output_results[i].extend(simu_results[i])
            elif configs["algorithm"] == "VIZ":
                mkdir("Viz/TreeStructures")
                viz_test(depth_list, traj_list, configs, date_str)
            # elif configs["algorithm"] == "TEST":
            #     hash_read_test(depth_list, traj_list, configs, date_str)
            elif configs["algorithm"] == "ANA_TEST":
                anay_read_test(configs)
            elif configs["algorithm"] == "TEST":
                Planner_test(depth_list, traj_list, configs, date_str)
            elif configs["algorithm"] == "RS":
                random_search(traj_list, configs, date_str)
            elif configs["algorithm"] == "BF":
                anal_results, simu_results = brute_force(depth_list, traj_list, configs, date_str)
                for i in range(test_number + 1):
                    anal_output_results[i].extend(anal_results[i])
                    simu_output_results[i].extend(simu_results[i])
        anal_out_file_name = "Results/"+configs["algorithm"]+"-" + str(try_target_vout) + "-anal-" + date_str + "-" + str(os.getpid()) + ".csv"
        with open(anal_out_file_name, 'w') as f:
            csv_writer = csv.writer(f)
            csv_writer.writerows(anal_output_results)
        f.close()
        simu_out_file_name = "Results/"+configs["algorithm"]+"-" + str(try_target_vout) + "-simu-" + date_str + "-" + str(os.getpid()) + ".csv"
        with open(simu_out_file_name, 'w') as f:
            csv_writer = csv.writer(f)
            csv_writer.writerows(simu_output_results)
        f.close()

    os.system("rm *-analytic*")
    os.system("rm PCC-*.cki")
    os.system("rm PCC-*.simu")
    return

if __name__ == '__main__':
    # from SimulatorAnalysis.sim_result import conduction_analytics_result
    # conduction_analytics_result('./', 'Rb1')
    # read_result('mutitest_50-2021-04-17-16-29-41-37526.txt')
    main('PyCharm')


#TO DO:
#- Integrate the csv file with the Dictionaries and the usernames and id lists
#- Prevent the user from repeating a username name
#- Allow the user to delete users from the list and dictionaries
#- Create a list for each user to have personalized connections

#Beginning of class User

class User :
    def __init__(self, user_name, user_id): #Constructor (requieres username and user id)
        self.user_name = user_name
        self.user_id= user_id
        self.connections = []

    #Getter Methods

    def getUserName(self):
        return self.user_name

    def getUserId(self):
        return self.user_id

    def getConnections(self):
        return self.connections

    #Setter Methods

    def addConnection (self, other_user_name):
        self.connections.append(other_user_name)

    #to delete a connection, get the index number of it and use del self.connections [index]

#End of User class

#Beginning of class Network
class Network :
    def __init__(self): #Network constuctor
        self.users = []
        self.usernames= []

    #Getter methods
    def numUsers(self):
        return len(self.users)

    def getUserId(self, user_name):
        #length = Network.numUsers()
        for index in range(len(self.users)):
            possible_user = self.users[index]
            if (possible_user.getUserName() == user_name):
                return possible_user.getUserId() #Match found
        return -1 #Match not found

    def getUsernameList(self):
        doc_usernames = open("Network_Usernames.txt","r")
        self.usernames = doc_usernames.readlines()
        return (self.usernames)
    #Setter Methods
    def addUser(self,csv):
        new_user_name= raw_input ("Create a new user name: ")
        new_user_id= (len(self.users)+1)
        new_user= User(new_user_name, new_user_id) #Creates a new user
        self.users.append(new_user)
        self.usernames.append(new_user_name)

        #Creating the dictionary and putting the data in the csv file
        dic ={"Username":[], "User ID":[]}
        dic["Username"].append(new_user_name)
        dic["User ID"].append(new_user_id)

        new_user_id=str(new_user_id)

        #Writing it out
        csv.write(new_user_name+","+new_user_id+"\n")

        #Keeping track of data
        #doc_usernames= open("Network_Usernames.csv","a")
        #doc_usernames.write(new_user_name)
        #doc_user_id= open ("Network_User_ID.csv","a")
        #snew_user_id= str(new_user_id)
        #doc_user_id.write(snew_user_id)
        #doc_usernames.close()
        #doc_user_id.close()

    def addConnection(self, user_name1, user_name2):
        user_id_1= self.getUserId(user_name1)
        user_id_2= self.getUserId(user_name2)

        if (user_id_1==-1 or user_id_2==-1):
            print("Users are not in the network.")
        else:
            user1= self.users[user_id_1]
            user2= self.users[user_id_2]

            user1.addConnection(user_name2)
            user2.addConnection(user_name1)
            print("These are the current connections")
            connections = User.getConnections()
            print(connections)
#End of Network Class

#This defines the program flow for the user interface / Main method
def main():
    net=intro()
    csv=open("Network_Users.csv","a")
    action (net,csv)

def intro():
    print("Welcome to the Network!")
    print("\t~~~~~~~")
    print("What would you like to do?")
    return (Network())

def action(net,csv):
    print ("A) Create a user \t B) Connect to a user \t C) Delete a user \t D) View current users \t E) Exit")
    decision = raw_input()
    #Create a user
    if decision == "A":
        net.addUser(csv)
        action(net,csv)
    #Connect users
    elif decision == "B":
        username1= raw_input("User Name 1:")
        username2= raw_input("User Name 2:")
        net.addConnection(username1,username2)
        action(net,csv)
    #Delete a user
    elif decision == "C":
        print ("Work in progress ... restart")
        main()
    #View current users
    elif decision =="D":
        user_list= net.getUsernameList()
        print ("These are the current users of the Network")
        print (user_list)
        action(net, csv)

    elif decision =="E":
        print ("Closing the Network")
        csv.close()
        quit()

#This runs your program

if __name__== '__main__':
    main()


import nltk
import numpy as np
import random
import string #For processing python std strings

# Read in the data from the txt file
# then filter it
f = open('chatbot.txt','r', errors = 'ignore')
raw = f.read()

# Convert to lowercase

raw = raw.lower()

 # converts to list of sentences
sent_tokens = nltk.sent_tokenize(raw)
# converts to list of words
word_tokens = nltk.word_tokenize(raw)

# Lemmatization essentially converts word into their base meaning, like running into run
# Define function LemTokens, takes tokens and normalizes them
lemmer = nltk.stem.WordNetLemmatizer()

# WordNet is a semantically-oriented dictionary of English included in NLTK.
def LemTokens(tokens):

    return [lemmer.lemmatize(token) for token in tokens]

remove_punct_dict = dict((ord(punct), None) for punct in string.punctuation)

# Normalizes the word
def LemNormalize(text):
    return LemTokens(nltk.word_tokenize(text.lower().translate(remove_punct_dict)))

# Keyword matching

GREETING_INPUTS = ("hello", "hi", "greetings", "sup", "what's up","hey",)

GREETING_RESPONSES = ["hi", "hey", "*nods*", "hi there", "hello", "I am glad! You are talking to me"]

# Matches a greeting with another greeting
# For generic greetings
def greeting(sentence):

    for word in sentence.split():
        if word.lower() in GREETING_INPUTS:
            return random.choice(GREETING_RESPONSES)

# Will convert document into matrix of TF-IDF features
# Term Frequency helps learn the importance of certain words
# also gives meaning to the document
from sklearn.feature_extraction.text import TfidfVectorizer
# Cosine similarity takes the dot product of the TF vectors
# Smaller the angle the more similar terms are
# Used to determine how similar a word is from the human, to the stored words
from sklearn.metrics.pairwise import cosine_similarity

# Response is needed if too many meaning are possible, or none
def response(user_response):
    robo_response = ''
    sent_tokens.append(user_response)

    # Run this through a method that will vercotize the tokens
    # Stop words are extremely common words that have little value
    TfidfVec = TfidfVectorizer(tokenizer=LemNormalize, stop_words='english')
    # Fit transform "centers" the data, normalizes it
    tfidf = TfidfVec.fit_transform(sent_tokens)
    vals = cosine_similarity(tfidf[-1], tfidf)
    idx = vals.argsort()[0][-2]
    flat = vals.flatten()
    flat.sort()
    req_tfidf = flat[-2]

    if(req_tfidf == 0):
        robo_response = robo_response + "I am sorry! I don't understand you"
        return robo_response
    else:
        robo_response = robo_response + sent_tokens[idx]
        return robo_response

# Main chat interface
flag=True
print("ROBO: My name is Robo. I will answer your queries about Chatbots. If you want to exit, type Bye!")
while(flag==True):
    user_response = input()
    user_response=user_response.lower()
    if(user_response!='bye'):
        if(user_response=='thanks' or user_response=='thank you' ):
            flag=False
            print("ROBO: You are welcome..")
        else:
            if(greeting(user_response)!=None):
                print("ROBO: "+greeting(user_response))
            else:
                print("ROBO: ",end="")
                print(response(user_response))
                sent_tokens.remove(user_response)
    else:
        flag=False
        print("ROBO: Bye! take care..")


from django.conf.urls import url, include
from django.contrib.auth import views as auth_views
from . import views

urlpatterns = [

    url(r'^choice_register/$', views.RegisterOption, name="register_option"),
    url(r'^$', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),
    url(r'^student_register/$', views.StudentRegister, name="student_register"),
    url(r'^tutor_register/$', views.TutorRegister, name="tutor_register"),
    url(r"^student_update/$", views.profile, name='student_update'),
    url(r'^logout/$', views.logoutUser, name="logout"),
]


import sys

sys.path.append("D:\\user\\pys\\Python-002\\")
print(f"current path:{sys.path}")

import scrapy

from scrapy.selector import Selector
from week02.homeWork01.homeWork01.items import Homework01Item

class Week2maoyan01Spider(scrapy.Spider):
    name = 'week2maoyan01'
    allowed_domains = ['maoyan.com']
    start_urls = ['https://maoyan.com/films?showType=3',
                  'file:///D:/user/pys/Python-002/week02/testDataFile/maoyanshowType3.html']

    def start_requests(self):
        yield scrapy.Request(url=self.start_urls[0], callback=self.parse)

    def parse(self, response):
        # 通过XPath获取电影属性
        movie_urls = Selector(response=response).xpath('//div[@class="movie-hover-info"]')[0:10]
        movie_info_item = Homework01Item()
        for movie_info in movie_urls:
            # span页签中class有的使用的是"name"，有的使用的是"name noscore"，故使用contains
            movie_name = movie_info.xpath("./div/span[contains(@class,'name ')]/text()")
            print(f"电影名称:{movie_name.extract_first()}")
            movie_info_item['movie_name'] = movie_name.extract_first()
            movie_type = movie_info.xpath("./div[contains(@class,'movie-hover-title')]/text()")
            # 不能使用normalize-space，经过查资料，该函数对单个字符串有效
            # movie_tag = movie_info.xpath("normalize-space(./div[contains(@class,'movie-hover-title')]/text())")

            # 使用strip()去除空格、\r\n，使用filter去除掉为空的字符
            movie_tag = list(filter(lambda x: x != '', [item.strip() for item in movie_type.extract()]))
            # print(f"电影类型:{movie_tag[0]}")
            movie_info_item['movie_type'] = movie_tag[0]
            # print(f"电影类型:{movie_tag[-1]}")
            movie_info_item['movie_release_time'] = movie_tag[-1]
            yield movie_info_item


"""
Given a list of numbers and a number k,
 return whether any two numbers from the list add up to k.

For example, given [10, 15, 3, 7] and k of 17,
 return true since 10 + 7 is 17.

Bonus: Can you do this in one pass?
"""

input_list = [10, 15 ,3 , 7]
k = 18

def test_for_sum(input_list, k):
    return_val = False
    for i, x in enumerate(input_list):
        for j, y in enumerate(input_list):
            if(i != j):
                if(x + y == k):
                    return_val = True
                    return return_val
    return return_val

#the question can be rephrased as
# given an element of input_list "x"
# does there exist any other element of input_list "y", such that
# y = k - x
#written in this way we can pass through the list once populating a new list
# and return true if an element is in that list

def test_for_sum2(input_list,k):
    """
    function checks whether any two elements in input_list add to k
    """
    return_val = False
    second_list = []
    for i, x in enumerate(input_list):
        second_list.append(k - x)
        for j, y in enumerate(second_list):
            if (i != j):
                if (x == y):
                    return_val = True
                    return return_val
    return return_val

test_for_sum(input_list,k)
test_for_sum2(input_list,k)


def game():
    play_field = [
        ["   ", " 1 ", " 2 ", " 3 "],
        [" 1 ", " - ", " - ", " - "],
        [" 2 ", " - ", " - ", " - "],
        [" 3 ", " - ", " - ", " - "],
    ]

    show_field(play_field)

    turn_count = 0

    for turn_count in range(10):
        if turn_count == 9:
            print("Ничья!")
            break
        if turn_count % 2 == 0:
            marker = " X "
        else:
            marker = " Y "

        coord_x, coord_y = users_input(play_field, marker)
        play_field[coord_x][coord_y] = marker

        if is_win(play_field, marker):
            print(f"Игрок{marker} Выиграл!\nПоздравляем!")
            show_field(play_field)
            break
        else:
            show_field(play_field)

def show_field(field):
    for row in field:
        print("".join([str(elem) for elem in row]))

def users_input(field, marker):
    while True:
        place = input(f"Введите координаты для{marker}: ").split()
        if len(place) != 2:
            print("Введите 2 координаты")
            continue
        if not (place[0].isdigit() and place[1].isdigit()):
            print("Введите числа")
            continue
        coord_x, coord_y = map(int, place)
        if not (coord_x > 0 and coord_x < 4 and coord_y > 0 and coord_y < 4):
            print("Вышли за рамки поля")
            continue
        if field[coord_x][coord_y] != " - ":
            print("Клетка занята")
            continue
        break
    return coord_x, coord_y

def is_win(field, marker):
    def check_line(item_1, item_2, item_3, marker):
        if item_1 == item_2 == item_3 == marker:
            return True
        return False

    for _ in range(1, 4):
        if (
            check_line(field[_][1], field[_][2], field[_][3], marker)
            or check_line(field[1][_], field[2][_], field[3][_], marker)
            or check_line(field[1][1], field[2][2], field[3][3], marker)
            or check_line(field[3][1], field[2][2], field[1][3], marker)
        ):
            return True
    return False

if __name__ == "__main__":
    game()


import numpy
from keras.datasets import cifar10
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.constraints import maxnorm
from keras.optimizers import SGD
from keras.layers.convolutional import Convolution2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
from keras.preprocessing.image import ImageDataGenerator
from keras import backend as K

K.set_image_dim_ordering('th')

seed = 7
numpy.random.seed(seed)

train_datagen = ImageDataGenerator(
    shear_range=0.2,
    zoom_range=0.2,
    rotation_range=0.1,
    channel_shift_range=0.2,
    horizontal_flip=True)

test_datagen = ImageDataGenerator()

(X_train, y_train), (X_test, y_test) = cifar10.load_data()

y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)

num_classes = y_test.shape[1]

train_generator = train_datagen.flow(X_train, y_train, batch_size=64)

validation_generator = test_datagen.flow(X_test, y_test, batch_size=64)

# (X_train, y_train), (X_test, y_test) = cifar10.load_data()
# X_train = X_train.astype('float32')
# X_test = X_test.astype('float32')
# X_train = X_train / 255.0
# X_test = X_test / 255.0

# y_train = np_utils.to_categorical(y_train)
# y_test = np_utils.to_categorical(y_test)
#
# num_classes = y_test.shape[1]

model = Sequential()
model.add(
    Convolution2D(32, 3, 3, input_shape=(3, 32, 32), border_mode='same', activation='relu'))
model.add(Dropout(0.2))
model.add(Convolution2D(32, 3, 3, activation='relu', border_mode='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(64, 3, 3, activation='relu', border_mode='same'))
model.add(Dropout(0.2))
model.add(Convolution2D(64, 3, 3, activation='relu', border_mode='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(128, 3, 3, activation='relu', border_mode='same'))
model.add(Dropout(0.2))
model.add(Convolution2D(128, 3, 3, activation='relu', border_mode='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(256, 3, 3, activation='relu', border_mode='same'))
model.add(Dropout(0.2))
model.add(Convolution2D(256, 3, 3, activation='relu', border_mode='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dropout(0.2))
model.add(Dense(1024, activation='relu', W_constraint=maxnorm(3)))
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu', W_constraint=maxnorm(3)))
model.add(Dropout(0.2))
model.add(Dense(num_classes, activation='softmax'))

epochs = 80
learning_rate = 0.01
decay = learning_rate/(epochs*2)
sgd = SGD(lr=learning_rate, momentum=0.9, decay=decay, nesterov=False)
model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])
print(model.summary())

numpy.random.seed(seed)

model.fit_generator(
    train_generator,
    samples_per_epoch=50000,
    nb_epoch=epochs,
    validation_data=validation_generator,
    nb_val_samples=300000,
    verbose=2)

scores = model.evaluate(X_test, y_test, verbose=0)
print("Accuracy: %.2f%%" % (scores[1]*100))


r"""Functions that deal with string representations of atomic symbols
and numbers."""

import re
import warnings
from typing import (Union, Optional, Any, Tuple)

from .elements import _atomic_symbols, _atomic_symbols_dict, _Elements
from .isotopes import _Isotopes
from .particles import ParticleZoo

from .parsing import _dealias_particle_aliases
from .classes import Particle

from ..utils import (AtomicWarning,
                     InvalidElementError,
                     InvalidIsotopeError,
                     InvalidIonError,
                     InvalidParticleError)

# TODO: Create an ion_symbol function
# TODO: Create a particle_symbol function

def atomic_symbol(element: Union[str, int]) -> str:
    r"""Returns the atomic symbol.

    Parameters
    ----------

    element: string or integer
        A string representing an element, isotope, or ion; or an
        integer or string representing an atomic number.

    Returns
    -------

    symbol: string
        The atomic symbol of the element, isotope, or nucleon.

    Raises
    ------

    InvalidElementError
        If the argument is a valid particle but not a valid element.

    InvalidParticleError
        If the argument does not correspond to a valid particle.

    TypeError
        If the argument is not a string or integer.

    See also
    --------

    isotope_symbol : returns isotope symbol instead of atomic symbol.

    element_name : returns the name of an element.

    Notes
    -----

    This function returns the symbol of the element rather than the
    symbol of an isotope.  For example, 'deuterium', 'T', or
    'hydrogen-2' will yield 'H'; 'alpha' will yield 'He'; and
    'iron-56' or 'Fe-56' will yield 'Fe'.

    This function is case insensitive when there is no ambiguity
    associated with case.  However, this function will return 'H' for
    hydrogen for lower case 'p' but capital 'P' if the argument is 'P'
    for phosphorus.  This function will return 'N' for nitrogen if the
    argument is capital 'N', but will not accept lower case 'n' for
    neutrons.

    Examples
    --------

    >>> atomic_symbol('helium')
    'He'
    >>> atomic_symbol(42)
    'Mo'
    >>> atomic_symbol('D')
    'H'
    >>> atomic_symbol('C-13')
    'C'
    >>> atomic_symbol('alpha')
    'He'
    >>> atomic_symbol('79')
    'Au'
    >>> atomic_symbol('N')  # Nitrogen
    'N'
    >>> atomic_symbol('P'), atomic_symbol('p')  # Phosphorus, proton
    ('P', 'H')

    """

    try:
        particle = Particle(element)
    except InvalidParticleError:
        raise InvalidParticleError(
            f"The argument {repr(element)} to atomic_symbol does not "
            f"represent a valid particle.")
    except TypeError:
        raise TypeError(
            f"The argument {repr(element)} to atomic_symbol is not an "
            f"integer or string.")

    if particle.element:
        return particle._atomic_symbol
    else:
        raise InvalidElementError(
            f"The argument {repr(element)} to atomic_symbol does not "
            f"represent a valid element.")

def isotope_symbol(argument: Union[str, int], mass_numb: int = None) -> str:
    r"""Returns the symbol representing an isotope.

    Parameters
    ----------

    argument: integer or string
        A string representing an element, isotope, or ion or an
        integer representing an atomic number

    mass_numb: integer or string
        An integer or string representing the mass number of the
        isotope.

    Returns
    -------

    symbol: string
        The isotopic symbol. The result will generally be returned as
        something like 'He-4' or 'Au-197', but will return 'D' for
        deuterium and 'T' for tritium.

    Raises
    ------

    InvalidIsotopeError
        If the argument is a valid particle but not a valid isotope.

    InvalidParticleError
        If the argument does not correspond to a valid particle
        or contradictory information is provided.

    TypeError
        If the argument is not a string or integer.

    Warns
    -----

    AtomicWarning
        If redundant isotope information is provided.

    See also
    --------

    atomic_symbol : returns atomic symbol instead of isotopic symbol

    Notes
    -----

    This function returns the symbol of the element rather than the
    symbol of an isotope.  For example, 'deuterium', 'T', or
    'hydrogen-2' will yield 'H'; 'alpha' will yield 'He'; and
    'iron-56' or 'Fe-56' will yield 'Fe'.

    Examples
    --------
    >>> isotope_symbol('He', 4)
    'He-4'
    >>> isotope_symbol(79, 197)
    'Au-197'
    >>> isotope_symbol('hydrogen-2')
    'D'
    >>> isotope_symbol('carbon-13')
    'C-13'
    >>> isotope_symbol('alpha')
    'He-4'

    """

    try:
        particle = Particle(argument, mass_numb=mass_numb)
    except InvalidParticleError:
        raise InvalidParticleError
    except Exception:
        pass

    # TODO: Remove this functionality when particle_symbol comes online
    if _is_neutron(argument, mass_numb):
        return 'n'

    if argument in ParticleZoo.everything - {'p+'}:
        raise InvalidIsotopeError("The argument {argument} does not "
                                  "correspond to a valid isotope in "
                                  "isotope_symbol.")

    try:
        element = atomic_symbol(argument)
    except InvalidParticleError:
        raise InvalidParticleError(
            f"The argument {argument} to isotope_symbol is not a valid "
            f"particle.")
    except InvalidElementError:
        raise InvalidIsotopeError(f"The argument {argument} to isotope_symbol "
                                  f"does not correspond to a valid element or "
                                  f"isotope.")

    # If the argument is already in our standard form for an isotope,
    # return the argument.

    if mass_numb is None and argument in _Isotopes.keys():
        return argument

    if isinstance(argument, str):
        argument, Z = _extract_integer_charge(argument)

    if isinstance(argument, str) and argument.isdigit():
        argument = int(argument)

    if isinstance(mass_numb, str) and mass_numb.isdigit():
        mass_numb = int(mass_numb)

    # This routine allows several forms of input, and must be able to handle
    # all of the exceptions that can arise with useful error messages.

    if not isinstance(argument, (str, int)):
        raise TypeError("The first argument in isotope_symbol must be either "
                        "a string representing an element or isotope, or an "
                        "integer representing the atomic number (or 0 for "
                        " neutrons).")

    if not (isinstance(mass_numb, int) or
            mass_numb is None):  # coveralls: ignore
        raise TypeError("The second argument in isotope_symbol must be an "
                        "integer (or a string containing an integer) that "
                        "represents the mass number of an isotope.")

    if isinstance(argument, int):
        if not 0 <= argument <= 118:
            raise InvalidParticleError(
                "Invalid atomic number in isotope_symbol")
        if mass_numb is None:
            raise InvalidIsotopeError("Insufficient information to determine "
                                      "isotope in isotope_symbol.")

    # Get mass number from argument, check for redundancies, and take
    # care of special cases.

    if isinstance(argument, str):
        if argument.count('-') == 1:
            dash_position = argument.find('-')
            mass_numb_from_arg = argument[dash_position+1:].strip()
            mass_numb_from_arg = int(mass_numb_from_arg)
        elif argument in ['p', 'p+'] or \
                argument.lower() in ['protium', 'proton']:
            mass_numb_from_arg = 1
        elif argument.lower() in ['d', 'deuterium', 'deuteron']:
            mass_numb_from_arg = 2
        elif argument.lower() in ['t', 'tritium', 'triton']:
            mass_numb_from_arg = 3
        elif _is_alpha(argument):
            mass_numb_from_arg = 4
        else:
            mass_numb_from_arg = None

        if mass_numb is None and mass_numb_from_arg is None:
            raise InvalidIsotopeError(
                "Insufficient information to determine the mass"
                " number from the inputs to isotope_symbol.")

        if mass_numb is not None and mass_numb_from_arg is not None:
            if mass_numb == mass_numb_from_arg:
                warnings.warn(
                    "Redundant mass number information in isotope_symbol "
                    f"from inputs: {argument}, {mass_numb}", AtomicWarning)
            else:  # coveralls: ignore
                raise InvalidParticleError(
                    "Contradictory mass number information in isotope_symbol.")

        if mass_numb_from_arg is not None:
            mass_numb = mass_numb_from_arg

    isotope = f"{element}-{mass_numb}"

    if isotope == 'H-2':
        isotope = 'D'
    elif isotope == 'H-3':
        isotope = 'T'

    if _Elements[element]['atomic_number'] > mass_numb:
        raise InvalidParticleError("The atomic number cannot exceed the mass "
                                   "number in isotope_symbol.")

    return isotope

def ion_symbol(argument: Union[str, int], mass_numb: int = None,
               Z: int = None) -> str:
    r"""Returns the ion symbol."""
    particle = Particle(argument, mass_numb=mass_numb, Z=Z)
    if particle.ion:
        return particle.ion
    else:
        raise InvalidIonError

def particle_symbol(argument: Union[str, int], mass_numb: int = None,
                    Z: int = None) -> str:
    r"""Returns the symbol of a particle."""
    particle = Particle(argument, mass_numb=mass_numb, Z=Z)
    return particle.particle

def element_name(argument: Union[str, int]) -> str:
    r"""Returns the name of an element.

    Parameters
    ----------

    argument : string or integer
        A string representing an element, isotope, or ion or an
        integer representing an atomic number

    Returns
    -------

    name : string
        The name of the element.

    Raises
    ------

    InvalidElementError
        If the argument is a valid particle but not a valid element.

    InvalidParticleError
        If the argument does not correspond to a valid particle.

    TypeError
        If the argument is not a string or integer.

    See also
    --------

    atomic_symbol : returns the atomic symbol

    isotope_symbol : returns the symbol of an isotope

    Examples
    --------

    >>> element_name("H")
    'hydrogen'
    >>> element_name("T")
    'hydrogen'
    >>> element_name("alpha")
    'helium'
    >>> element_name(42)
    'molybdenum'
    >>> element_name("C-12")
    'carbon'

    """

    try:
        element = atomic_symbol(argument)
        name = _Elements[element]["name"]
    except InvalidElementError:
        raise InvalidElementError("Invalid element in element_name.")
    except InvalidParticleError:
        raise InvalidParticleError("Invalid particle in element_name.")
    except TypeError:
        raise TypeError("Invalid input to element_name.")

    return name

def _extract_integer_charge(argument: str) -> Tuple[str, int]:
    r"""Splits strings containing element or isotope and charge state
    information into a string without the charge state information and
    the charge state as an integer (or None if no charge state
    information is given).

    Parameters
    ----------

    argument : string
        String containing information for an element or isotope in any
        of the allowed formats, followed by charge state information.

    Returns
    -------

    argument : string
        The original string with charge state information removed.

    Z : integer
        The charge state of an ion (e.g., this will return 1 if one
        electron has been removed and -1 if one electron has been
        gained)

    Raises
    ------

    InvalidParticleError
        If invalid charge information is included.

    Notes
    -----

    If the argument is not a string, this function will return the
    original argument and None.

    This function does not check that the charge state is valid.

    Examples
    --------

    >>> isotope, Z = _extract_integer_charge('Fe-56+++')
    >>> print(isotope)
    Fe-56
    >>> print(Z)
    3
    >>> _extract_integer_charge('D +1')
    ('D', 1)

    """

    if not isinstance(argument, str):
        return argument, None

    try:
        argument = _dealias_particle_aliases(argument)
    except Exception:
        pass

    if argument in ['n', 'antineutron'] or 'nu_' in argument:
        return argument, 0
    if argument in ['e-', 'mu-', 'tau-', 'p-']:
        return argument, -1
    elif argument in ['e+', 'mu+', 'tau+', 'p']:
        return argument, 1

    if argument.lower() == 'alpha':
        return 'He-4', 1
    elif 'alpha' in argument.lower():
        raise InvalidParticleError("Invalid representation of an alpha"
                                   " particle")

    if argument.count(' ') == 1:  # For cases like 'Fe +2' and 'Fe-56 2+'

        argument, ion_info = argument.split()

        check1 = (ion_info.endswith(('-', '+')) ^
                  ion_info.startswith(('-', '+')))

        check2 = ((ion_info.count('-') == 1 and ion_info.count('+') == 0) or
                  (ion_info.count('-') == 0 and ion_info.count('+') == 1))

        if '-' in ion_info:
            sign = -1
            charge = ion_info.replace('-', '')
        elif '+' in ion_info:
            sign = +1
            charge = ion_info.replace('+', '')

        try:
            Z = sign * int(charge)
            check3 = True
        except Exception:
            check3 = False

        if not (check1 and check2 and check3):
            raise InvalidParticleError(
                "The following input does not have valid charge: "
                f"state information: {argument}{ion_info}")

    elif argument.endswith(('-', '+')):  # For cases like 'Fe++' or 'Si-'

        char = argument[-1]
        match = re.match(r"["+char+"]*", argument[::-1])

        Z = match.span()[1]

        if char == '-':
            Z = -Z

        argument = argument[0:len(argument)-match.span()[1]]

        if argument.endswith(('-', '+')):
            raise InvalidParticleError("Invalid charge state information")

    else:
        Z = None

    if Z is not None and Z < -3:
        warnings.warn(f"Element {atomic_symbol(argument)} has a charge of "
                      f"{Z} which is unlikely to occur in nature.",
                      AtomicWarning)

    return argument, Z

def _is_neutron(argument: Any, mass_numb: int = None) -> bool:
    r"""Returns True if the argument corresponds to a neutron, and
    False otherwise."""

    if argument == 0 and mass_numb == 1:
        return True
    elif isinstance(argument, str) and mass_numb is None:
        if argument in ('n', 'n-1') or argument.lower() in ['neutron', 'n0']:
            return True
        else:
            return False
    else:
        return False

def _is_hydrogen(argument: Any,
                 can_be_atomic_number: Optional[bool] = False) -> bool:
    r"""Returns True if the argument corresponds to hydrogen, and False
    otherwise."""

    case_sensitive_aliases = ['p', 'p+', 'H', 'D', 'T']

    case_insensitive_aliases = ['proton', 'protium', 'deuterium',
                                'deuteron', 'triton', 'tritium', 'hydrogen']

    for mass_numb in range(1, 8):
        case_sensitive_aliases.append(f'H-{mass_numb}')
        case_insensitive_aliases.append(f'hydrogen-{mass_numb}')

    if isinstance(argument, str):

        argument, Z = _extract_integer_charge(argument)

        if argument in case_sensitive_aliases:
            is_hydrogen = True
        else:
            is_hydrogen = argument.lower() in case_insensitive_aliases

        if is_hydrogen and Z is not None and Z > 1:
            raise InvalidParticleError("Invalid charge state of hydrogen")

    elif argument == 1 and can_be_atomic_number:
        is_hydrogen = True
    else:
        is_hydrogen = False

    return is_hydrogen

def _is_electron(arg: Any) -> bool:
    r"""Returns True if the argument corresponds to an electron, and False
    otherwise."""

    if not isinstance(arg, str):
        return False

    return arg in ['e', 'e-'] or arg.lower() == 'electron'

def _is_positron(arg: Any) -> bool:
    r"""Returns True if the argument corresponds to a positron, and False
    otherwise."""

    if not isinstance(arg, str):
        return False

    return arg == 'e+' or arg.lower() == 'positron'

def _is_antiproton(arg: Any) -> bool:
    r"""Returns True if the argument corresponds to an antiproton, and
    False otherwise."""

    if not isinstance(arg, str):
        return False

    return arg == 'p-' or arg.lower() == 'antiproton'

def _is_antineutron(arg: Any) -> bool:
    r"""Returns True if the argument corresponds to an antineutron, and
    False otherwise."""

    if not isinstance(arg, str):
        return False

    return arg.lower() == 'antineutron'

def _is_proton(arg: Any, Z: int = None, mass_numb: int = None) -> bool:
    r"""Returns True if the argument corresponds to a proton, and
    False otherwise.  This function returns False for 'H-1' if no
    charge state is given."""

    argument, Z_from_arg = _extract_integer_charge(arg)

    if (Z is None) == (Z_from_arg is None):
        return False
    else:
        if Z is None:
            Z = Z_from_arg

    try:
        isotope = isotope_symbol(arg, mass_numb)
    except Exception:
        return False

    return isotope == 'H-1' and Z == 1

def _is_alpha(arg: Any) -> bool:
    r"""Returns True if the argument corresponds to an alpha particle,
    and False otherwise."""

    if not isinstance(arg, str):
        return False

    if arg.lower() == 'alpha':
        return True
    elif 'alpha' in arg.lower():
        raise InvalidParticleError(
            f"{arg} is an invalid representation of an alpha particle")
    else:
        arg, Z = _extract_integer_charge(arg)

        if Z != 2 or arg[-2:] != '-4':
            return False
        else:

            dash_position = arg.find('-')
            arg = arg[:dash_position]

            return arg.lower() == 'helium' or arg == 'He'


"""
Forms
"""
import wtforms
from gutter.client.models import Switch, Condition

from . import wtforms_extension
from .registry import operators, arguments

class ConditionForm(wtforms.Form):
    """
    Form defining the individual condition of a switch.
    """

    argument = wtforms_extension.SelectField(choices=arguments.as_choices)
    negative = wtforms.SelectField(choices=(('0', 'Is'), ('1', 'Is Not')))
    operator = wtforms_extension.SelectField(choices=operators.as_choices)

    operator_arguments = wtforms.FieldList(wtforms.StringField(),
                                           min_entries=2,
                                           max_entries=2)

class SwitchForm(wtforms.Form):
    """
    Form defining a switch.
    """
    name = wtforms.StringField(u'Name',
                               validators=[wtforms.validators.required(),
                                           wtforms.validators.length(max=100)])
    label = wtforms.StringField(u'Label')
    description = wtforms.TextAreaField(u'Description')

    state = wtforms.SelectField(
        u'State',
        choices={'1': 'Disabled',
                 '2': 'Selective',
                 '3': 'Global'}.items())

    compounded = wtforms.BooleanField()
    concent = wtforms.BooleanField(u'Consent')
    delete = wtforms.HiddenField()
    is_new = wtforms.HiddenField()

    conditions = wtforms.FieldList(wtforms.FormField(ConditionForm),
                                   min_entries=0)

    def to_object(self):
        """
        Return a Switch object based on form data.
        """
        switch = Switch(name=self.name.data,
                        label=self.label.data,
                        description=self.description.data,
                        # States must be integer to match gutter client
                        state=int(self.state.data),
                        compounded=self.compounded.data,
                        concent=self.concent.data)

        for condition in self.conditions:
            switch.conditions.append(self.make_condition(condition))

        return switch

    def make_condition(self, condition_form):
        """
        Create a condition based on form input
        """
        # Operators in the registry are the types (classes), so extract that
        # and we will construct it from the remaining data, which are the
        # arguments for the operator
        operator_type = operators[condition_form.operator.data]

        # Arguments are a Class property, so just a simple fetch from the
        # arguments dict will retreive it
        argument = arguments[condition_form.argument.data]

        # The remaining variables in the data are the arguments to the
        # operator, but they need to be cast by the argument to their
        # right type
        caster = argument.variable.to_python

        operator_kwargs = dict((k, caster(v)) for k, v in zip(
            operator_type.arguments, condition_form.operator_arguments.data))

        return Condition(argument=argument.owner,
                         attribute=argument.name,
                         operator=operator_type(**operator_kwargs),
                         negative=bool(int(condition_form.negative.data)))

    @classmethod
    def from_object(cls, switch):
        """
        Return a new form based on a Switch object.
        """
        data = dict(label=switch.label,
                    name=switch.name,
                    description=switch.description,
                    state=switch.state,
                    compounded=switch.compounded,
                    concent=switch.concent)

        instance = cls(**data)

        for condition in switch.conditions:
            instance.append_condition(condition)

        return instance

    def append_condition(self, condition):
        """
        Given a condition, append it to the list of form conditions.
        """
        fields = dict(argument='.'.join((condition.argument.__name__,
                                         condition.attribute)),
                      negative='1' if condition.negative else '0',
                      operator=condition.operator.name)

        operator_arguments = [condition.operator.variables[arg]
                              for arg in condition.operator.arguments]
        fields['operator_arguments'] = operator_arguments

        self.conditions.append_entry(fields)


from django.contrib import admin

# Register your models here.
from django.http import JsonResponse

from components.models import *
from simpleui.admin import AjaxAdmin

@admin.register(CharModel)
class CharModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8')

@admin.register(MeditorModel)
class MeditorAdmin(admin.ModelAdmin):
    list_display = ('pk', 'md')

@admin.register(UeditorModel)
class UeditorModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'html', 'description')

@admin.register(RadioModel)
class RadioModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f')

@admin.register(CheckBoxModelTest)
class CheckBoxModelTestAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f')

@admin.register(SwitchModel)
class SwitchModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f')

@admin.register(InputNumberModel)
class InputNumberModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f')

@admin.register(SliderModel)
class SliderModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f1', 'f2', 'f3')

@admin.register(ImageModel)
class ImageModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f1', 'f2')

@admin.register(RateModel)
class RateModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f1', 'f2', 'f3')

@admin.register(TimeModel)
class TimeModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f1', 'f2', 'f3')

@admin.register(DateModel)
class DateModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f1', 'f2', 'f3')

@admin.register(DateTimeModel)
class DateTimeModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'f1', 'f2', 'f3')

@admin.register(StudentClasses)
class StudentClassesAdmin(admin.ModelAdmin):
    search_fields = ('pk',)
    pass

@admin.register(StudentOneToOneModel)
class StudentOneToOneModelAdmin(admin.ModelAdmin):
    search_fields = ('f',)

@admin.register(StudentArea)
class StudentAreaAdmin(admin.ModelAdmin):
    pass

@admin.register(StudentManyToManyModel)
class StudentManyToManyModelAdmin(admin.ModelAdmin):
    search_fields = ('f',)

@admin.register(StudentModel)
class StudentModelAdmin(admin.ModelAdmin):
    search_fields = ('name',)
    list_filter = ('classes', 'sex',)
    autocomplete_fields = ('classes',)
    list_display = ('pk', 'name', 'sex', 'star', 'money', 'score', 'classes')

# 一对一

@admin.register(OneToOneModel)
class OneToOneModelAdmin(admin.ModelAdmin):
    # 可以开启远程搜索，要指定对象在admin中的search_fields
    autocomplete_fields = ('one_to_one',)

# 穿梭框
@admin.register(TransferManyToManyModel)
class StudentTransferModelAdmin(admin.ModelAdmin):
    search_fields = ('pk',)

@admin.register(TransferModel)
class TransferModelAdmin(admin.ModelAdmin):
    pass

# 多对多
@admin.register(ManyToManyModel)
class ManyToManyModelAdmin(admin.ModelAdmin):
    # 多对多也是可以开启 autocomplete_fields 的
    autocomplete_fields = ('many_to_many',)

    list_display = ('id', 'name', 'many_to_many_display')

# Intger字段
@admin.register(IntegerModel)
class IntegerModelAdmin(admin.ModelAdmin):
    pass

@admin.register(Layer)
class LayerAdmin(AjaxAdmin):
    actions = ('layer_input', 'upload_file')

    def layer_input(self, request, queryset):
        # 这里的queryset 会有数据过滤，只包含选中的数据

        post = request.POST
        # 这里获取到数据后，可以做些业务处理
        # post中的_action 是方法名
        # post中 _selected 是选中的数据，逗号分割
        if not post.get('_selected'):
            return JsonResponse(data={
                'status': 'error',
                'msg': '请先选中数据！'
            })
        else:
            return JsonResponse(data={
                'status': 'success',
                'msg': '处理成功！'
            })

    layer_input.short_description = '弹出对话框输入'
    layer_input.type = 'success'
    layer_input.icon = 'el-icon-s-promotion'
    layer_input.enable = True
    # 指定一个输入参数，应该是一个数组

    # 指定为弹出层，这个参数最关键
    layer_input.layer = {
        # 弹出层中的输入框配置

        # 这里指定对话框的标题
        'title': '弹出层输入框',
        # 提示信息
        'tips': '这个弹出对话框是需要在admin中进行定义，数据新增编辑等功能，需要自己来实现。',
        # 确认按钮显示文本
        'confirm_button': '确认提交',
        # 取消按钮显示文本
        'cancel_button': '取消',

        # 弹出层对话框的宽度，默认50%
        'width': '40%',

        # 表单中 label的宽度，对应element-ui的 label-width，默认80px
        'labelWidth': "80px",
        'params': [{
            # 这里的type 对应el-input的原生input属性，默认为input
            'type': 'input',
            # key 对应post参数中的key
            'key': 'name',
            # 显示的文本
            'label': '名称',
            # 为空校验，默认为False
            'require': True
        }, {
            'type': 'select',
            'key': 'type',
            'label': '类型',
            'width': '200px',
            # size对应elementui的size，取值为：medium / small / mini
            'size': 'small',
            # value字段可以指定默认值
            'value': '0',
            'options': [{
                'key': '0',
                'label': '收入'
            }, {
                'key': '1',
                'label': '支出'
            }]
        }, {
            'type': 'number',
            'key': 'money',
            'label': '金额',
            # 设置默认值
            'value': 1000
        }, {
            'type': 'date',
            'key': 'date',
            'label': '日期',
        }, {
            'type': 'datetime',
            'key': 'datetime',
            'label': '时间',
        }, {
            'type': 'rate',
            'key': 'star',
            'label': '评价等级'
        }, {
            'type': 'color',
            'key': 'color',
            'label': '颜色'
        }, {
            'type': 'slider',
            'key': 'slider',
            'label': '滑块'
        }, {
            'type': 'switch',
            'key': 'switch',
            'label': 'switch开关'
        }, {
            'type': 'input_number',
            'key': 'input_number',
            'label': 'input number'
        }, {
            'type': 'checkbox',
            'key': 'checkbox',
            # 必须指定默认值
            'value': [],
            'label': '复选框',
            'options': [{
                'key': '0',
                'label': '收入'
            }, {
                'key': '1',
                'label': '支出'
            }, {
                'key': '2',
                'label': '收益'
            }]
        }, {
            'type': 'radio',
            'key': 'radio',
            'label': '单选框',
            'options': [{
                'key': '0',
                'label': '收入'
            }, {
                'key': '1',
                'label': '支出'
            }, {
                'key': '2',
                'label': '收益'
            }]
        }]
    }

    def upload_file(self, request, queryset):
        # 这里的upload 就是和params中配置的key一样
        upload = request.FILES['upload']
        print(upload)
        return JsonResponse(data={
            'status': 'success',
            'msg': '处理成功！'
        })

    upload_file.short_description = '文件上传对话框'
    upload_file.type = 'success'
    upload_file.icon = 'el-icon-upload'
    upload_file.enable = True

    upload_file.layer = {
        'params': [{
            'type': 'file',
            'key': 'upload',
            'label': '文件'
        }]
    }

@admin.register(UUIDKeyModel)
class UUIDModelAdmin(admin.ModelAdmin):
    list_display = ('pk', 'name')

@admin.register(AMapModel)
class AMapModelAdmin(admin.ModelAdmin):
    """
    高德地图支持
    """
    list_display = ('pk', 'name', 'geo', 'address')


class Solution:
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        from bisect import bisect_left
        if not nums: return -1
        first = nums[0]
        if target == first:
            return 0
        l, r = 0, len(nums)
        while l < r:
            m = (l+r) // 2
            if nums[m-1] > first > nums[m]:
                break
            elif nums[m] > first:
                l = m + 1
                # print(l)
            elif nums[m] < first:
                # print("r")
                r = m
            else:
                m = m + 1
                break
        if target < first:
            i = bisect_left(nums[m:], target) + m
        else:
            i = bisect_left(nums[:m], target)
        if i < len(nums) and nums[i] == target:
            return i
        return -1
    # https://leetcode.com/problems/search-in-rotated-sorted-array/discuss/14435/Clever-idea-making-it-simple
    # great idea
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        if not nums: return -1
        l, r, f = 0, len(nums), nums[0]
        while l < r:
            m = (l+r) // 2
            v = nums[m]
            if not(nums[m] < f and target < f):
                v = float("inf")
                if target < f:
                    v = -float("inf")

            if v == target:
                return m
            elif v < target:
                l = m + 1
            else:
                r = m
        return -1

if __name__ == '__main__':
    from minitest import *

    with test(Solution):
        Solution().search([4,5,6,7,0,1,2], 0).must_equal(4)
        Solution().search([1], 0).must_equal(-1)


for n in range(1, 101):
    fizz = n % 3 == 0
    buzz = n % 5 == 0

    output = ''
    if fizz and buzz:
        output = 'FizzBuzz'
    elif fizz:
        output = 'Fizz'
    elif buzz:
        output = 'Buzz'

    print(output or n)


import numpy as np
import scipy.constants as sp
from math import pi, sin, exp

class Mesh:
    def __init__(self, ncells, ddx, epsilon_r, sigma, start_m, end_m):
        self.ncells=ncells
        self.ddx=ddx
        self.epsilon_r= epsilon_r
        self.sigma= sigma
        self.start_m= start_m
        self.end_m= end_m

    def material(self, k):
        self.k=k

        dt=self.ddx / sp.c

        ca = np.ones(self.ncells+1)
        cb = np.ones(self.ncells+1) * 0.5

        eaf = dt * self.sigma / (2 * sp.epsilon_0 * self.epsilon_r)
        ca[self.start_m : self.end_m] = (1 - eaf ) / (1 + eaf )
        cb[self.start_m : self.end_m] = 0.5 / (self.epsilon_r * (1 + eaf ))

        return ca[k], cb[k]

    def boundarymur(self, ex, time_step):
        self.ex=ex
        self.time_step=time_step

        if time_step >= 2:
                ex[time_step][0]=ex[time_step-2][1]
                ex[time_step][self.ncells]=ex[time_step-2][self.ncells-1]

        return ex

class Source:
    def __init__(self, sourcetype, t_0, s_0):
        self.sourcetype=sourcetype
        self.t_0=t_0
        self.s_0=s_0

    def pulse(self, time_step):

        self.time_step=time_step

        if self.sourcetype == 'gauss':
            pulse = exp(-0.5*( (self.t_0 - time_step) / self.s_0 )**2)

        return pulse


"""
This file defines actions, i.e. functions the URLs are mapped into
The @action(path) decorator exposed the function at URL:

    http://127.0.0.1:8000/{app_name}/{path}

If app_name == '_default' then simply

    http://127.0.0.1:8000/{path}

If path == 'index' it can be omitted:

    http://127.0.0.1:8000/

The path follows the bottlepy syntax.

@action.uses('generic.html')  indicates that the action uses the generic.html template
@action.uses(session)         indicates that the action uses the session
@action.uses(db)              indicates that the action uses the db
@action.uses(T)               indicates that the action uses the i18n & pluralization
@action.uses(auth.user)       indicates that the action requires a logged in user
@action.uses(auth)            indicates that the action requires the auth object

session, db, T, auth, and tempates are examples of Fixtures.
Warning: Fixtures MUST be declared with @action.uses({fixtures}) else your app will result in undefined behavior
"""

import uuid

from py4web import action, request, abort, redirect, URL, Field
from py4web.utils.form import Form, FormStyleBulma
from py4web.utils.url_signer import URLSigner
from pydal.validators import *

from yatl.helpers import A
from . common import db, session, T, cache, auth, signed_url

url_signer = URLSigner(session)

# The auth.user below forces login.
@action('index', method='GET')
@action.uses('index.html', auth.user,session,db)
def index():
	#passes user variable to allow for signout button to display while signed in.
	user=auth.current_user
	#query that selects the contacts under the email of the current logged in user
	rows = db(db.person.user_email==user.get('email')).select().as_list()
	for row in rows:

		s=db(db.phone.owner_id==row.get('id')).select(db.phone.phone_number,db.phone.phone_name)

		row["phone_number"]=s

		pass
	return dict(rows=rows, url_signer=url_signer, user=user)

@action('add_contact', method=['GET', 'POST'])
@action.uses('contact_form.html', auth.user, session, db)
def add_contact():
    form = Form(db.person, csrf_session=session, formstyle=FormStyleBulma)
    if form.accepted:
        # We always want POST requests to be redirected as GETs.
        redirect(URL('index'))
    return dict(form=form)

@action('edit_contact/<contact_id>', method=['GET', 'POST'])
@action.uses('contact_form.html', auth.user, session, db)
def edit_contact(contact_id=None):

    p = db.person[contact_id]
    pCreator=p.user_email
    #checks if actually a entry
    if p is None:
        redirect(URL('index'))
    #checks if current user is editing their own contact
    if pCreator!=auth.current_user.get('email'):
    	redirect(URL('index'))

    form = Form(db.person, record=p, deletable=False, csrf_session=session, formstyle=FormStyleBulma)
    if form.accepted:
        # We always want POST requests to be redirected as GETs.
        redirect(URL('index'))
    return dict(form=form)

@action('delete_contact/<contact_id>', method=['GET','POST'])
@action.uses('index.html',auth.user, session, db, url_signer.verify())
def delete_contact(contact_id=None):
    p = db.person[contact_id]
    pCreator=p.user_email
    #check if actually a entry
    if p is None:
        redirect(URL('index'))
    #if user is deleting a contact that isnt theirs
    if pCreator!=auth.current_user.get('email'):
    	redirect(URL('index'))
    else:
        db(db.person.id == contact_id).delete()
        redirect(URL('index'))

@action('edit_phones/<contact_id>', method=['GET', 'POST'])
@action.uses('edit_phones.html', auth.user, session, db)
def edit_phones(contact_id=None):

    #passes user variable to allow for signout button to display while signed in.
	user=auth.current_user
	#pulls data from person table
	contactInfo=db(db.person.id==contact_id).select()

	if auth.current_user.get('email')!=contactInfo[0].user_email:
		redirect(URL('index'))
	#query that selects the contacts under the email of the current logged in user
	rows = db((db.person.user_email==user.get('email')) & (db.person.id==contact_id) & (db.phone.owner_id==contact_id)).select().as_list()

	return dict(rows=rows,contactInfo=contactInfo, url_signer=url_signer, user=user)

@action('add_phone/<contact_id>', method=['GET', 'POST'])
@action.uses('phone_form.html', auth.user, session, db)
def add_phone(contact_id=None):
	contactInfo=db(db.person.id==contact_id).select()

	if auth.current_user.get('email')!=contactInfo[0].user_email:
		redirect(URL('index'))

	form = Form([Field('phone_number', requires=IS_NOT_EMPTY()),Field('phone_name',requires=IS_NOT_EMPTY())],csrf_session=session,formstyle=FormStyleBulma)
	if form.accepted:
		# We always want POST requests to be redirected as GETs.

		db.phone.insert(phone_number=form.vars["phone_number"], phone_name=form.vars["phone_name"], owner_id=contact_id)
		redirect(URL('edit_phones',contact_id))
	return dict(form=form, contactInfo=contactInfo)

@action('edit_phone/<contact_id>/<phone_id>', method=['GET', 'POST'])
@action.uses('phone_form.html', auth.user, session, db)
def edit_phone(contact_id=None, phone_id=None):
	contactInfo=db(db.person.id==contact_id).select()

	if auth.current_user.get('email')!=contactInfo[0].user_email:
		redirect(URL('index'))
	p = db.phone[phone_id]

	form = Form([Field('phone_number',requires=IS_NOT_EMPTY()),Field('phone_name',requires=IS_NOT_EMPTY())],record=dict(phone_number=p.phone_number, phone_name=p.phone_name),deletable=False, csrf_session=session,formstyle=FormStyleBulma)
	if form.accepted:

		#modeled after documentation on update
		myset = db(db.phone.id==phone_id)
		myset.update(phone_number=form.vars["phone_number"], phone_name=form.vars["phone_name"], owner_id=contact_id)
		redirect(URL('edit_phones',contact_id))
	return dict(form=form,contactInfo=contactInfo)

@action('delete_phone/<contact_id>/<phone_id>', method=['GET','POST'])
@action.uses('edit_phones.html',auth.user, session, db, url_signer.verify())
def delete_phone(contact_id=None,phone_id=None):
    p = db.phone[phone_id]
    contactInfo=db(db.person.id==contact_id).select()

    #check if actually a entry
    if p is None:
        redirect(URL('edit_phones',contact_id))
    #if user is deleting a contact that isnt theirs
    if contactInfo[0].user_email!=auth.current_user.get('email'):
    	redirect(URL('edit_phones',contact_id))
    else:
        db(db.phone.id == phone_id).delete()
        redirect(URL('edit_phones',contact_id))


from numpy import array,dot,exp,random

class neural_layer():
    def __init__(self,number_of_neurons,number_inputs_per_neuron):
        self.neuralweights = 2*random.random((number_inputs_per_neuron,number_of_neurons))-1

class  neural_networks():
    def __init__(self,hidden_layer,layer2):
        self.hidden_layer=hidden_layer
        self.layer2=layer2

    def __sigmoid(self,x):
        return (1/(1+exp(-x)))

    def __sigmoidgrad(self,x):
        return (x*(1-x))

    def think(self,inputs):
        neth=array(dot(inputs,self.hidden_layer.neuralweights))
        output_from_hidden_layer=self.__sigmoid(neth)

        neto=array(dot(output_from_hidden_layer,self.layer2.neuralweights))
        output_from_layer_2=self.__sigmoid(neto)

        return output_from_hidden_layer,output_from_layer_2

    def train(self,training_inputs, training_outputs, number_of_iterations):
        for iteration in range(number_of_iterations):
            output_from_hidden_layer,output_from_layer_2 = self.think(training_inputs)
# The following few lines are nothing but calculation of d(Error)/d(Ai) and d(Error)/d(Wi)
            layer2_error=training_outputs-output_from_layer_2

            layer2_delta=layer2_error*self.__sigmoidgrad(output_from_layer_2)

            hidden_layer_error=layer2_delta.dot(self.layer2.neuralweights.T)
            hidden_layer_delta=hidden_layer_error*self.__sigmoidgrad(output_from_hidden_layer)

# Just to reinforce from the theory above, hidden layer adjustment = d(Error)/d(Wi ) =
# (d(Error)/d(Output)) * (d(Output)/d(Neto )) * (d(Neto )/d(Outputh )) * (d(Outputh )/d(Neth )) * (d(Neth )/d(Wi ))
# or d(Error)/d(Wi ) = (Output - Target)*(Output*(1-Output))*Ai *(Outputh *(1-Outputh ))*Xi
# Similarly Layer 2 adjustment = d(Error)/d(Ai ) = -1*(Target-Output) * (Output*(1-Output))*Outputh

            hidden_layer_adjustment=training_inputs.T.dot(hidden_layer_delta)
            layer2_adjustment=output_from_hidden_layer.T.dot(layer2_delta)

            self.hidden_layer.neuralweights+=hidden_layer_adjustment
            self.layer2.neuralweights+=layer2_adjustment

    def print_weights(self):
        print ("Weights for hidden_layer")
        print (self.hidden_layer.neuralweights)
        print ("Weights for Layer 2")
        print ((self.layer2.neuralweights))

if __name__=="__main__":
    random.seed(1)
# Initializing layers
    hidden_layer=neural_layer(4,3)
    layer2=neural_layer(1,4)
    neural_network = neural_networks(hidden_layer,layer2)
# Initializing weights
    print ("Starting weights")
    neural_network.print_weights()
# Intializing input and target output
    training_set_inputs = array([[0, 0, 1], [0, 1, 1], [1, 0, 1], [0, 1, 0], [1, 0, 0], [1, 1, 1], [0, 0, 0]])
    training_set_outputs = array([[1, 1, 1, 0, 0, 1, 0]]).T
# Training begins
    neural_network.train(training_set_inputs,training_set_outputs,100000)
    print ("Ending weights")
    neural_network.print_weights()
# Now for the exam.!
    print ("Output")
    hidden_state,output=neural_network.think(array([0,1,0]))
    print (output)


import logging
import uuid
import time
import argparse
import importlib

from slamon.agent import timeutil
from slamon.agent.executor import Executor
from slamon.agent.communication import TemporaryError
from slamon.agent.communication import Communicator
from slamon.agent.handlers import TaskHandler

class Agent(object):
    """
    Agent class presents an instance of agent application.
    """

    def __init__(self, afm_url, default_wait=60, max_tasks=2, name='Python Agent 1.0', agent_uuid=None):

        self.afm = Communicator(afm_url)
        self.max_tasks = max_tasks
        self.default_wait = default_wait
        self.name = name
        self.uuid = agent_uuid if agent_uuid else str(uuid.uuid1())
        self._run = True

    def exit(self):
        """
        Signal agent to exit. After issuing exit, agent will not make further task requests,
        but will wait until all currently processed tasks finish.
        """
        self._run = False

    def run(self):
        """
        The "main function" of the agent, looping the claim & execute tasks flow.
        """

        with Executor(self.max_tasks) as executor:
            while self._run:

                wait_time = self.default_wait
                min_wait_time = 1

                # request for tasks
                try:
                    task_response = self.afm.request_tasks(
                        agent_id=self.uuid,
                        agent_name=self.name,
                        agent_time=timeutil.format(timeutil.now()),
                        agent_capabilities=TaskHandler.list_all(),
                        max_tasks=executor.available_executors()
                    )
                    if 'tasks' in task_response:
                        for task_data in task_response['tasks']:
                            executor.submit_task(task_data,
                                                 lambda *args, **kwargs: self.afm.post_result(*args, **kwargs))
                    if 'return_time' in task_response:
                        return_time = timeutil.parse(task_response['return_time'])
                        wait_time = max(min_wait_time, (return_time - timeutil.now()).total_seconds())
                except TemporaryError as e:
                    logging.getLogger("Agent").error("An error occurred while claiming tasks: %s", e)

                time.sleep(wait_time)

def _import_module(module_name, package=None):
    """
    Recursively load modules to search for task handlers.
    """
    m = importlib.import_module(module_name, package=package)
    if hasattr(m, '__all__'):
        for sub_module_name in m.__all__:
            _import_module('.' + sub_module_name, module_name)

def main():
    """
    Entry point for the agent script.
    """
    logging.basicConfig(level=logging.DEBUG)

    parser = argparse.ArgumentParser()
    parser.add_argument('-u', '--url', action='store', required=True,
                        help='Coordinator URL')
    parser.add_argument('-l', '--load', action='append',
                        help='Load handlers from specified module')
    parser.add_argument('-w', '--default-wait', type=int, default=60,
                        help='Seconds to wait before reconnection after connection failure.')
    parser.add_argument('-x', '--num-executors', type=int, default=2,
                        help='Number of concurrent task executors.')
    args = parser.parse_args()

    # import defined modules to load defined handlers
    if args.load:
        for module in args.load:
            _import_module(module)

    agent = Agent(
        args.url,
        default_wait=args.default_wait,
        max_tasks=args.num_executors
    )
    agent.run()
    return 0

if __name__ == "__main__":
    main()


#pylint: disable=no-init
from __future__ import (absolute_import, division, print_function)

import mantid.simpleapi as api
from mantid.api import *
from mantid.kernel import *
from mantid import config
from os.path import join as pjoin

DEFAULT_RANGE = [6.24, 6.30]
DEFAULT_MASK_GROUP_DIR = "/SNS/BSS/shared/autoreduce/new_masks_08_12_2015"
DEFAULT_VANADIUM_ENERGY_RANGE = [-0.0034, 0.0034]  # meV
DEFAULT_VANADIUM_BINS = [-0.0034, 0.068, 0.0034]  # meV
DEFAULT_CONFIG_DIR = config["instrumentDefinition.directory"]

# BASIS allows two possible reflections, with associated default properties
#pylint: disable=line-too-long
REFLECTIONS_DICT = {"silicon111": {"name": "silicon111",
                                   "energy_bins": [-150, 0.4, 500],  # micro-eV
                                   "q_bins": [0.3, 0.2, 1.9],  # inverse Angstroms
                                   "mask_file": "BASIS_Mask_ThreeQuartersRemain_SouthTop_NorthTop_NorthBottom_MorePixelsEliminated_08122015.xml",
                                   "parameter_file": "BASIS_silicon_111_Parameters.xml",
                                   "default_energy": 2.0826},  # mili-eV
                    "silicon311": {"name": "silicon311",
                                   "energy_bins": [-740, 1.6, 740],
                                   "q_bins": [0.5, 0.2, 3.7],
                                   "mask_file": "BASIS_Mask_OneQuarterRemains_SouthBottom.xml",
                                   "parameter_file": "BASIS_silicon_311_Parameters.xml",
                                   "default_energy": 7.6368}}

#pylint: disable=too-many-instance-attributes
class BASISReduction(PythonAlgorithm):

    _short_inst = None
    _long_inst = None
    _extension = None
    _doIndiv = None
    _noMonNorm = None
    _groupDetOpt = None
    _overrideMask = None
    _dMask = None
    _run_list = None  # a list of runs, or a list of sets of runs
    _samWs = None
    _samMonWs = None
    _samWsRun = None
    _samSqwWs = None

    def __init__(self):
        PythonAlgorithm.__init__(self)
        self._normalizeToFirst = False

        # properties related to the chosen reflection
        self._reflection = None  # entry in the reflections dictionary
        self._etBins = None
        self._qBins = None
        self._maskFile = None

        # properties related to division by Vanadium (normalization)
        self._doNorm = None  # stores the selected item from normalization_types
        self._normalizationType = None
        self._normRange = None
        self._norm_run_list = None
        self._normWs = None
        self._normMonWs = None

    def category(self):
        return "Inelastic\\Reduction"

    def name(self):
        return "BASISReduction"

    def version(self):
        return 1

    def summary(self):
        return "Multiple-file BASIS reduction for its two reflections."

    def PyInit(self):
        self._short_inst = "BSS"
        self._long_inst = "BASIS"
        self._extension = "_event.nxs"

        self.declareProperty("RunNumbers", "", "Sample run numbers")
        self.declareProperty("DoIndividual", False, "Do each run individually")
        self.declareProperty("NoMonitorNorm", False,
                             "Stop monitor normalization")
        grouping_type = ["None", "Low-Resolution", "By-Tube"]
        self.declareProperty("GroupDetectors", "None",
                             StringListValidator(grouping_type),
                             "Switch for grouping detectors")
        self.declareProperty("NormalizeToFirst", False, "Normalize spectra to intensity of spectrum with lowest Q?")

        # Properties affected by the reflection selected
        titleReflection = "Reflection Selector"
        available_reflections = list(REFLECTIONS_DICT.keys())
        available_reflections.sort()  # preserve order in which they are presented
        default_reflection = REFLECTIONS_DICT["silicon111"]
        self.declareProperty("ReflectionType", default_reflection["name"],
                             StringListValidator(available_reflections),
                             "Analyzer. Documentation lists typical associated property values.")
        self.setPropertyGroup("ReflectionType", titleReflection)
        self.declareProperty(FloatArrayProperty("EnergyBins",
                                                default_reflection["energy_bins"],
                                                direction=Direction.Input),
                             "Energy transfer binning scheme (in ueV)")
        self.setPropertyGroup("EnergyBins", titleReflection)
        self.declareProperty(FloatArrayProperty("MomentumTransferBins",
                                                default_reflection["q_bins"],
                                                direction=Direction.Input),
                             "Momentum transfer binning scheme")
        self.setPropertyGroup("MomentumTransferBins", titleReflection)
        self.declareProperty(FileProperty(name="MaskFile",
                                          defaultValue=pjoin(DEFAULT_MASK_GROUP_DIR,
                                                             default_reflection["mask_file"]),
                                          action=FileAction.OptionalLoad, extensions=['.xml']),
                             "See documentation for latest mask files.")
        self.setPropertyGroup("MaskFile", titleReflection)

        # Properties setting the division by vanadium
        titleDivideByVanadium = "Normalization by Vanadium"
        self.declareProperty("DivideByVanadium", False, direction=Direction.Input,
                             doc="Do we normalize by the vanadium intensity?")
        self.setPropertyGroup("DivideByVanadium", titleDivideByVanadium)
        ifDivideByVanadium = EnabledWhenProperty("DivideByVanadium",
                                                 PropertyCriterion.IsNotDefault)

        normalization_types = ["by Q slice", "by detector ID"]
        self.declareProperty("NormalizationType", "by Q slice",
                             StringListValidator(normalization_types),
                             "Select a Vanadium normalization")
        self.setPropertySettings("NormalizationType", ifDivideByVanadium)
        self.setPropertyGroup("NormalizationType", titleDivideByVanadium)

        self.declareProperty("NormRunNumbers", "", "Normalization run numbers")
        self.setPropertySettings("NormRunNumbers", ifDivideByVanadium)
        self.setPropertyGroup("NormRunNumbers", titleDivideByVanadium)
        arrVal = FloatArrayLengthValidator(2)
        self.declareProperty(FloatArrayProperty("NormWavelengthRange", DEFAULT_RANGE,
                                                arrVal, direction=Direction.Input),
                             "Wavelength range for normalization")
        self.setPropertySettings("NormWavelengthRange", ifDivideByVanadium)
        self.setPropertyGroup("NormWavelengthRange", titleDivideByVanadium)

    def PyExec(self):
        config['default.facility'] = "SNS"
        config['default.instrument'] = self._long_inst
        self._reflection = REFLECTIONS_DICT[self.getProperty("ReflectionType").value]
        self._doIndiv = self.getProperty("DoIndividual").value
        self._etBins = 1.E-03 * self.getProperty("EnergyBins").value  # micro-eV to mili-eV
        self._qBins = self.getProperty("MomentumTransferBins").value
        self._qBins[0] -= self._qBins[1]/2.0  # self._qBins[0] is leftmost bin boundary
        self._qBins[2] += self._qBins[1]/2.0  # self._qBins[2] is rightmost bin boundary
        self._noMonNorm = self.getProperty("NoMonitorNorm").value
        self._maskFile = self.getProperty("MaskFile").value
        self._groupDetOpt = self.getProperty("GroupDetectors").value
        self._normalizeToFirst = self.getProperty("NormalizeToFirst").value
        self._doNorm = self.getProperty("DivideByVanadium").value

        datasearch = config["datasearch.searcharchive"]
        if datasearch != "On":
            config["datasearch.searcharchive"] = "On"

        # Apply default mask if not supplied by user
        self._overrideMask = bool(self._maskFile)
        if not self._overrideMask:
            config.appendDataSearchDir(DEFAULT_MASK_GROUP_DIR)
            self._maskFile = self._reflection["mask_file"]

        api.LoadMask(Instrument='BASIS', OutputWorkspace='BASIS_MASK',
                     InputFile=self._maskFile)

        # Work around length issue
        _dMask = api.ExtractMask('BASIS_MASK')
        self._dMask = _dMask[1]
        api.DeleteWorkspace(_dMask[0])

        ############################
        ##  Process the Vanadium  ##
        ############################

        norm_runs = self.getProperty("NormRunNumbers").value
        if self._doNorm and bool(norm_runs):
            if ";" in norm_runs:
                raise SyntaxError("Normalization does not support run groups")
            self._normalizationType = self.getProperty("NormalizationType").value
            self.log().information("Divide by Vanadium with normalization" + self._normalizationType)

            # The following steps are common to all types of Vanadium normalization

            # norm_runs encompasses a single set, thus _getRuns returns
            # a list of only one item
            norm_set = self._getRuns(norm_runs, doIndiv=False)[0]
            self._normWs = self._sum_and_calibrate(norm_set, extra_extension="_norm")

            # This rebin integrates counts onto a histogram of a single bin
            if self._normalizationType == "by detectorID":
                normRange = self.getProperty("NormWavelengthRange").value
                self._normRange = [normRange[0], normRange[1]-normRange[0], normRange[1]]
                api.Rebin(InputWorkspace=self._normWs, OutputWorkspace=self._normWs,
                          Params=self._normRange)

            # FindDetectorsOutsideLimits to be substituted by MedianDetectorTest
            api.FindDetectorsOutsideLimits(InputWorkspace=self._normWs,
                                           OutputWorkspace="BASIS_NORM_MASK")

            # additional reduction steps when normalizing by Q slice
            if self._normalizationType == "by Q slice":
                self._normWs = self._group_and_SofQW(self._normWs,
                                                     DEFAULT_VANADIUM_BINS,
                                                     isSample=False)

        ##########################
        ##  Process the sample  ##
        ##########################
        self._run_list = self._getRuns(self.getProperty("RunNumbers").value,
                                       doIndiv=self._doIndiv)
        for run_set in self._run_list:
            self._samWs = self._sum_and_calibrate(run_set)
            self._samWsRun = str(run_set[0])
            # Mask detectors with insufficient Vanadium signal
            if self._doNorm:
                api.MaskDetectors(Workspace=self._samWs,
                                  MaskedWorkspace='BASIS_NORM_MASK')
            # Divide by Vanadium
            if self._normalizationType == "by detector ID":
                api.Divide(LHSWorkspace=self._samWs, RHSWorkspace=self._normWs,
                           OutputWorkspace=self._samWs)
            # additional reduction steps
            self._samSqwWs = self._group_and_SofQW(self._samWs, self._etBins, isSample=True)
            # Divide by Vanadium
            if self._normalizationType == "by Q slice":
                api.Divide(LHSWorkspace=self._samSqwWs, RHSWorkspace=self._normWs,
                           OutputWorkspace=self._samSqwWs)
            # Clear mask from reduced file. Needed for binary operations
            # involving this S(Q,w)
            api.ClearMaskFlag(Workspace=self._samSqwWs)
            # Scale so that elastic line has Y-values ~ 1
            if self._normalizeToFirst:
                self._ScaleY(self._samSqwWs)
            # Output Dave and Nexus files
            extension = "_divided.dat" if self._doNorm else ".dat"
            dave_grp_filename = self._makeRunName(self._samWsRun,
                                                  False) + extension
            api.SaveDaveGrp(Filename=dave_grp_filename,
                            InputWorkspace=self._samSqwWs,
                            ToMicroEV=True)
            extension = "_divided_sqw.nxs" if self._doNorm else "_sqw.nxs"
            processed_filename = self._makeRunName(self._samWsRun,
                                                   False) + extension
            api.SaveNexus(Filename=processed_filename,
                          InputWorkspace=self._samSqwWs)

    def _getRuns(self, rlist, doIndiv=True):
        """
        Create sets of run numbers for analysis. A semicolon indicates a
        separate group of runs to be processed together.
        @param rlist: string containing all the run numbers to be reduced.
        @return if _doIndiv is False, return a list of IntArrayProperty objects.
         Each item is a pseudolist containing a set of runs to be reduced together.
         if _doIndiv is True, return a list of strings, each string is a run number.
        """
        run_list = []
        # ";" separates the runs into substrings. Each substring represents a set of runs
        rlvals = rlist.split(';')
        for rlval in rlvals:
            iap = IntArrayProperty("", rlval)  # split the substring
            if doIndiv:
                run_list.extend([[x] for x in iap.value])
            else:
                run_list.append(iap.value)
        return run_list

    def _makeRunName(self, run, useShort=True):
        """
        Make name like BSS_24234
        """
        if useShort:
            return self._short_inst + "_" + str(run)
        else:
            return self._long_inst + "_" + str(run)

    def _makeRunFile(self, run):
        """
        Make name like BSS24234
        """
        return self._short_inst + str(run)

    def _sumRuns(self, run_set, sam_ws, mon_ws, extra_ext=None):
        """
        Aggregate the set of runs
        @param run_set: list of run numbers
        @param sam_ws:  name of aggregate workspace for the sample
        @param mon_ws:  name of aggregate workspace for the monitors
        @param extra_ext: string to be added to the temporary workspaces
        """
        for run in run_set:
            ws_name = self._makeRunName(run)
            if extra_ext is not None:
                ws_name += extra_ext
            mon_ws_name = ws_name + "_monitors"
            run_file = self._makeRunFile(run)

            # Faster loading for the 311 reflection
            if self._reflection["name"] == "silicon311":
                kwargs = {"BankName": "bank2"}  # 311 analyzers only in bank2
            else:
                kwargs = {}
            api.LoadEventNexus(Filename=run_file, OutputWorkspace=ws_name, **kwargs)

            if not self._noMonNorm:
                api.LoadNexusMonitors(Filename=run_file,
                                      OutputWorkspace=mon_ws_name)
            if sam_ws != ws_name:
                api.Plus(LHSWorkspace=sam_ws, RHSWorkspace=ws_name,
                         OutputWorkspace=sam_ws)
                api.DeleteWorkspace(ws_name)
            if mon_ws != mon_ws_name and not self._noMonNorm:
                api.Plus(LHSWorkspace=mon_ws,
                         RHSWorkspace=mon_ws_name,
                         OutputWorkspace=mon_ws)
                api.DeleteWorkspace(mon_ws_name)

    def _calibData(self, sam_ws, mon_ws):
        api.MaskDetectors(Workspace=sam_ws,
                          DetectorList=self._dMask)
                          #MaskedWorkspace='BASIS_MASK')
        api.ModeratorTzeroLinear(InputWorkspace=sam_ws,\
                           OutputWorkspace=sam_ws)
        api.LoadParameterFile(Workspace=sam_ws,
                              Filename=pjoin(DEFAULT_CONFIG_DIR,
                                             self._reflection["parameter_file"]))
        api.ConvertUnits(InputWorkspace=sam_ws,
                         OutputWorkspace=sam_ws,
                         Target='Wavelength', EMode='Indirect')

        if not self._noMonNorm:
            api.ModeratorTzeroLinear(InputWorkspace=mon_ws,\
                               OutputWorkspace=mon_ws)
            api.Rebin(InputWorkspace=mon_ws,
                      OutputWorkspace=mon_ws, Params='10')
            api.ConvertUnits(InputWorkspace=mon_ws,
                             OutputWorkspace=mon_ws,
                             Target='Wavelength')
            api.OneMinusExponentialCor(InputWorkspace=mon_ws,
                                       OutputWorkspace=mon_ws,
                                       C='0.20749999999999999',
                                       C1='0.001276')
            api.Scale(InputWorkspace=mon_ws,
                      OutputWorkspace=mon_ws,
                      Factor='9.9999999999999995e-07')
            api.RebinToWorkspace(WorkspaceToRebin=sam_ws,
                                 WorkspaceToMatch=mon_ws,
                                 OutputWorkspace=sam_ws)
            api.Divide(LHSWorkspace=sam_ws,
                       RHSWorkspace=mon_ws,
                       OutputWorkspace=sam_ws)

    def _sum_and_calibrate(self, run_set, extra_extension=""):
        """
        Aggregate the set of runs and calibrate
        @param run_set: list of run numbers
        @param extra_extension: string to be added to the workspace names
        @return: workspace name of the aggregated and calibrated data
        """
        wsName = self._makeRunName(run_set[0])
        wsName += extra_extension
        wsMonName = wsName + "_monitors"
        self._sumRuns(run_set, wsName, wsMonName, extra_extension)
        self._calibData(wsName, wsMonName)
        return wsName

    def _group_and_SofQW(self, wsName, etRebins, isSample=True):
        """ Transforms from wavelength and detector ID to S(Q,E)
        @param wsName: workspace as a function of wavelength and detector id
        @param etRebins: final energy domain and bin width
        @param isSample: discriminates between sample and vanadium
        @return: S(Q,E)
        """
        api.ConvertUnits(InputWorkspace=wsName,
                         OutputWorkspace=wsName,
                         Target='DeltaE', EMode='Indirect')
        api.CorrectKiKf(InputWorkspace=wsName,
                        OutputWorkspace=wsName,
                        EMode='Indirect')
        api.Rebin(InputWorkspace=wsName,
                  OutputWorkspace=wsName,
                  Params=etRebins)
        if self._groupDetOpt != "None":
            if self._groupDetOpt == "Low-Resolution":
                grp_file = "BASIS_Grouping_LR.xml"
            else:
                grp_file = "BASIS_Grouping.xml"
            # If mask override used, we need to add default grouping file
            # location to search paths
            if self._overrideMask:
                config.appendDataSearchDir(DEFAULT_MASK_GROUP_DIR)
                api.GroupDetectors(InputWorkspace=wsName,
                                   OutputWorkspace=wsName,
                                   MapFile=grp_file, Behaviour="Sum")
        wsSqwName = wsName+'_divided_sqw' if isSample and self._doNorm else wsName+'_sqw'
        api.SofQW3(InputWorkspace=wsName,
                   OutputWorkspace=wsSqwName,
                   QAxisBinning=self._qBins, EMode='Indirect',
                   EFixed=self._reflection["default_energy"])
        return wsSqwName

    def _ScaleY(self, wsName):
        """
        Scale all spectra by a number so that the maximum of the first spectra
        is rescaled to 1
        @param wsName: name of the workspace to rescale
        """
        workspace = mtd[wsName]
        maximumYvalue = workspace.dataY(0).max()
        api.Scale(InputWorkspace=wsName,
                  OutputWorkspace=wsName,
                  Factor=1./maximumYvalue, Operation="Multiply",)

# Register algorithm with Mantid.
AlgorithmFactory.subscribe(BASISReduction)