# -*- coding: utf-8 -*-
import telebot
from telebot import types
import sqlite3
import io #для работы с битовыми данными в памяти
import docx # для работы с Word
import datetime # для работы с временем
import SQLfunctions # подключаем sqlite функции
from PIL import Image, ImageDraw, ImageFont # библиотека по работе с изображениями, добавление текста, шрифты

conn = sqlite3.connect('running.db')# подключаем sqlite

# курсор для работы с таблицами
cursor = conn.cursor()

try:
    # sql запрос на создание таблицы
    query = "CREATE TABLE \"runningData\" (\"ID\" INTEGER UNIQUE, \"user_id\" INTEGER, \"run\" TEXT, PRIMARY KEY (\"ID\"))"
    cursor.execute(query)
except:
    pass

try:
    query1 = "CREATE TABLE \"rewardData\" (\"ID\" INTEGER UNIQUE, \"user_id\" INTEGER, \"reward\" TEXT, PRIMARY KEY (\"ID\"))"
    cursor.execute(query1)
except:
    pass

# подключим токен нашего бота
bot = telebot.TeleBot(SQLfunctions.token)

# напишем, что делать нашему боту при команде старт
@bot.message_handler(commands=['start'])
def send_keyboard(message, text="Привет, я HSE_Running Bot! Твой персональный помощник для учета пробежек! \n"
                                "Записывай пробежки в формате:\n"
                                'Дистанция (км), Время (мин), Локация, Дата (дд.мм.гггг)\n\n'
                                'Пример:\n2 км, 10 мин, Москва, 12.10.2021 \n\n'
                                'Ты также можешь записать в пробежку только дистанцию и время пробежки.\n\n'
                                'Пример:\n2 км, 10 мин\n'
                                'Но в таком случае я не смогу учесть такие пробежки при показе активности за последний месяц или '
                                'при показе пробежек за определенный период.\n\n'
                                "Если у тебя уже есть записи пробежек в файлах, можешь загрузить их сюда выбрав 'Импорт записей'"):
    keyboard = types.ReplyKeyboardMarkup(row_width=2)
    itembtn1 = types.KeyboardButton('Добавить пробежку')
    itembtn2 = types.KeyboardButton('Удалить пробежку')
    itembtn3 = types.KeyboardButton('Показать все пробежки')
    itembtn4 = types.KeyboardButton('Показать пробежки за период')
    itembtn5 = types.KeyboardButton('Показать активность за месяц')
    itembtn6 = types.KeyboardButton('Импорт записей')  # загрузить записи из файла
    itembtn7 = types.KeyboardButton('Награды')
    itembtn8 = types.KeyboardButton('Пока все!')
    itembtn9 = types.KeyboardButton('Очистить runningData')
    itembtn10 = types.KeyboardButton('Очистить rewardData')

    # keyboard.add(itembtn1, itembtn2, itembtn3, itembtn4, itembtn5, itembtn6, itembtn7, itembtn8, itembtn9, itembtn10)
    keyboard.add(itembtn1, itembtn2, itembtn6, itembtn3, itembtn4, itembtn5, itembtn7, itembtn8, itembtn9, itembtn10)
    # пришлем это все сообщением и запишем выбранный вариант
    msg = bot.send_message(message.from_user.id, text=text, reply_markup=keyboard)

    # отправим этот вариант в функцию, которая его обработает
    bot.register_next_step_handler(msg, callback_worker)

#--------------------------------
# Теперь напишем функции для нашего бота

# функция "Добавить пробежку"
def add_run(msg):
    flag_marathon = 0      # переменная, которая нужна для отслеживания, пробежал ли пользователь марафон
    flag_half_marathon = 0 # переменная, которая нужна для отслеживания, пробежал ли пользователь полумарафон
    if (msg.text != 'Назад'):
        try:
            curDist = float(str(msg.text).split(',')[0].split()[0].lstrip('(').lstrip("'"))
            if curDist >= 0:
                if ',' in str(msg.text).rstrip(')').rstrip(','):
                    if curDist == 42:
                        flag_marathon = 1
                    elif curDist == 21:
                        flag_half_marathon = 1
                SQLfunctions.insertRunIntoBD(msg, msg.text)
                runText = msg.text # сохраним текущую пробежку в переменную, потом передадим ее в функцию memoryPhoto
                #-----
                bot.send_message(msg.chat.id, 'Вау, это надо зафиксировать :)')
                reward(msg, flag_marathon, flag_half_marathon) # вызываем функцию учета наград
                markup = types.ReplyKeyboardMarkup()
                markup.add(types.KeyboardButton('Да'))
                markup.add(types.KeyboardButton('Нет'))
                msg = bot.send_message(msg.from_user.id, text='Сделаем памятную фотку?', reply_markup=markup)
                bot.register_next_step_handler(msg, memoryPhoto, runText)  # переходим к функции memory photo
            else:
                bot.send_message(msg.chat.id, "Ваша дистанция - отрицательное число. Извините, я не могу добавить эту пробежку в список.")
                send_keyboard(msg, "Чем еще могу помочь?")
        except:
            bot.send_message(msg.chat.id, 'Вы ввели пробежку в неправильном формате! Извините, я не могу её добавить в список')
            send_keyboard(msg, "Чем еще могу помочь?")
    else:
        send_keyboard(msg, "Хорошо, отменяем. Чем еще могу помочь?")

# функция для выбора сделать фото или нет
def memoryPhoto(msg, runText):
    # bot.send_message(msg.chat.id, 'Попали в функцию memoryPhoto')
    if (msg.text == 'Да'):
        bot.send_message(msg.chat.id, 'Хорошо, скинь фотку... ')
        bot.register_next_step_handler(msg, image_open, runText)
        #send_keyboard(msg, 'Могу помочь чем-то еще?')
    else:
        bot.send_message(msg.chat.id, 'Нет так нет')
        send_keyboard(msg, 'Могу помочь чем-то еще?')

# реализуем функцию "Обработки изображений"
@bot.message_handler(content_types=['photo'])
def image_open(msg, runText):
    if (msg.text != 'Назад'):
        try:
            file_info = bot.get_file(msg.photo[len(msg.photo) - 1].file_id)
            downloaded_file = bot.download_file(file_info.file_path) #сохраняем фото с сервера телеграмм
            fp = io.BytesIO(downloaded_file) # декодируем изображение из байт
            img = Image.open(fp) # откроем изображение с библиотекой Pillow
            font = ImageFont.load_default() # стандартный шрифт
            text = str(runText)[-10:].encode('cp1251')#.encode('UTF-8') #добавляемый шрифт
            draw_text = ImageDraw.Draw(img) # добавим текст на изображение
            draw_text.text((10,10), text, font=font, fill=('#58F9F6')) # параметры размещения текста на изображении
            #img.save('test.jpg')
            bot.send_photo(msg.chat.id, img)
            bot.send_message(msg.chat.id, "Ваше фото готово!")
            send_keyboard(msg, 'Могу помочь чем-то еще?')
        except Exception as e:
            bot.reply_to(msg, e)

    else:
        bot.send_message(msg, "Хорошо, отменяем. Чем еще могу помочь?")

# функция "Импорт из файла"
# теперь напишем функцию "Импорт записей" для загрузки записей из файлов txt и docx
# Это функция красивых строк для текстовых файлов(docx)
def get_document_string(documentList):
    documentList_str1 = [line.rstrip() for line in documentList]
    documentList_str2 = [str(val) for val in
                    documentList_str1]
    return '\n'.join(documentList_str2)

# Это функция красивых строк для текстовых файлов(txt)
def get_document_string_txt(documentList):
    documentList_str = [str(val) for val in
                    documentList]
    return '\n'.join(documentList_str)

# функция записи пробежек из файла
@bot.message_handler(content_types=['document'])
def import_run(msg):
    flag_marathon = 0
    flag_half_marathon = 0
    text = []
    if (msg.text != 'Назад'):
        bot.send_message(msg.chat.id, "Вы загрузили документ")
        try:
            chat_id = msg.chat.id
            file_info = bot.get_file(msg.document.file_id) #получаем информацию о файле на сервере telegram
            downloaded_file = bot.download_file(file_info.file_path) #записываем в память битовое представление файла
            end_file_txt = msg.document.file_name.endswith('.txt') #расширение файла txt
            end_file_docx = msg.document.file_name.endswith('.docx') #расширение файла docx
            if (end_file_txt == True) or (end_file_docx == True):

                if end_file_txt == True:
                    text = downloaded_file.decode('utf-8').split('\n')
                    doc_str = get_document_string_txt(text)
                    bot.send_message(msg.chat.id, 'Я сохраню эти записи:')
                    bot.send_message(msg.chat.id, doc_str)

                elif end_file_docx == True:
                     doc = docx.Document(io.BytesIO(downloaded_file))
                     text = [paragraph.text for paragraph in doc.paragraphs]
                     doc_str = get_document_string(text)
                     bot.send_message(msg.chat.id, 'Я сохраню эти записи:')
                     bot.send_message(msg.chat.id, doc_str)

            else: #если расширение другое, бот отправит стикер sorry
                bot.send_sticker(msg.chat.id, 'CAACAgIAAxkBAAIKJGFrNvGpUtjbltZpXQNpetpU1FC6AAI_AAOtZbwUhPhHiSSyKyAhBA')
        except Exception as e:
            bot.reply_to(msg, e)

        record_run = [line.rstrip() for line in text] #запишем построчно данные из файла в БД
        for i in range(len(record_run)):
            try:
                curDist = float(str(record_run[i]).split(',')[0].split()[0].lstrip('(').lstrip("'"))
                if curDist >= 0:
                    if ',' in str(record_run[i]):
                        if curDist == 42:
                            flag_marathon = 1
                        elif curDist == 21:
                            flag_half_marathon = 1
                    SQLfunctions.insertRunIntoBD(msg, record_run[i])
                else:
                    bot.send_message(msg.chat.id,
                                     "Ваша дистанция - отрицательное число. Извините, я не могу добавить следующую пробежку в список:")
                    bot.send_message(msg.chat.id, record_run[i])
            except:
                bot.send_message(msg.chat.id,
                                 'Вы ввели пробежку в неправильном формате! Извините, я не могу добавить следующую пробежку в список:')
                bot.send_message(msg.chat.id, record_run[i])

        reward(msg, flag_marathon, flag_half_marathon)
        bot.send_message(msg.chat.id, 'Я всё зафиксировал :)')
        send_keyboard(msg, 'Могу помочь чем-то еще?')
    else:
        send_keyboard(msg, "Хорошо, отменяем. Чем еще могу помочь?")

# реализуем теперь функцию "Показать все пробежки"
# для этого сначала напишем функцию get_runs_string, которая делает красивые строки и отправляет их пользователю
def get_runs_string(runsList):
    runsList_str = []
    for val in list(enumerate(runsList)):
        runsList_str.append(str(val[0] + 1) + ') ' + val[1][0] + '\n')
    return ''.join(runsList_str)

# теперь отправляем пользователю список пробежек
def showAllRuns(msg):
    runsList = get_runs_string(SQLfunctions.selectRunsfromBD(msg))
    bot.send_message(msg.chat.id, runsList)
    send_keyboard(msg, "Чем еще могу помочь?")

# реализуем функцию "Удалить пробежку"
# для этого сначала пользователь должен выбрать пробежку, которую он хочет удалить
def chooseToDelete(msg):
    markup = types.ReplyKeyboardMarkup()
    runsList = SQLfunctions.selectRunsfromBD(msg)
    if len(runsList) != 0:
        for val in runsList:
            markup.add(types.KeyboardButton(val[0]))
        msg = bot.send_message(msg.from_user.id, text="Выбери пробежку для удаления", reply_markup=markup)
        bot.register_next_step_handler(msg, confirmDelete)
    else:
        bot.send_message(msg.from_user.id,'Здесь пусто. Нечего удалить.')
        send_keyboard(msg, "Чем еще могу помочь?")

# спрашиваем, точно ли пользователь хочет удалить пробежку
def confirmDelete(msg):
    bot.send_message(msg.from_user.id, 'Вы собираетесь удалить следующую пробежку:')
    bot.send_message(msg.from_user.id, msg.text)
    markup = types.ReplyKeyboardMarkup()
    markup.add(types.KeyboardButton('Да'))
    markup.add(types.KeyboardButton('Нет'))
    msg1 = bot.send_message(msg.from_user.id, text='Удаляем?', reply_markup=markup)
    bot.register_next_step_handler(msg1, deleteRun, msg)

# теперь напишем саму функцию deleteRun
def deleteRun(msg1, msg):
    if msg1.text == 'Да':
        SQLfunctions.deleteRunFromBD(msg)
        bot.send_message(msg.chat.id, 'Пробежка удалена')
        send_keyboard(msg, "Чем еще могу помочь?")
    else:
        send_keyboard(msg, "Окей, ничего не удаляем! Чем еще могу помочь?")

# перейдем к функции Получения, просмотра и учета наград
# сначала добавим функцию для красивого вывода строк с наградой
def get_reward_string(rewardList):
    rewList_str = []
    for val in list(rewardList):
        rewList_str.append(str('"' + val[0] + '"' + '\n'))
    return ''.join(rewList_str)

# теперь переходим к самой функции
def reward(msg, flag_marathon, flag_half_marathon):
    runsList = SQLfunctions.selectRunsfromBD(msg)
    rewardList = get_reward_string(SQLfunctions.selectRewardsfromBD(msg))

    summdistance = 0  # переменная, в которую будем записывать суммарное расстояние, которое уже пробежал пользователь
    for val in runsList:
        # try:
        if ',' in str(val).rstrip(')').rstrip(','):
            summdistance += float(str(val).split(',')[0].split()[0].lstrip('(').lstrip("'"))
        else:
            bot.send_message(msg.chat.id, "================\n"
                                              "УВЕДОМЛЕНИЕ\n"
                                              "* Вы вашем списке пробежек есть пробежка в нерекомендуемом формате!\n"
                                              "* Пробежка в нерекомендуемом формате не влияет на работу бота, однако данные из данной"
                                              " пробежки никак не будут учтены\n"
                                              "* Рекомендуется удалить данную пробежку:\n")
            bot.send_message(msg.chat.id, val)
            bot.send_message(msg.chat.id, "================")
        # except:
        #     bot.send_message(msg.chat.id, "================\n"
        #                                   "УВЕДОМЛЕНИЕ\n"
        #                                   "* Вы вашем списке пробежек есть пробежка в нерекомендуемом формате!\n"
        #                                   "* Пробежка в нерекомендуемом формате не влияет на работу бота, однако данные из данной"
        #                                   " пробежки никак не будут учтены\n"
        #                                   "* Рекомендуется удалить данную пробежку:\n")
        #     bot.send_message(msg.chat.id, val)
        #     bot.send_message(msg.chat.id, "================")

    if (flag_marathon == 1) and ("Пробежать марафон!" not in rewardList):
        bot.send_message(msg.chat.id, 'Да вы марафонец!')
        bot.send_sticker(msg.chat.id, 'CAACAgIAAxkBAAEDG4JhbVH5kd2eiIFKEUWJ8g6jxBehlgACtQADwZxgDNJKKKDyOCUEIQQ')
        SQLfunctions.insertRewardIntoBD(msg, "Пробежать марафон!")

    if (flag_half_marathon == 1) and ("Пробежать полумарафон!" not in rewardList):
        bot.send_message(msg.chat.id, 'Ого, вы пробежали полумарафон!')
        bot.send_sticker(msg.chat.id, 'CAACAgIAAxkBAAEDG4RhbVUKixP0SEOvFQQ8nIb2QDvkgAACtwADwZxgDPilirtWD6kDIQQ')
        SQLfunctions.insertRewardIntoBD(msg, "Пробежать полумарафон!")

    if (summdistance > 0 and "Начало положено!" not in rewardList):
        bot.send_message(msg.chat.id, 'Ваша первая пробежка, круто!')
        bot.send_sticker(msg.chat.id, 'CAACAgIAAxkBAAEDEl1hZzjsZ1hhdMvFXy5uI4Twg_THfwACxAADMNSdEcjFvLwK6xVKIQQ')
        SQLfunctions.insertRewardIntoBD(msg, "Начало положено!")

    if (summdistance >= 100 and "Преодолеть отметку в 100 км!" not in rewardList):
        bot.send_message(msg.chat.id, f'Вау, вы преодолели отметку в 100 км! Так держать!')
        bot.send_sticker(msg.chat.id, 'CAACAgIAAxkBAAEDFKBhaFqay25yDk89nZ53k2qPmwLMOAACywADMNSdEXWK5xlwcXbPIQQ')
        SQLfunctions.insertRewardIntoBD(msg, "Преодолеть отметку в 100 км!")

    if (summdistance >= 200 and "Преодолеть отметку в 200 км!" not in rewardList):
        bot.send_message(msg.chat.id, 'Вау, вы преодолели отметку в 200 км! Очень круто!')
        bot.send_sticker(msg.chat.id, 'CAACAgIAAxkBAAEDG5xhbWhoD5PCuVHwuXIfomPf1-7XrgACzAADMNSdEbg0CDIOCTHMIQQ')
        SQLfunctions.insertRewardIntoBD(msg, "Преодолеть отметку в 200 км!")

# функция, которая выводит список всех полученных наград
def showRewards(msg):
    rewardList = get_reward_string(SQLfunctions.selectRewardsfromBD(msg))
    bot.send_message(msg.chat.id, rewardList)
    send_keyboard(msg, "Чем еще могу помочь?")

# напишем теперь функцию, которая удаляет ВСЕ пробежки из БД
# для этого сначала добавим функцию, которая будет спрашивать, уверен ли пользователь,
# что он хочет удалить все пробежки
def confirmDeleteAllRunningData(msg):
    markup = types.ReplyKeyboardMarkup()
    markup.add(types.KeyboardButton('Да'))
    markup.add(types.KeyboardButton('Нет'))
    msg = bot.send_message(msg.from_user.id, text='Вы уверены, что хотите удалить ВСЕ пробежки?', reply_markup=markup)
    bot.register_next_step_handler(msg, deleteAllRunningData)

# сама функция удаления пробежек
def deleteAllRunningData(msg):
    if (msg.text == 'Да'):
        SQLfunctions.deleteAllRunsFromBD(msg)
        send_keyboard(msg, "Все пробежки удалены. Чем еще могу помочь?")
    else:
        send_keyboard(msg, "Ок, ничего не удаляем. Чем еще могу помочь?")

# напишем теперь функцию, которая удаляет ВСЕ награды из БД
# для этого сначала добавим функцию, которая будет спрашивать, уверен ли пользователь,
# что он хочет удалить все награды
def confirmDeleteAllRewardData(msg):
    markup = types.ReplyKeyboardMarkup()
    markup.add(types.KeyboardButton('Да'))
    markup.add(types.KeyboardButton('Нет'))
    msg = bot.send_message(msg.from_user.id, text='Вы уверены, что хотите удалить ВСЕ достижения?', reply_markup=markup)
    bot.register_next_step_handler(msg, deleteAllRewardData)

# сама функция удаления наград
def deleteAllRewardData(msg):
    if (msg.text == 'Да'):
        SQLfunctions.deleteAllRewardsFromBD(msg)
        send_keyboard(msg, "Все награды удалены. Чем еще могу помочь?")
    else:
        send_keyboard(msg, "Ок, ничего не удаляем. Чем еще могу помочь?")

# функция создания сводки
def activity_while_month(msg):
    runsList = SQLfunctions.selectRunsfromBD(msg)
    distance = 0
    time = 0
    places = []
    for i in runsList:
        for j in i:
            try:
                info = str(j).split(',')
                date = info[3].replace(' ','').split('.')
                now = datetime.datetime.now().strftime('%y-%m-%d')
                bd = datetime.date(int(date[2]),int(date[1]),int(date[0])).strftime('%y-%m-%d')
                if (datetime.datetime.strptime(now,'%y-%m-%d') - datetime.datetime.strptime(bd,'%y-%m-%d')).days <= 30:
                    distance += float(str(i).split(',')[0].split()[0].lstrip('(').lstrip("'"))
                    time += int(str(i).split(',')[1].split()[0].lstrip('(').lstrip("'"))
                    places.append(str(info[2]))
            except:
                # bot.send_message(msg.chat.id, "Следующая пробежка не удовлетворяет требованиям к формату, поэтому не будет учтена")
                # bot.send_message(msg.chat.id, i)
                pass

    if len(places) != 0:
        a = max(set(places), key=places.count)
    else:
        a = 'Нет мест'
    bot.send_message(msg.chat.id, 'Расстояние: {} км'.format(distance))
    bot.send_message(msg.chat.id, 'Время: {} минут'.format(time))
    bot.send_message(msg.chat.id, 'Самое часто посещаемое место: {}'.format(a))
    send_keyboard(msg, "Чем еще могу помочь?")

# програмка на вход получает количество месяцев, а дальше смотрит на разницу по датам и подбирает все пробежки
def runs_in_period(msg):
    answer = ''
    runsList = SQLfunctions.selectRunsfromBD(msg)
    if len(runsList) != 0:
        spisok = []
        for i in runsList:
            for j in i:
                try:
                    info = str(j).split(',')
                    date = info[3].replace(' ', '').split('.')
                    now = datetime.datetime.now().strftime('%y-%m-%d')
                    bd = datetime.date(int(date[2]), int(date[1]), int(date[0])).strftime('%y-%m-%d')
                    if (datetime.datetime.strptime(now, '%y-%m-%d') - datetime.datetime.strptime(bd, '%y-%m-%d')).days <= int(msg.text) * 30:
                        spisok.append(j)
                except:
                    pass

        if len(spisok) != 0:
            for i in spisok:
                answer += str(i) + '\n'
            bot.send_message(msg.from_user.id, answer)
            send_keyboard(msg, "Чем еще могу помочь?")
        else:
            bot.send_message(msg.from_user.id, "У вас нет пробежек за указанный период")
            send_keyboard(msg, "Чем еще могу помочь?")
    else:
        bot.send_message(msg.from_user.id, "У вас еще нет пробежек")
        send_keyboard(msg, "Чем еще могу помочь?")
#--------------------------------

# привязываем функции к кнопкам на клавиатуре
def callback_worker(call):
    if call.text == "Добавить пробежку":
        markup = types.ReplyKeyboardMarkup(row_width=1)
        markup.add(types.KeyboardButton('Назад'))
        msg = bot.send_message(call.chat.id, text='Давайте добавим пробежку! Напишите ее в чат.\n'
                                       'Помните, что пробежку нужно записать в формате:\n'
                                       'Дистанция (км), Время (мин), Локация, Дата (дд.мм.гггг)\n\n'
                                       'Пример:\n2 км, 10 мин, Москва, 12.10.2021 \n\n'
                                       'Если нажали на команду по ошибке, нажмите "Назад" на клавиатуре',
                                        reply_markup=markup)
        bot.register_next_step_handler(msg, add_run)

    # кнопка импорта файлов
    elif call.text == "Импорт записей":
        markup = types.ReplyKeyboardMarkup(row_width=1)
        markup.add(types.KeyboardButton('Назад'))
        msg = bot.send_message(call.chat.id,
                               'Давайте добавим ваши записи пробежек! Нажмите на скрепку и прикрепите файл!\n'
                               'Я умею работать только с файлами .txt или .docx\n '
                               'Желательно, чтобы записи пробежек были на отдельных строках и в формате:\n'
                               'Дистанция (км), Время (мин), Локация, Дата (дд.мм.гггг)\n\n'
                               'Если нажали на команду по ошибке, нажмите "Назад" на клавиатуре',
                               reply_markup=markup)
        bot.register_next_step_handler(msg, import_run)

    elif call.text == "Показать все пробежки":
        try:
            showAllRuns(call)
        except:
            bot.send_message(call.chat.id, 'Здесь пусто. Может начнем бежать прямо сейчас? ;)')
            send_keyboard(call, "Чем еще могу помочь?")

    elif call.text == "Удалить пробежку":
        chooseToDelete(call)

    elif call.text == "Награды":
        bot.send_message(call.chat.id, 'Давайте посмотрим на ваши награды!...')
        try:
            showRewards(call)
        except:
            bot.send_message(call.chat.id, 'У вас пока нет наград :(')
            bot.send_sticker(call.chat.id, 'CAACAgIAAxkBAAEDFKRhaGPl0VAFJ14oaW1t7nnHVPEw8wACdQADwDZPE5B0WbJxOIvjIQQ')
            send_keyboard(call, "Чем еще могу помочь?")

    elif call.text == 'Показать активность за месяц':
        bot.send_message(call.chat.id, 'Вот ваша активность за последний месяц')
        activity_while_month(call)

    elif call.text == 'Показать пробежки за период':
        msg = bot.send_message(call.from_user.id, 'Введите количество месяцев')
        bot.register_next_step_handler(msg, runs_in_period)

    elif call.text == "Пока все!":
        bot.send_message(call.chat.id, 'Хорошего дня! Когда захотите продолжить нажмите на команду\n /start')

    elif call.text == 'Очистить runningData':
        confirmDeleteAllRunningData(call)

    elif call.text == 'Очистить rewardData':
        confirmDeleteAllRewardData(call)

    else:
        bot.send_message(call.chat.id, 'Извините, я вас не понимаю :(')
        send_keyboard(call, "Пожалуйста, выберите функции из предложенных")

bot.polling(none_stop=True)


from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
import random
import time

options = webdriver.ChromeOptions()         #시스템 오류를 위해 삽입한구문
options.add_experimental_option("excludeSwitches", ["enable-logging"])   #시스템 오류를 위해 삽입한구문
browser = webdriver.Chrome(options=options)  #시스템 오류를 위해 삽입한구문

# 1 네이버 로그인
browser.get('https://naver.com')
browser.maximize_window()   #창의 크기 맥시멈
action = ActionChains(browser)  #키다운 할때 필요한 부분을 액션으로 저장함.

# 2 네이버 로그인 버튼 찾기 및 클릭.
browser.find_element_by_class_name("link_login").click()
time.sleep(random.uniform(1,3))
# 3네이버 아이디 및 패스워드 창
time.sleep(3)
browser.find_element_by_id("id").send_keys("bestbox1111")

# action.send_keys(Keys.TAB).perform()
time.sleep(3)
browser.find_element_by_id("pw").send_keys("k9910k057")
time.sleep(3)

# 4 로그인 버튼 클릭
browser.find_element_by_id("log.login").click()


from apps.productos.models import Producto, Categoria, Venta, VentaProducto
from django import forms

class ProductoForm(forms.ModelForm):
    class Meta:
        model = Producto

        fields = [
            'id_cliente',
            'nombre',
            'precio',
            'foto',
            'existencias',
            'categoria',
        ]

        labels = {
            'id_cliente': 'Id Cliente',
            'nombre': 'Nombre',
            'precio': 'Precio',
            'foto': 'Foto',
            'existencias': 'Existencias',
            'categoria': 'Categoria',

        }

    def __init__(self, *args, **kwargs):
        super(ProductoForm, self).__init__(*args, **kwargs)
        self.fields['id_cliente'].widget = forms.HiddenInput()
        self.fields['nombre'].widget.attrs.update({'placeholder': 'Digite el Nombre Del Producto'})
        self.fields['precio'].widget.attrs.update({'placeholder': 'Digite el Precio Del Producto'})
        self.fields['existencias'].widget.attrs.update({'placeholder': 'Digite las Existencias Del Producto'})
        self.fields['categoria'].widget.attrs.update({'placeholder': 'Digite la Categoria Del Producto'})

        for field in self.fields:
            self.fields[field].widget.attrs.update({'class': 'form-control'})

class CategoriaForm(forms.ModelForm):
    class Meta:
        model = Categoria

        fields = [
            'nombre',
        ]

        labels = {
            'nombre':'Nombre',
        }

        widgets = {
            'nombre': forms.TextInput(attrs={'class':'form-control'}),
        }

class VentaForm(forms.ModelForm):
    class Meta:
        model = Venta

        fields = [
            'fecha',
            'total',
            'id_producto',
            'id_tienda',
            'firstname_cliente',
            'lastname_cliente',
            'pais_cliente',
            'direccion_cliente',
            'ciudad_cliente',
            'telefono_cliente',
            'correo_cliente',
        ]

        labels = {
            'fecha': 'Fecha',
            'total': 'Total',
            'id_producto': 'Id Producto',
            'id_tienda': 'Id Tienda',
            'firstname_cliente':'Nombres',
            'lastname_cliente':'Apellidos',
            'pais_cliente':'Pais',
            'direccion_cliente':'Direccion',
            'ciudad_cliente':'Ciudad',
            'telefono_cliente':'Telefono',
            'correo_cliente':'Correo',
        }

    def __init__(self, *args, **kwargs):
        super(VentaForm, self).__init__(*args, **kwargs)
        self.fields['fecha'].widget = forms.HiddenInput()
        self.fields['total'].widget = forms.HiddenInput()
        self.fields['id_producto'].widget = forms.HiddenInput()
        self.fields['id_tienda'].widget = forms.HiddenInput()
        self.fields['firstname_cliente'].widget.attrs.update({'placeholder': 'Digite sus Nombres'})
        self.fields['lastname_cliente'].widget.attrs.update({'placeholder': 'Digite su Apellidos'})
        self.fields['pais_cliente'].widget.attrs.update({'placeholder': 'Digite su Pais'})
        self.fields['direccion_cliente'].widget.attrs.update({'placeholder': 'Digite su Direccion'})
        self.fields['ciudad_cliente'].widget.attrs.update({'placeholder': 'Digite su Ciudad'})
        self.fields['telefono_cliente'].widget.attrs.update({'placeholder': 'Digite su Telefono'})
        self.fields['correo_cliente'].widget.attrs.update({'placeholder': 'Digite su Correo'})

        for field in self.fields:
            self.fields[field].widget.attrs.update({'class': 'form-control'})

class VentaProductoForm(forms.ModelForm):
    class Meta:
        model = VentaProducto

        fields = [
            'cantidad',
            'precio_venta',
            'descuento',
            'producto',
            'venta',
        ]

        labels = {
            'cantidad': 'Cantidad',
            'precio_venta': 'Precio',
            'descuento': 'Descuento',
            'producto': 'Producto',
            'venta': 'Venta',
        }

    def __init__(self, *args, **kwargs):
        super(VentaProductoForm, self).__init__(*args, **kwargs)

        for field in self.fields:
            self.fields[field].widget.attrs.update({'class': 'form-control'})


import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.nn import functional as F

class myRNN(nn.Module):
    def __init__(self, config):
        super(myRNN, self).__init__()

        self.hidden_size = 30  # Dimension of hidden state
        self.embedding_length = config.wordvec_dim
        self.num_layers = 2  # Stack layers
        self.embedding_length = config.wordvec_dim
        self.word_embeddings = nn.Embedding(
            config.weight.shape[0], self.embedding_length)  # Embedding layer
        self.word_embeddings = self.word_embeddings.from_pretrained(
            config.weight, freeze=False)  # Load pretrianed word embedding, and fine-tuing
        self.recurrent = nn.RNN(self.embedding_length, self.hidden_size,
                                num_layers=self.num_layers, bidirectional=True, batch_first=True)  # Recurrent layer
        self.fc = nn.Linear(2*self.num_layers *
                            self.hidden_size, config.label_num)  # FC layer

    def forward(self, input_sentences):

        x = self.word_embeddings(input_sentences)  # (batch_size, batch_dim, embedding_length)
        # print(x.size())

        output, h_n = self.recurrent(x)  # 特征，隐状态
        # print(h_n.size())
        # h_n.size() = (2*self.num_layers, batch_size, hidden_size), 2 for bidirectional

        # (batch_size, 2*self.num_layers, hidden_size)
        h_n = h_n.permute(1, 0, 2)

        h_n = h_n.contiguous().view(h_n.size()[0], h_n.size()[1]*h_n.size()[2])  # (batch_size, 4*hidden_size)
        # print(h_n.size())

        logits = self.fc(h_n)  # (batch_size, label_num)

        return F.softmax(logits, dim=1)

class LSTMClassifier(nn.Module):
    def __init__(self, config):
        super(LSTMClassifier, self).__init__()

        self.label_num = config.label_num
        self.hidden_size = 150
        self.embedding_length = config.wordvec_dim
        self.word_embeddings = nn.Embedding(
            config.weight.shape[0], self.embedding_length)  # Embedding layer
        self.word_embeddings = self.word_embeddings.from_pretrained(
            config.weight, freeze=False)  # Load pretrianed word embedding, and fine-tuing

        self.lstm = nn.LSTM(self.embedding_length,
                            self.hidden_size, batch_first=True)  # lstm
        self.fc = nn.Linear(self.hidden_size, self.label_num)

    def forward(self, input_sentence):
        x = self.word_embeddings(input_sentence)  # (batch_size, batch_dim, embedding_length)

        output, (final_hidden_state, final_cell_state) = self.lstm(x)

        logits = self.fc(final_hidden_state[-1])  # final_hidden_state.size() = (1, batch_size, hidden_size) & logits.size() = (batch_size, label_num)

        return F.softmax(logits, dim=1)


import numpy
from gnuradio import gr
import pmt

import datetime

class print_timestamp(gr.basic_block):
    """
    docstring for block print_timestamp
    """
    def __init__(self, tstamp_format=''):
        gr.basic_block.__init__(self,
            name="swap_crc",
            in_sig=[],
            out_sig=[])

        self.message_port_register_in(pmt.intern('in'))
        self.set_msg_handler(pmt.intern('in'), self.handle_msg)
        self.message_port_register_out(pmt.intern('out'))

        self.tstamp_format = tstamp_format

    def handle_msg(self, msg_pmt):
        if self.tstamp_format:
            timestamp = datetime.datetime.utcnow()
            print(timestamp.strftime(self.tstamp_format))

        self.message_port_pub(pmt.intern('out'), msg_pmt)


"""
author: Wenyu Qiu
des: some def for the diff of func
"""
import sympy as sp

def multi_diff(func, vars):
    """ acquire diff for n-dimension func """
    dif_vect = sp.zeros(len(vars), 1)
    for i in range(len(vars)):
        tmp_dif = sp.diff(func, vars[i])
        dif_vect[i] = tmp_dif
    return dif_vect

def hessian(func, vars):
    """ for n-dimension hessian matrix, @func(spy), @variables(under spy sys) """
    hes_mat = sp.zeros(len(vars), len(vars))
    for i in range(len(vars)):
        v1 = vars[i]
        df1 = sp.diff(func, v1)
        for j in range(len(vars)):
            v2 = vars[j]
            df2 = sp.diff(df1, v2)
            hes_mat[i, j] = df2
    return hes_mat

if __name__ == '__main__':
    x1, x2 = sp.symbols('x1, x2')
    func = x1**2 + x2**2
    # print(hessian(func, [x1, x2]))
    print(multi_diff(func, [x1, x2]))


import json
import redis

r = redis.Redis(host='localhost', port=22772)
my_dict = {"name": "John", "age": 30, "city": "New York"}
outDict = {'urlKey': my_dict}  # 改为使用冒号分隔键和值
# 序列化成JSON字符串
json_string = json.dumps(outDict)

# 将JSON字符串存储到Redis中
r.set("my_dict", json_string)

# 从Redis中读取JSON字符串
json_string_redis = r.get("my_dict")

# 反序列化成Python对象
my_dict_redis = json.loads(json_string_redis)
print(my_dict_redis)


# multiAgents.py
# --------------
# Licensing Information:  You are free to use or extend these projects for
# educational purposes provided that (1) you do not distribute or publish
# solutions, (2) you retain this notice, and (3) you provide clear
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
#
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
# The core projects and autograders were primarily created by John DeNero
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
# Student side autograding was added by Brad Miller, Nick Hay, and
# Pieter Abbeel (pabbeel@cs.berkeley.edu).

from curses import newpad
import dis
from re import A
from shutil import move
from util import manhattanDistance
from game import Directions
import random, util
import statistics

from game import Agent

class ReflexAgent(Agent):
    """
    A reflex agent chooses an action at each choice point by examining
    its alternatives via a state evaluation function.

    The code below is provided as a guide.  You are welcome to change
    it in any way you see fit, so long as you don't touch our method
    headers.
    """

    def getAction(self, gameState):
        """
        You do not need to change this method, but you're welcome to.

        getAction chooses among the best options according to the evaluation function.

        Just like in the previous project, getAction takes a GameState and returns
        some Directions.X for some X in the set {NORTH, SOUTH, WEST, EAST, STOP}
        """
        # Collect legal moves and successor states
        legalMoves = gameState.getLegalActions(self.index)
        legalMoves.remove("Stop")

        # Choose one of the best actions
        scores = [self.evaluationFunction(gameState, action) for action in legalMoves]
        bestScore = max(scores)
        bestIndices = [index for index in range(len(scores)) if scores[index] == bestScore]
        chosenIndex = random.choice(bestIndices) # Pick randomly among the best

        "Add more of your code here if you want to"

        return legalMoves[chosenIndex]

    def evaluationFunction(self, currentGameState, action):
        """
        Design a better evaluation function here.

        The evaluation function takes in the current and proposed successor
        GameStates (pacman.py) and returns a number, where higher numbers are better.

        The code below extracts some useful information from the state, like the
        remaining food (newFood) and Pacman position after moving (newPos).

        Print out these variables to see what you're getting, then combine them
        to create a masterful evaluation function.
        """
        # Useful information you can extract from a GameState (pacman.py)
        score=0
        successorGameState = currentGameState.generatePacmanSuccessor(self.index, action)
        newPos = successorGameState.getPacmanPosition(self.index)
        newFood = successorGameState.getFood()
        newGhostStates = successorGameState.getGhostStates()
        ghostStates = currentGameState.getGhostStates()
        newGhostPositions = successorGameState.getGhostPositions()
        currentPos = currentGameState.getPacmanPosition(self.index)
        distances = []
        for position in newGhostPositions:
            distances.append(manhattanDistance(newPos,position))

        nearestGhostDis = min(distances)
        if(nearestGhostDis<2):#very dangerous
           score-=100
        elif(nearestGhostDis<3):#quite dangerous
           score-=50
        elif(nearestGhostDis<4):#less dangerous
           score-=25
        elif(nearestGhostDis<5):#less dangerous
            score-=10
        food = successorGameState.getFood()
        foodCoordinates = food.asList()#generates a list of coordinates with remaining food
        distancesToFood = []
        #print(pacPos)
        foodRemaining = successorGameState.getNumFood()
        for foodpos in foodCoordinates:
            distancesToFood.append(manhattanDistance(foodpos,newPos))
        if(foodRemaining>=1):
            nearestFoodDis = min(distancesToFood)
            score+=10/nearestFoodDis
        if(foodRemaining<3):
            score+=25/nearestFoodDis
            score=score

        currentFoodCount = currentGameState.getNumFood()
        newFoodCount = successorGameState.getNumFood()
        if(newFoodCount<currentFoodCount):
            score+=5

        remainingCapPos = successorGameState.getCapsules() #positions of remaining capsules
        numRemainingCaps = len(remainingCapPos) #number of capsules remaining
        #score-=25*numRemainingCaps #the less capsules remaining, the better
        distancesToCaps = []
        #score += 0.1*intgamestate.getScore()
        for cappos in remainingCapPos:
            distancesToCaps.append(manhattanDistance(cappos,newPos))
            #print(manhattanDistance(cappos,pacPos))
        if(numRemainingCaps>=1):
            nearestCapDistance = min(distancesToCaps)
            score += 20/nearestCapDistance

        capsules = currentGameState.getCapsules()
        newcapsules = successorGameState.getCapsules()
        closestCapsuleDis = 999999
        closestCapsulePos = (0,0)
        for cap in capsules:
            olddistocap = manhattanDistance(currentPos,cap)
            if(olddistocap<closestCapsuleDis):
                closestCapsuleDis=olddistocap
                closestCapsulePos=cap
        olddistocap=manhattanDistance(closestCapsulePos,currentPos)
        newdistocap = manhattanDistance(closestCapsulePos,newPos)
        print(olddistocap)
        print(newdistocap)
        foods = currentGameState.getFood()
        if(len(newcapsules)>=1):

            if(newdistocap<olddistocap):
                #score+=1000
                print("YESSS")
        elif(newFoodCount==currentFoodCount):
            closestFoodPos = (0,0)
            closesfoodDis = 99999
            #for x in range(0,len(foods)):
            #    for y in range(0,len(foods[x])):
            #        if(currentGameState.hasFood(x,y)):
            #            if(manhattanDistance((x,y),currentPos)<closesfoodDis):
            #                closesfoodDis=manhattanDistance((x,y),currentPos)
            #                closestFoodPos=(x,y)
            #if(manhattanDistance(closestFoodPos,newPos)<closestFoodPos):
            #    score+=5
            #get closer to next available food
        if((len(capsules)-len(newcapsules))>=1):
            score+=10*(newFoodCount)

        #if len(newFood.asList()):
        #    fooddist = util.manhattanDistance(newPos, newFood.asList()[0])
        #else:
        #    fooddist = 0
        print(action)
        print(score)
        return score

def scoreEvaluationFunction(currentGameState, index):
    """
    This default evaluation function just returns the score of the state.
    The score is the same one displayed in the Pacman GUI.

    This evaluation function is meant for use with adversarial search agents
    (not reflex agents).
    """
    return currentGameState.getScore()[index]

class MultiAgentSearchAgent(Agent):
    """
    This class provides some common elements to all of your
    multi-agent searchers.  Any methods defined here will be available
    to the MinimaxPacmanAgent & AlphaBetaPacmanAgent.

    You *do not* need to make any changes here, but you can if you want to
    add functionality to all your adversarial search agents.  Please do not
    remove anything, however.

    Note: this is an abstract class: one that should not be instantiated.  It's
    only partially specified, and designed to be extended.  Agent (game.py)
    is another abstract class.
    """

    def __init__(self, index = 0, evalFn = 'scoreEvaluationFunction', depth = '2'):
        self.index = index # Pacman is always agent index 0
        self.evaluationFunction = lambda state:util.lookup(evalFn, globals())(state, self.index)
        self.depth = int(depth)

class MultiPacmanAgent(MultiAgentSearchAgent):
    """
    You implementation here
    """
    def heuristic(self,gamestate):
        if(gamestate.isWin()):
            return 99999999
        elif(gamestate.isLose()):
            return -9999999
        score = 0
        index = self.index #index of pacman
        pacPos = gamestate.getPacmanPosition(index) #position of pacman
        foodRemaining = gamestate.getNumFood() #amount of food remaining in the game
        #score -= 2*foodRemaining #the less food remaining, the better
        food = gamestate.getFood()
        foodCoordinates = food.asList()#generates a list of coordinates with remaining food
        distancesToFood = []
        #print(pacPos)
        for foodpos in foodCoordinates:
            distancesToFood.append(manhattanDistance(foodpos,pacPos))
        if(foodRemaining>=1):
            nearestFoodDis = min(distancesToFood)
            score+=10/nearestFoodDis
        if(foodRemaining<3):
            score+=25/nearestFoodDis
            score=score
        ghostPositions = gamestate.getGhostPositions()
        distancesToGhost = []
        for ghostpos in ghostPositions:
            distancesToGhost.append(manhattanDistance(ghostpos,pacPos))

        nearestGhostDis = min(distancesToGhost)

        if(nearestGhostDis<2):#very dangerous
           score-=50
        elif(nearestGhostDis<3):#quite dangerous
           score-=25
        elif(nearestGhostDis<4):#less dangerous
           score-=10
        #elif(nearestGhostDis<5):#less dangerous
        #    score-=10
        averageGhostdis = statistics.mean(distancesToGhost)
        #score +=2*averageGhostdis
        remainingCapPos = gamestate.getCapsules() #positions of remaining capsules
        numRemainingCaps = len(remainingCapPos) #number of capsules remaining
        #score-=25*numRemainingCaps #the less capsules remaining, the better
        distancesToCaps = []
        #score += 0.1*intgamestate.getScore()
        for cappos in remainingCapPos:
            distancesToCaps.append(manhattanDistance(cappos,pacPos))
            #print(manhattanDistance(cappos,pacPos))
        if(numRemainingCaps>=1):
            nearestCapDistance = min(distancesToCaps)
            score += 30/nearestCapDistance #incentivize capsules before food raised to 30 from 20
        score+=100/(foodRemaining+1)
        score+=250/(numRemainingCaps+1) #raised from 200 to 250
        score+=scoreEvaluationFunction(gamestate,index) #incentivize eating food faster and avoiding idle states

        return score

    #My implementation extends the minmax algo for multiple minimizing agents. By splitting the search into three functions, I'm
    #able to compute the min and max scores for each action and then return the action with the highest score.

    def minimumSearchAgent(self,gamestate,index,depth):
        if((gamestate.isWin())): #depth reached or game is over
            return 9999999
        if(gamestate.isLose()):
            return -999999
        if(depth==self.depth):
            return self.heuristic(gamestate) #must cut search slightly early to avoid timeout on gradescope
        lowestScore = float('inf') # set the initial lowest score to infinity so that all scores are worse
        nextagent = ""
        for i in range(0,len(gamestate.getLegalActions(index))):
            nextState = gamestate.generateSuccessor(index,gamestate.getLegalActions(index)[i])
            if(index<gamestate.getNumAgents()-1): #iterate for next ghost
                lowestScore = min(lowestScore,self.minimumSearchAgent(nextState,index+1,depth))
            elif(index>=gamestate.getNumAgents()-1): #iterated for all ghosts, return to maxsearch
                lowestScore = min(lowestScore,self.maximumSearchAgent(nextState,0,depth)) #pass control back to maxsearch
        return lowestScore

    def maximumSearchAgent(self,gamestate,index,depth):
        if((gamestate.isWin())): #depth reached or game is over
            return 9999999
        if(gamestate.isLose()):
            return -999999
        if(depth==self.depth):
            return self.heuristic(gamestate)
        greatestScore = float('-inf') #set the initial best score to negative infinity so all scores are better

        for i in range(0,len(gamestate.getLegalActions(self.index))):
            nextState = gamestate.generateSuccessor(index,gamestate.getLegalActions(self.index)[i])
            greatestScore = max(self.minimumSearchAgent(nextState,index+1,depth+1),greatestScore)
        return greatestScore

    def minmaxSearch(self,gamestate,index,depth):
        bestScore = float('-inf') #initialize the initial best score to be better than all possible scores
        for i in range(0,len(gamestate.getLegalActions(index))):
            scores = []
            scoresandmoves = []
            nextstate = gamestate.generateSuccessor(index,gamestate.getLegalActions(index)[i]) #evaluate for all possible pacman moves
            if(nextstate.isWin()):
                return gamestate.getLegalActions(index)[i]
            currentGameScore = self.minimumSearchAgent(nextstate,index+1,depth) #pacman just moved so evaluate ghost actions next
            scores.append(currentGameScore)
            scoresandmoves.append([currentGameScore,move])
            if(currentGameScore>bestScore):     #choose the best state at the end of the search
                bestScore, bestmove=currentGameScore, gamestate.getLegalActions(index)[i]

        return bestmove,bestScore #return the move with the highest score and corresponging score

    def getAction(self, gameState):
        index = self.index # pacman index
        """
        Returns the expectimax action using self.depth and self.evaluationFunction

        All ghosts should be modeled as choosing uniformly at random from their
        legal moves.

        Some functions you may need:
        if gameState.isWin() or gameState.isLose():
            return self.evaluationFunction(gameState)
        legalMoves = gameState.getLegalActions(agent)
        legalNextState = [gameState.generateSuccessor(agent, action)
                          for action in legalMoves]
        """
        "*** YOUR CODE HERE ***"
        #print("Number of Pacmans:", gameState.getNumPacman(), ", Number of ghosts:", gameState.getNumGhosts())
        pos = gameState.getPacmanPosition(self.index)
        legalmoves = gameState.getLegalActions(self.index)
        for move in legalmoves:
            if(gameState.generateSuccessor(self.index,move).isWin()):
                return move

        return self.minmaxSearch(gameState,index,1)[0]

        util.raiseNotDefined()

class RandomAgent(MultiAgentSearchAgent):
    def getAction(self, gameState):
        legalMoves = gameState.getLegalActions(self.index)
        return random.choice(legalMoves)


import os
import base64
import requests
import RPi.GPIO as GPIO
import time
import sys
from gpiozero import DistanceSensor

servoPIN1 = 2
GPIO.setmode(GPIO.BCM)
GPIO.setup(servoPIN1, GPIO.OUT)
pwm1=GPIO.PWM(servoPIN1,50)
pwm1.start(90)

servoPIN2 = 3
GPIO.setmode(GPIO.BCM)
GPIO.setup(servoPIN2, GPIO.OUT)
pwm2=GPIO.PWM(servoPIN2,50)
pwm2.start(90)

sensor1 = DistanceSensor(trigger=19, echo=26)
sensor2 = DistanceSensor(trigger=20, echo=21)
sensor3 = DistanceSensor(trigger=6, echo=13)

def SetAngle(angle):
    duty1 = angle/18 + 2
    duty2 = (180 - angle)/18 + 2
    GPIO.output(servoPIN1, True)
    pwm1.ChangeDutyCycle(duty1)
    GPIO.output(servoPIN2, True)
    pwm2.ChangeDutyCycle(duty2)
    time.sleep(1)
    GPIO.output(servoPIN1, False)
    pwm1.ChangeDutyCycle(0)
    GPIO.output(servoPIN2, False)
    pwm2.ChangeDutyCycle(0)

SetAngle(90)
SetAngle(0)
SetAngle(135)
SetAngle(0)
SetAngle(90)
pwm1.stop()
pwm2.stop()

while True:
    # Wait 2 seconds
    time.sleep(2)
                  # Get the distance in metres

    # But we want it in centimetres
    distance1 = sensor1.distance * 100
    distance2 = sensor2.distance * 100
    distance3 = sensor3.distance * 100

    # We would get a large decimal number so we will round it to 2 places

    # Print the information to the screen
    print("Distance1: {} cm".format(distance1))
    print("Distance2: {} cm".format(distance2))
    print("Distance3: {} cm".format(distance3))

GPIO.cleanup()


import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
from eralchemy import render_er

Base = declarative_base()
class Characters(Base):
    __tablename__ = "characters"
    id = Column(Integer, primary_key=True)
    name = Column(String(250), nullable=False)
    gender = Column(String(250), nullable=False)
    Age = Column(Integer, nullable=False)

class Planet(Base):
    __tablename__= "planets"
    id = Column(Integer, primary_key=True)
    name = Column(String(250), nullable=False)
    location = Column(String(250), nullable=False)
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(250), nullable=False)
    favoritesid = Column(Integer, ForeignKey("favorites.id"))

class Favorite(Base):
    __tablename__ = 'favorites'
    id = Column(Integer, primary_key=True)
    planetid = Column(Integer, ForeignKey("planets.id"))
    characterid = Column(Integer, ForeignKey("characters.id"))
    user = relationship(User)
    character = relationship(Characters)
    planet = relationship(Planet)

    def to_dict(self):
        return {}

## Draw from SQLAlchemy base
render_er(Base, 'diagram.png')


#Scripts\activate เพื่อเข้าไปใน environment
#ก่อนใช้ ให้เข้าไปใน tutorial ก่อน โดยการ cd week03
#ใช้ scrapy crawl quotes -o LMAOXD.csv โดย LMAOXD คือชื่อไฟล์ และ .csv คือนามสกุลที่อยากได้ สามารถเปลี่ยนเป็น json ได้
#ใช้ scrapy shell https://store.steampowered.com/app/551170/Onmyoji/ เพื่อ test
#Tutorial Vid : https://youtu.be/ALizgnSFTwQ

import scrapy
import time
import random
from scrapy_selenium import SeleniumRequest

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

class QuotesSpider(scrapy.Spider):
    name = 'quotes'

    def start_requests(self):
        url = 'https://store.steampowered.com/app/2089350/Hatsune_Miku_Logic_Paint_S/'

        yield SeleniumRequest(
            url=url,
            callback=self.parse,
            wait_time=1
            )

    def parse(self, response):
        for quote in response.css('div.page_content_ctn'):
            yield {
                'url' : response.request.url,
                'title': quote.css('.apphub_AppName::text').get(),
                'tag': quote.css('.app_tag::text').re('Hero Shooter|Battle Royale|Competitive|Roguelite|JRPG|Story Rich|Automobile Sim|Sports|Racing|Driving|Immersive Sim|Tactical|Management|Third Person|Simulation|Co-op|Local Multiplayer|Online Co-Op|PvE|Football|Soccer|PvP|Controller|Team-Based|Realistic|Local Co-Op|Multiplayer|Singleplayer|Anime|Strategy|Card Battler|Casual|PvP|Turn-Based Tactics|Card Game|Deckbuilding|Comic Book|Free to Play|Early Access|Superhero|Stylized|RPG|Free to Play|Turn-Based|Adventure|Fantasy|Stealth|Horror|Action|Parody|Retro|Funny|FPS|Shooter|Open World'),
                #([A-Z])\w+')
                # 'price' : quote.css('.game_purchase_price.price::text').get(),
                # 'price02' : quote.css('.game_purchase_price.price::text').getall(),
                'price' : quote.css('.game_purchase_price.price::text').re('Free To Play|Free|\d+'),
                'discounted price' : quote.css('.discount_original_price::text').get()
            }

        next_page = response.css('div.store_horizontal_autoslider_ctn a::attr(href)').get()
        next_pages = response.css('div.block_responsive_horizontal_scroll a::attr(href)').getall()
        for next_page in next_pages:
            if next_page is not None:
                #time.sleep(5)
                next_page = response.urljoin(next_page)
                yield SeleniumRequest(url=next_page, callback=self.parse,wait_time=1)


import os
import boto3
import numpy as np
import tensorflow as tf
from flask import Flask
from dotenv import load_dotenv
from pymongo import MongoClient
from keras.models import load_model
from sklearn.preprocessing import LabelEncoder
from werkzeug.datastructures import FileStorage
from werkzeug.middleware.proxy_fix import ProxyFix
from flask_restplus import Api, Resource
from utils.Model import ModelManager
load_dotenv()

# Mongodb connection
client = MongoClient(os.environ['MONGO_CLIENT_URL'])
db = client.registry

# AWS S3 connection
session = boto3.Session(
    aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=os.environ['AWS_SECRET_KEY']
)
s3 = session.resource('s3')

# App and API setup
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)

api = Api(app, version="1.0", title="Anomaly Detection", description="")
ns = api.namespace('api')
single_parser = api.parser()
single_parser.add_argument("files", location="files", type=FileStorage, action='append', required=True)

graph = tf.get_default_graph()
backup_model = load_model("./models/backup/model.h5")
backup_label_encoder = LabelEncoder()
backup_label_encoder.classes_ = np.load("./models/backup/classes.npy")

@ns.route("/classify")
class MultiClassification(Resource):
    @api.doc(parser=single_parser, description='Upload an image of a solar panel')
    def post(self):
        model = ModelManager(db, s3, graph, backup_model, backup_label_encoder,
            bucket_name=os.environ['AWS_BUCKET_NAME'])
        model.load_latest_model()

        args = single_parser.parse_args()
        image_files = args.files

        preds = []
        for image in image_files:
            image_array = model.preprocess(image)
            preds.append(model.predict(image_array)[0])

        return {'prediction': str(preds)}

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0")


from flask import request, current_app, jsonify, render_template, redirect, make_response
from prance import ResolvingParser
from werkzeug.utils import secure_filename
from fuzzyman.util import (
    get_host,
    get_method_from_path,
    get_parameters_for_path,
    get_path_parameters_for_path,
    get_paths,
    get_schemes,
    build_response
)
from fuzzyman.request import Request
from fuzzyman.collection import Collection

import uuid
import json
import base64
import os
import requests

ALLOWED_EXTENSIONS = {'yml', 'yaml'}
def allowed_file(filename):
    try:
        ext = filename.rsplit('.', 1)[1].lower()
        if '.' in filename and ext in ALLOWED_EXTENSIONS:
            return filename, ext
    except:
        pass
    raise Exception(f"Invalid filename or extension")

# @build_response
def convert_file():
    try:
        file = request.files['file']
    except:
        raise Exception("Must include a file.")

    API = False
    if request.args.get('api'):
        API = True

    num_trials = request.args.get('num_trials')
    if num_trials:
        try:
            num_trials = int(num_trials)
        except:
            num_trials = 5
    else:
        num_trials = 5

    if file.filename == '':
        raise Exception("Invalid file")

    f_name, ext = allowed_file(file.filename)

    random_file_name = f"{f_name}-{uuid.uuid4().hex}.{ext}"

    filename = secure_filename(random_file_name)

    file.save(os.path.join(current_app.config['UPLOAD_FOLDER'], filename))

    try:
        parser = ResolvingParser(current_app.config['UPLOAD_FOLDER'] + f"/{filename}", strict=False)
    except:
        raise Exception("Failed to parse openapi upload. Please verify it is valid and try again.")

    paths = get_paths(parser.specification)
    host = get_host(parser)
    schemes = get_schemes(parser)

    c = Collection(name=f_name + " fuzzed")

    for path in paths:
        methods = get_method_from_path(spec=parser.specification, path=path)
        for method in methods:
            parameters = get_parameters_for_path(spec=parser.specification, path=path, method=method)
            for _ in range(num_trials):
                c.item.append(Request(f"{method.upper()} - {path} {_}", path, host, schemes[0], method, parameters=parameters))

    data = json.dumps(json.loads(str(c)))

    if API and os.getenv('POSTMAN_API_KEY'):
        resp = requests.request(
            'POST',
            'https://api.getpostman.com/collections',
            headers={
                'X-Api-Key' : os.getenv('POSTMAN_API_KEY'),
                'Content-Type' : 'application/json'
            },
            data=data
        )
        return {
            "status" : "Created a collection for you."
        }

    response = make_response(render_template('index.html', data=data))

    return response


import os
from collections import defaultdict

import numpy as np
import pandas as pd
import pylab as plt
import seaborn as sns
import matplotlib.patches as mpatches
import pymzml

from vimms.Chemicals import UnknownChemical, get_absolute_intensity, get_key
from vimms.Common import load_obj, PROTON_MASS, find_nearest_index_in_array
from vimms.MassSpec import FragmentationEvent
from vimms.Roi import make_roi, RoiToChemicalCreator
from vimms.SpectralUtils import get_precursor_info, get_chemicals

def get_N(row):
    if 'T10' in row['filename']:
        return 10
    else:
        return row['filename'].split('_')[3]

def get_dew(row):
    if 'T10' in row['filename']:
        return 15
    else:
        tok = row['filename'].split('_')[5]  # get the dew value in the filename
        return tok.split('.')[0]  # get the part before '.mzML'

def experiment_group(row):
    if 'experiment' in row:
        col_to_check = 'experiment'
    else:
        col_to_check = 'filename'

    if 'beer' in row[col_to_check]:
        return 'beer'
    else:
        return 'urine'

def add_group_column(df):
    df['group'] = df.apply(lambda row: experiment_group(row), axis=1)

def get_df(csv_file, min_ms1_intensity, rt_range, mz_range):
    df = pd.read_csv(csv_file)
    return filter_df(df, min_ms1_intensity, rt_range, mz_range)

def filter_df(df, min_ms1_intensity, rt_range, mz_range):
    # filter by rt range
    if rt_range is not None:
        df = df[(df['rt'] > rt_range[0][0]) & (df['rt'] < rt_range[0][1])]

    # filter by mz range
    if mz_range is not None:
        df = df[(df['rt'] > mz_range[0][0]) & (df['rt'] < mz_range[0][1])]

    # filter by min intensity
    intensity_col = 'maxo'
    if min_ms1_intensity is not None:
        df = df[(df[intensity_col] > min_ms1_intensity)]

    # add log intensity column
    df['log_intensity'] = df.apply(lambda row: np.log(row[intensity_col]), axis=1)

    # add N column
    try:
        df['N'] = df.apply(lambda row: get_N(row), axis=1)
        df[['N']] = df[['N']].astype('int')
    except IndexError:
        pass
    except ValueError:
        df['N'] = df.apply(lambda row: np.nan, axis=1)

    # add group column
    df['group'] = df.apply(lambda row: experiment_group(row), axis=1)
    return df

def make_boxplot(df, x, y, xticklabels, title, outfile=None):
    g = sns.catplot(x=x, y=y, kind='box', data=df)
    g.fig.set_size_inches(10, 3)
    if xticklabels is not None:
        g.set_xticklabels(xticklabels, rotation=90)
    else:
        g.set_xticklabels(rotation=90)
    plt.title(title)
    plt.tight_layout()
    if outfile is not None:
        plt.savefig(outfile, dpi=300)
    plt.show()

def make_hist(df, col_name, file_name, title):
    gb = df.groupby('filename')
    group_df = gb.get_group(file_name)
    vals = group_df[col_name].values
    print(vals, len(vals))
    _ = plt.hist(vals, bins=100)
    plt.title(title)
    plt.tight_layout()
    plt.show()

def to_chemical(row):
    mz = row['mz'] - PROTON_MASS
    rt = row['rt']
    max_intensity = row['maxo']
    chrom = None
    chem = UnknownChemical(mz, rt, max_intensity, chrom, children=None)
    return chem

def df_to_chemicals(df, filename=None):
    if filename is not None:
        filtered_df = df.loc[df['filename'] == filename]
    else:
        filtered_df = df
    chems = filtered_df.apply(lambda row: to_chemical(row), axis=1).values
    return chems

def find_chem(to_find, min_rts, max_rts, min_mzs, max_mzs, chem_list):
    query_mz = to_find.isotopes[0][0]
    query_rt = to_find.rt
    min_rt_check = min_rts <= query_rt
    max_rt_check = query_rt <= max_rts
    min_mz_check = min_mzs <= query_mz
    max_mz_check = query_mz <= max_mzs
    idx = np.nonzero(min_rt_check & max_rt_check & min_mz_check & max_mz_check)[0]
    matches = chem_list[idx]

    # pick a match
    if len(matches) == 0:
        return None
    elif len(matches) == 1:
        return matches[0]
    else:  # multiple matches, take the closest in rt
        diffs = [np.abs(chem.rt - to_find.rt) for chem in matches]
        idx = np.argmin(diffs)
        return matches[idx]

def match(chemical_list_1, chemical_list_2, mz_tol, rt_tol, verbose=False):
    matches = {}
    chem_list = np.array(chemical_list_2)
    min_rts = np.array([chem.rt - rt_tol for chem in chem_list])
    max_rts = np.array([chem.rt + rt_tol for chem in chem_list])
    min_mzs = np.array([chem.isotopes[0][0] * (1 - mz_tol / 1e6) for chem in chem_list])
    max_mzs = np.array([chem.isotopes[0][0] * (1 + mz_tol / 1e6) for chem in chem_list])
    for i in range(len(chemical_list_1)):
        to_find = chemical_list_1[i]
        if i % 1000 == 0 and verbose:
            print('%d/%d found %d' % (i, len(chemical_list_1), len(matches)))
        match = find_chem(to_find, min_rts, max_rts, min_mzs, max_mzs, chem_list)
        if match:
            matches[to_find] = match
    return matches

def match_peaklist(mz_list_1, rt_list_1, intensity_list_1, mz_list_2, rt_list_2, intensity_list_2, mz_tol, rt_tol):
    if mz_tol is not None:  # create mz range for matching in ppm
        min_mzs = np.array([mz * (1 - mz_tol / 1e6) for mz in mz_list_2])
        max_mzs = np.array([mz * (1 + mz_tol / 1e6) for mz in mz_list_2])

    else:  # create mz ranges by rounding to 2dp
        min_mzs = np.around(mz_list_2, decimals=2)
        max_mzs = np.around(mz_list_2, decimals=2)
        mz_list_1 = np.around(mz_list_1, decimals=2)

    # create rt ranges for matching
    min_rts = np.array([rt - rt_tol for rt in rt_list_2])
    max_rts = np.array([rt + rt_tol for rt in rt_list_2])

    matches = {}
    for i in range(len(mz_list_1)):  # loop over query and find a match
        query = (mz_list_1[i], rt_list_1[i], intensity_list_1[i],)
        match = find_match(query, min_rts, max_rts, min_mzs, max_mzs, mz_list_2, rt_list_2, intensity_list_2)
        matches[query] = match
    return matches

def check_found_matches(matches, left_label, right_label, N=20):
    found = [key for key in matches if matches[key] is not None]
    print('Found %d/%d (%f)' % (len(found), len(matches), len(found) / len(matches)))

    print('%s\t\t\t\t\t\t%s' % (left_label, right_label))
    for key, value in list(matches.items())[0:N]:
        if value is not None:
            print('mz %.2f rt %.4f intensity %.4f\tmz %.2f rt %.4f intensity %.4f' % (
                key[0], key[1], key[2], value[0], value[1], value[2]))

def plot_matched_precursors(matches, min_mz, max_mz, min_rt, max_rt, out_file=None):
    plt.figure(figsize=(12, 6))
    plt.rcParams.update({'font.size': 24})
    for key in matches:
        mz, rt, intensity = key
        if min_mz < mz < max_mz and min_rt < rt < max_rt:
            if matches[key] is not None:
                plt.plot([rt], [mz], marker='.', markersize=5, color='blue', alpha=0.1)
            else:
                plt.plot([rt], [mz], marker='.', markersize=5, color='red', alpha=0.1)

    blue_patch = mpatches.Patch(color='blue', label='Matched')
    red_patch = mpatches.Patch(color='red', label='Unmatched')
    plt.legend(handles=[blue_patch, red_patch])
    plt.title('Matched fragmentation events', fontsize=30)
    plt.xlabel('Retention Time (s)')
    plt.ylabel('m/z')
    plt.tight_layout()
    if out_file is not None:
        plt.savefig(out_file, dpi=300)

def count_stuff(input_file, min_rt, max_rt):
    run = pymzml.run.Reader(input_file, MS1_Precision=5e-6,
                            extraAccessions=[('MS:1000016', ['value', 'unitName'])],
                            obo_version='4.0.1')
    mzs = []
    rts = []
    intensities = []
    count_ms1_scans = 0
    count_ms2_scans = 0
    cumsum_ms1_scans = []
    cumsum_ms2_scans = []
    count_selected_precursors = 0
    for spectrum in run:
        ms_level = spectrum['ms level']
        current_scan_rt, units = spectrum.scan_time
        if units == 'minute':
            current_scan_rt *= 60.0
        if min_rt < current_scan_rt < max_rt:
            if ms_level == 1:
                count_ms1_scans += 1
                cumsum_ms1_scans.append((current_scan_rt, count_ms1_scans,))
            elif ms_level == 2:
                try:
                    selected_precursors = spectrum.selected_precursors
                    count_selected_precursors += len(selected_precursors)
                    mz = selected_precursors[0]['mz']
                    intensity = selected_precursors[0]['i']

                    count_ms2_scans += 1
                    mzs.append(mz)
                    rts.append(current_scan_rt)
                    intensities.append(intensity)
                    cumsum_ms2_scans.append((current_scan_rt, count_ms2_scans,))
                except KeyError:
                    # print(selected_precursors)
                    pass

    print('Number of ms1 scans =', count_ms1_scans)
    print('Number of ms2 scans =', count_ms2_scans)
    print('Total scans =', count_ms1_scans + count_ms2_scans)
    print('Number of selected precursors =', count_selected_precursors)
    return np.array(mzs), np.array(rts), np.array(intensities), np.array(cumsum_ms1_scans), np.array(cumsum_ms2_scans)

def find_match(query, min_rts, max_rts, min_mzs, max_mzs, mz_list, rt_list, intensity_list):
    # check ranges
    query_mz, query_rt, query_intensity = query
    min_rt_check = min_rts <= query_rt
    max_rt_check = query_rt <= max_rts
    min_mz_check = min_mzs <= query_mz
    max_mz_check = query_mz <= max_mzs
    idx = np.nonzero(min_rt_check & max_rt_check & min_mz_check & max_mz_check)[0]

    # get mz, rt and intensity of matching indices
    matches_mz = mz_list[idx]
    matches_rt = rt_list[idx]
    matches_intensity = intensity_list[idx]

    if len(idx) == 0:  # no match
        return None

    elif len(idx) == 1:  # single match
        return (matches_mz[0], matches_rt[0], matches_intensity[0],)

    else:  # multiple matches, take the closest in rt
        diffs = [np.abs(rt - query_rt) for rt in matches_rt]
        idx = np.argmin(diffs)
        return (matches_mz[idx], matches_rt[idx], matches_intensity[idx],)

def plot_num_scans(real_cumsum_ms1, real_cumsum_ms2, simulated_cumsum_ms1, simulated_cumsum_ms2, out_file=None):
    plt.plot(real_cumsum_ms1[:, 0], real_cumsum_ms1[:, 1], 'r')
    plt.plot(real_cumsum_ms2[:, 0], real_cumsum_ms2[:, 1], 'b')
    plt.plot(simulated_cumsum_ms1[:, 0], simulated_cumsum_ms1[:, 1], 'r--')
    plt.plot(simulated_cumsum_ms2[:, 0], simulated_cumsum_ms2[:, 1], 'b--')

    plt.legend(['Actual MS1', 'Actual MS2', 'Simulated MS1', 'Simulated MS2'])
    plt.xlabel('Retention Time (s)')
    plt.ylabel('Cumulative sum')
    plt.title('Cumulative number of MS1 and MS2 scans', fontsize=18)
    plt.tight_layout()

    if out_file is not None:
        plt.savefig(out_file, dpi=300)

def plot_matched_intensities(matched_intensities, unmatched_intensities, out_file=None):
    plt.figure()
    temp1 = plt.hist(np.log(matched_intensities), bins = np.linspace(10,20,50), color='blue')
    temp2 = plt.hist(np.log(unmatched_intensities), bins = np.linspace(10,20,50), color='red')
    plt.title('Matched precursor intensities')

    blue_patch = mpatches.Patch(color='blue', label='Matched')
    red_patch = mpatches.Patch(color='red', label='Unmatched')
    plt.legend(handles=[blue_patch, red_patch])
    plt.xlabel('log(intensity)')
    plt.ylabel('Precursor count')
    plt.tight_layout()

    if out_file is not None:
        plt.savefig(out_file, dpi=300)

def load_controller(results_dir, experiment_name, N, rt_tol):
    analysis_name = 'experiment_%s_N_%d_rttol_%d' % (experiment_name, N, rt_tol)
    pickle_in = '%s/%s.p' % (results_dir, analysis_name)
    print('Loading %s' % analysis_name)
    try:
        controller = load_obj(pickle_in)
    except FileNotFoundError:
        controller = None
    return controller

def load_controllers(results_dir, Ns, rt_tols):
    controllers = []
    for N in Ns:
        for rt_tol in rt_tols:
            controller = load_controller(results_dir, N, rt_tol)
            if controller is not None:
                controllers.append(controller)
    return controllers

def compute_performance_scenario_1(controller, dataset, min_ms1_intensity,
                                   fullscan_filename, P_peaks_df,
                                   matching_mz_tol, matching_rt_tol,
                                   chem_to_frag_events=None):
    if chem_to_frag_events is None:  # read MS2 fragmentation events from pickled controller
        chem_to_frag_events = get_frag_events(controller, 2)

    # match with xcms peak-picked ms1 data
    detected_ms1 = df_to_chemicals(P_peaks_df, fullscan_filename)
    matches_fullscan = match(dataset, detected_ms1, matching_mz_tol, matching_rt_tol, verbose=False)

    # check if matched and set a flag to indicate that
    update_matched_status(dataset, matches_fullscan, None)

    # positive instances are ground truth MS1 peaks found by XCMS
    # negative instances are chemicals that cannot be matched to XCMS output
    positives = list(filter(lambda x: x.found_in_fullscan, dataset))
    negatives = list(filter(lambda x: not x.found_in_fullscan, dataset))

    # for both positive and negative instances, count how many frag events they have
    # and whether it's above (good) or below (bad) the minimum ms1 intensity at the time of fragmentation.
    positives_count = get_chem_frag_counts(positives, chem_to_frag_events, min_ms1_intensity)
    negatives_count = get_chem_frag_counts(negatives, chem_to_frag_events, min_ms1_intensity)

    # TP = positive instances that are good only
    tp = [chem for chem in positives if positives_count[chem]['good'] > 0 and positives_count[chem]['bad'] == 0]

    # FP = negative instances that are fragmented (both good + bad)
    fp = [chem for chem in negatives if negatives_count[chem]['good'] > 0 or negatives_count[chem]['bad'] > 0]

    # FN = positive instances that are not fragmented at all + positive instances that are bad only
    fn = [chem for chem in positives if \
          (positives_count[chem]['good'] == 0 and positives_count[chem]['bad'] == 0) or \
          (positives_count[chem]['good'] == 0 and positives_count[chem]['bad'] > 0)]

    tp = len(tp)
    fp = len(fp)
    fn = len(fn)
    prec, rec, f1 = compute_pref_rec_f1(tp, fp, fn)
    return tp, fp, fn, prec, rec, f1

# def compute_performance_scenario_1(controller, chemicals, min_ms1_intensity,
#                                    fullscan_filename, P_peaks_df,
#                                    matching_mz_tol, matching_rt_tol,
#                                    chem_to_frag_events=None):

#     if chem_to_frag_events is None: # read MS2 fragmentation events from pickled controller
#         chem_to_frag_events = get_frag_events(controller, 2)

#     # match xcms picked ms1 peaks to fragmentation peaks
#     detected_ms1 = df_to_chemicals(P_peaks_df, fullscan_filename)
#     matches_fullscan = match(detected_ms1, chemicals, matching_mz_tol, matching_rt_tol, verbose=False)
#     matched_frags = set(matches_fullscan.values())
#     print('%d/%d %d/%d' % (len(matches_fullscan), len(detected_ms1), len(matched_frags), len(chemicals)))

#     # ms1 peaks that are also fragmented
#     positives = []
#     for ms1_peak in matches_fullscan:
#         frag_peak = matches_fullscan[ms1_peak]
#         frag_events = chem_to_frag_events[frag_peak]
#         if len(frag_events) > 0:
#             positives.append(frag_peak)

#     # fragmentation peaks that are not in ms1 peaks
#     negatives = []
#     for frag_peak in chemicals:
#         if frag_peak not in matched_frags:
#             frag_events = chem_to_frag_events[frag_peak]
#             if len(frag_events) > 0:
#                 negatives.append(frag_peak)

#     positives_count = get_chem_frag_counts(positives, chem_to_frag_events, min_ms1_intensity)
#     negatives_count = get_chem_frag_counts(negatives, chem_to_frag_events, min_ms1_intensity)

#     # peaks from ground truth (found in full-scan files) that are fragmented above the minimum intensity threshold
#     tp = [chem for chem in positives if positives_count[chem]['good'] > 0 and positives_count[chem]['bad'] == 0]
#     tp = len(tp)

#     # peaks from ground truth that are not fragmented + peaks from ground truth that are fragmented below the minimum intensity threshold.
#     fp = len(detected_ms1) - tp

#     # peaks not from ground truth that are fragmented above the minimum intensity threshold.
#     fn = [chem for chem in negatives if negatives_count[chem]['good'] > 0 and negatives_count[chem]['bad'] == 0]
#     fn = len(fn)

#     prec, rec, f1 = compute_pref_rec_f1(tp, fp, fn)
#     return tp, fp, fn, prec, rec, f1

def compute_performance_scenario_2(controller, dataset, min_ms1_intensity,
                                   fullscan_filename, fragfile_filename,
                                   fullscan_peaks_df, fragmentation_peaks_df,
                                   matching_mz_tol, matching_rt_tol,
                                   chem_to_frag_events=None):
    if chem_to_frag_events is None:  # read MS2 fragmentation events from pickled controller
        chem_to_frag_events = get_frag_events(controller, 2)

    # load the list of xcms-picked peaks
    detected_from_fullscan = df_to_chemicals(fullscan_peaks_df, fullscan_filename)
    detected_from_fragfile = df_to_chemicals(fragmentation_peaks_df, fragfile_filename)

    # match with xcms peak-picked ms1 data from fullscan file
    matches_fullscan = match(dataset, detected_from_fullscan, matching_mz_tol, matching_rt_tol, verbose=False)

    # match with xcms peak-picked ms1 data from fragmentation file
    matches_fragfile = match(dataset, detected_from_fragfile, matching_mz_tol, matching_rt_tol, verbose=False)

    # check if matched and set a flag to indicate that
    update_matched_status(dataset, matches_fullscan, matches_fragfile)

    # True positive: a peak that is fragmented above the minimum MS1 intensity and is picked by XCMS from
    # the MS1 information in the DDA file and is picked in the fullscan file.
    found_in_both = list(filter(lambda x: x.found_in_fullscan and x.found_in_fragfile, dataset))
    frag_count = get_chem_frag_counts(found_in_both, chem_to_frag_events, min_ms1_intensity)
    tp = [chem for chem in found_in_both if frag_count[chem]['good'] > 0 and frag_count[chem]['bad'] == 0]
    tp = len(tp)

    # False positive: any peak that is above minimum intensity and is picked by XCMS
    # from the DDA file but is not picked from the fullscan.
    found_in_dda_only = list(filter(lambda x: not x.found_in_fullscan and x.found_in_fragfile, dataset))
    frag_count = get_chem_frag_counts(found_in_dda_only, chem_to_frag_events, min_ms1_intensity)
    fp = [chem for chem in found_in_dda_only if frag_count[chem]['good'] > 0 and frag_count[chem]['bad'] == 0]
    fp = len(fp)

    # False negative: any peak that is picked from fullscan data, and is not fragmented, or
    # is fragmented below the minimum intensity.
    found_in_fullscan = list(filter(lambda x: x.found_in_fullscan, dataset))
    fn = len(found_in_fullscan) - tp

    prec, rec, f1 = compute_pref_rec_f1(tp, fp, fn)
    return tp, fp, fn, prec, rec, f1

def get_frag_events(controller, ms_level):
    '''
    Gets the fragmentation events for all chemicals for an ms level from the controller
    :param controller: A Top-N controller object
    :param ms_level: The MS-level (usually 2)
    :return: A dictionary where keys are chemicals and values are a list of fragmentation events
    '''
    filtered_frag_events = list(filter(lambda x: x.ms_level == ms_level, controller.mass_spec.fragmentation_events))
    chem_to_frag_events = defaultdict(list)
    for frag_event in filtered_frag_events:
        key = frag_event.chem
        chem_to_frag_events[key].append(frag_event)
    return dict(chem_to_frag_events)

def count_frag_events(chem, chem_to_frag_events, min_ms1_intensity):
    '''
    Counts how many good and bad fragmentation events for each chemical (key).
    Good fragmentation events are defined as fragmentation events that occur when at the time of fragmentation,
    the chemical MS1 intensity is above the min_ms1_intensity threshold.
    :param chem: the chemical to count
    :param chem_to_frag_events: a dictionary of chemicals to frag events (from get_frag_events above())
    :return: a tuple of good and bad fragmentation event counts
    '''
    frag_events = chem_to_frag_events[chem]
    good_count = 0
    bad_count = 0
    for frag_event in frag_events:
        chem = frag_event.chem
        query_rt = frag_event.query_rt
        if get_absolute_intensity(chem, query_rt) < min_ms1_intensity:
            bad_count += 1
        else:
            good_count += 1
    return good_count, bad_count

def get_chem_frag_counts(chem_list, chem_to_frag_events, min_ms1_intensity):
    # get the count of good/bad fragmentation events for all chemicals in chem_list
    results = {}
    for i in range(len(chem_list)):
        chem = chem_list[i]
        try:
            good_count, bad_count = count_frag_events(chem, chem_to_frag_events, min_ms1_intensity)
        except KeyError:
            good_count = 0
            bad_count = 0
        results[chem] = {
            'good': good_count,
            'bad': bad_count
        }
    return results

def update_matched_status(dataset, matches_fullscan, matches_fragfile):
    '''
    Update a boolean flag in the Chemical object that tells us if it is found in fullscan or fragmentation data
    :param dataset: a list of Chemicals
    :param matches_fullscan: the result of matching Chemicals in dataset to fullscan file
    :param matches_fragfile: the result of matching Chemicals in dataset to fragmentation file
    :return: None, but the Chemical objects in dataset is modified
    '''
    found_in_fullscan = 0
    found_in_fragfile = 0
    for chem in dataset:
        if matches_fullscan is not None:  # check if a match is found in fullscan mzML
            if chem in matches_fullscan:
                chem.found_in_fullscan = True
                found_in_fullscan += 1
            else:
                chem.found_in_fullscan = False

        if matches_fragfile is not None:  # check if a match is found in fragmentation mzML
            if chem in matches_fragfile:
                chem.found_in_fragfile = True
                found_in_fragfile += 1
            else:
                chem.found_in_fragfile = False

    print('Matched %d/%d in fullscan data, %d/%d in fragmentation data' % (found_in_fullscan, len(dataset),
                                                                           found_in_fragfile, len(dataset)))

def compute_pref_rec_f1(tp, fp, fn):
    prec = tp / (tp + fp)
    rec = tp / (tp + fn)
    f1 = (2 * prec * rec) / (prec + rec)
    return prec, rec, f1

def calculate_performance(params):
    # get parameters
    fragfile = params['fragfile']
    N = params['N']
    rt_tol = params['rt_tol']
    roi_mz_tol = params['roi_mz_tol']
    roi_min_ms1_intensity = params['roi_min_ms1_intensity']
    fragmentation_min_ms1_intensity = params['fragmentation_min_ms1_intensity']
    min_rt = params['min_rt']
    max_rt = params['max_rt']
    roi_min_length = params['roi_min_length']
    fullscan_filename = params['fullscan_filename']
    P_peaks_df = params['P_peaks_df']
    Q_peaks_df = params['Q_peaks_df']
    matching_mz_tol = params['matching_mz_tol']
    matching_rt_tol = params['matching_rt_tol']
    scenario = params['scenario']

    controller_file = params['controller_file']
    chemicals_file = params['chemicals_file']

    if chemicals_file.endswith('.p'):
        print('Loading chemicals')
        chemicals = load_obj(chemicals_file)
    else:
        print('Extracting chemicals')
        chemicals = get_chemicals(chemicals_file, roi_mz_tol, roi_min_ms1_intensity, min_rt, max_rt,
                                  min_length=roi_min_length)

    if type(chemicals) == list:
        chemicals = np.array(chemicals)

    if controller_file.endswith('.p'):
        print('Loading fragmentation events')
        controller = load_obj(controller_file)
        chem_to_frag_events = None
    else:
        print('Extracting fragmentation events')
        controller = None
        precursor_df = get_precursor_info(controller_file)
        chem_to_frag_events = get_chem_to_frag_events(chemicals, precursor_df)

    # compute performance under each scenario
    print('Computing performance under scenario %d' % scenario)
    tp, fp, fn, prec, rec, f1 = 0, 0, 0, 0, 0, 0
    if scenario == 1:
        tp, fp, fn, prec, rec, f1 = compute_performance_scenario_1(controller, chemicals,
                                                                   fragmentation_min_ms1_intensity,
                                                                   fullscan_filename, P_peaks_df,
                                                                   matching_mz_tol, matching_rt_tol,
                                                                   chem_to_frag_events=chem_to_frag_events)
    elif scenario == 2:
        fragfile_filename = os.path.basename(fragfile)
        tp, fp, fn, prec, rec, f1 = compute_performance_scenario_2(controller, chemicals,
                                                                   fragmentation_min_ms1_intensity,
                                                                   fullscan_filename, fragfile_filename,
                                                                   P_peaks_df, Q_peaks_df, matching_mz_tol,
                                                                   matching_rt_tol,
                                                                   chem_to_frag_events=chem_to_frag_events)

    return N, rt_tol, scenario, tp, fp, fn, prec, rec, f1

def get_chem_to_frag_events(chemicals, ms1_df):
    # used for searching later
    min_rts = np.array([min(chem.chromatogram.raw_rts) for chem in chemicals])
    max_rts = np.array([max(chem.chromatogram.raw_rts) for chem in chemicals])
    min_mzs = np.array([min(chem.chromatogram.raw_mzs) for chem in chemicals])
    max_mzs = np.array([max(chem.chromatogram.raw_mzs) for chem in chemicals])

    # loop over each fragmentation event in ms1_df, attempt to match it to chemicals
    chem_to_frag_events = defaultdict(list)
    for idx, row in ms1_df.iterrows():
        query_rt = row['ms1_scan_rt']
        query_mz = row['ms1_mz']
        query_intensity = row['ms1_intensity']
        scan_id = row['ms2_scan_id']

        chem = None
        idx = _get_chem_indices(query_mz, query_rt, min_mzs, max_mzs, min_rts, max_rts)
        if len(idx) == 1:  # single match
            chem = chemicals[idx][0]

        elif len(
                idx) > 1:  # multiple matches, find the closest in intensity to query_intensity at the time of fragmentation
            matches = chemicals[idx]
            possible_intensities = np.array([get_absolute_intensity(chem, query_rt) for chem in matches])
            closest = find_nearest_index_in_array(possible_intensities, query_intensity)
            chem = matches[closest]

        # create frag event for the given chem
        if chem is not None:
            ms_level = 2
            peaks = []  # we don't know which ms2 peaks are linked to this chem object
            # key = get_key(chem)
            frag_event = FragmentationEvent(chem, query_rt, ms_level, peaks, scan_id)
            chem_to_frag_events[chem].append(frag_event)
    return dict(chem_to_frag_events)

def get_chemicals(mzML_file, mz_tol, min_ms1_intensity, start_rt, stop_rt, min_length=1):
    '''
    Extract ROI from an mzML file and turn them into UnknownChemical objects
    :param mzML_file: input mzML file
    :param mz_tol: mz tolerance for ROI extraction
    :param min_ms1_intensity: ROI will only be kept if it has one point above this threshold
    :param start_rt: start RT to extract ROI
    :param stop_rt: end RT to extract ROI
    :return: a list of UnknownChemical objects
    '''
    min_intensity = 0
    good_roi, junk = make_roi(mzML_file, mz_tol=mz_tol, mz_units='ppm', min_length=min_length,
                              min_intensity=min_intensity, start_rt=start_rt, stop_rt=stop_rt)

    # keep ROI that have at least one point above the minimum to fragment threshold
    keep = []
    for roi in good_roi:
        if np.count_nonzero(np.array(roi.intensity_list) > min_ms1_intensity) > 0:
            keep.append(roi)

    ps = None  # unused
    rtcc = RoiToChemicalCreator(ps, keep)
    chemicals = np.array(rtcc.chemicals)
    return chemicals

def evaluate_serial(all_params):
    results = []
    for params in all_params:
        res = calculate_performance(params)
        results.append(res)
        print('N=%d rt_tol=%d scenario=%d tp=%d fp=%d fn=%d prec=%.3f rec=%.3f f1=%.3f\n' % res)
    result_df = pd.DataFrame(results, columns=['N', 'rt_tol', 'scenario', 'TP', 'FP', 'FN', 'Prec', 'Rec', 'F1'])
    return result_df

def evaluate_parallel(all_params, pushed_dict=None):
    import ipyparallel as ipp
    rc = ipp.Client()
    dview = rc[:]  # use all engines
    with dview.sync_imports():
        import os
        from vimms.Common import load_obj

    if pushed_dict is not None:
        dview.push(pushed_dict)

    results = dview.map_sync(calculate_performance, all_params)
    result_df = pd.DataFrame(results, columns=['N', 'rt_tol', 'scenario', 'TP', 'FP', 'FN', 'Prec', 'Rec', 'F1'])
    return result_df


#!/usr/bin/python3
import sys
import os

import peewee

from slugify import slugify

sys.path.insert(1, os.path.join(sys.path[0], '../src'))

from faker import Faker
from main import database, db, Proposal, Community, User, Tag

import random

from peewee import fn

fake = Faker('sv_SE')

def get_random_user():
    return User.select(User.id).order_by(fn.Random()).get()

def get_random_text():
  return fake.text() + '\n' + fake.text()

def get_random_sentence():
  return fake.sentence()

items = [
  ("Natur, Miljö och Resurser", "politik"),
  ("Konst och Kultur", "politik"),
  ("Socialpolitik och Mönsterbrytning", "politik"),
  ("Integration och Arbetsmarknaden", "politik"),
  ("Freds- och Försvarspolitik", "politik"),
  ("Bostad, Städer och Landsbygd", "politik"),
  ("IT och Digitala Rättigheter", "politik"),
  ("Skola, Utbildning och Forskning", "politik"),
  ("Finans-, Ekonomi- och Skattepolitik", "politik"),
  ("Asylpolitik", "politik"),
  ("Ny Politisk Kultur", "politik"),
  ("Transport", "politik"),
  ("Entreprenör, Näringsliv och Handel", "politik"),
  ("Religion och Kyrka", "politik"),
  ("Sundhet, Livskvalité och Familjeliv", "politik"),
  ("Rättspolitik", "politik"),
  ("EU- och Utrikespolitik", "politik"),
  ("Mat, Jordbruk och Fiske", "politik"),
  ("Inrikespolitik och Offentliga Sektorn", "politik"),
  ("Principer och Värderingar", "politik"),
  ("Mångfald och Jämställdhet", "politik"),
  ("Förslag under utveckling", "politiska-förslag"),
  ("Förslag i samråd", "politiska-förslag"),
  ("Förslag som söker medlemmars stöd", "politiska-förslag"),
  ("Förslag under utveckling", "politiska-förslag"),
  ("Politisk katalog (snälla betygsätt)", "politiska-förslag"),
  ("Annullerade förslag", "politiska-förslag"),
  ("Söker medarbetare", "politiska-förslag"),
  ("Visioner i samråd", "politisk-vision"),
  ("Visioner som väntar på behandling i Politiska Forum", "politisk-vision"),
  ("Visioner under utveckling", "politisk-vision"),
  ("Antagna visioner", "politisk-vision"),
  ("Hemsidan", "hemsidan"),
  ("Funktioner och önskemål", "meta"),
  ("Support", "meta"),
  ("Felanmälan", "meta"),
  ("Lokal Karlshamn", "lokalförening")
]

me = User.get(User.username == 'ganehag')

for title, tag in items:
  c = Community()
  c.name = slugify(title)
  c.description = title
  c.maintainer = me

  with database.atomic():
    try:
      c.save()
    except peewee.IntegrityError as e:
      pass

  c = Community.get(Community.name == slugify(title))

  t = Tag()
  t.title = tag
  t.community = c

  with database.atomic():
    try:
      t.save()
    except peewee.IntegrityError as e:
      pass

  continue

  for z in range(0, 5):
    p = Proposal()
    p.title = fake.sentence(nb_words=5, variable_nb_words=False)
    p.author = get_random_user()
    p.community = c
    p.content = get_random_text()
    p.update_search_index()
    p.published = True
    p.upvotes = random.randint(0, 10000)
    p.downvotes = random.randint(0, 10000)
    try:
      with database.atomic():
        p.save()
    except peewee.IntegrityError as e:
      print(e)
      print("Duplicate title", p.title)


import json

#Load and Open the external json file
with open("data.json") as json_file:
    json_data = json.load(json_file)

#Get user id from user and search the loaded json data displaying records that match that id
userId = int(input("Enter your id: ")) - 1

if int(json_data[userId]["id"]) - 1 == userId:
    print(
        "User ID:\t" + str(json_data[userId]["id"]) + "\n" +
        "First Name:\t" + json_data[userId]["first_name"] + "\n" +
        "Last Name:\t" + json_data[userId]["last_name"] + "\n" +
        "Email:\t\t" + json_data[userId]["email"] + "\n" +
        "Gender:\t\t" + json_data[userId]["gender"])

else:
    print("User not found")

#Declare a variable to hold the number of Males

count = 0

#Loop through the json data adding 1 to count where gender is equal to "Male"
for i in range(len(json_data)):
    if json_data[i]["gender"] == "Male":
        count += 1

#Display the count of Males and Females where Females = Total - count
print("\nData Summary ..........................................")
print("\tNumber of Females: \t" + str(len(json_data) - count))
print("\tNumber of Males: \t" + str(count))