import re

import jieba
import numpy as np

def extract_keyword(bug_description):
    # it will remove the stopwords.
    f_stopwords = open('stopwords1893.txt', encoding='utf-8')
    stopwords = f_stopwords.read()
    stopwords = stopwords.split('\n')

    description = jieba.lcut(bug_description.strip(), cut_all=False)
    keyword_1 = []
    for word in description:  # filter stopwords
        # remove the numbers and the letters in this word
        word_ = re.sub(r"[A-Za-z0-9]+", "", word).strip()

        if word_ not in stopwords and word_ is not '':
            keyword_1.append(word_)

    return keyword_1

if __name__ == '__main__':
    project_list = ['MyListening', '2048', 'HuaWei', 'HuJiang', 'TravelDiary', 'Wonderland']

    for project in project_list:
        keywords = []

        filename = '/TIURP_JSEP/reduced_bug_description/' + project + '.txt'
        with open(filename, 'r', encoding='utf-8') as f:
            for line in f:
                keywords.append(extract_keyword(line))

        corpus = []
        for index, words in enumerate(keywords):
            # print(index, words)
            corpus.extend(words)

        corpus = list(set(corpus))  # P1: it has 781 words.
        print('Corpus size: ', len(corpus))
        print(corpus)

        text_feature_vector = np.zeros((len(keywords), len(corpus)), dtype=int)
        for index, keyword in enumerate(keywords):
            if len(keyword) != 0:
                for i in range(len(keyword)):
                    for j in range(len(corpus)):
                        if keyword[i] == corpus[j]:
                            text_feature_vector[index][j] += 1

        print('The shape of text vector: ', text_feature_vector.shape, '\n')

        text_filename = './fasttext_text_vector/' + project + '.txt'
        np.savetxt(text_filename, text_feature_vector, fmt='%d', delimiter=' ', encoding='utf-8')


import maps
from mock import patch

from tendrl.commons import objects
from tendrl.commons.objects.job import Job

def load(*args):
    parent = Job(job_id="Test Parent Job Id", status="Active",
                 payload=maps.NamedDict())
    if args[0]:
        parent.errors = "Error Message"
        parent.children = ["Test_child"]
    return parent

# Testing __init__
def test_constructor():
    '''Testing for constructor involves checking if all needed

    variables are declared initialized
    '''
    job = Job()
    assert job.job_id is None
    # Passing Dummy Values
    job = Job(job_id="Test job id", payload="Test Payload",
              status=True, errors=None, children=None,
              locked_by=None, output="Job Done")
    assert job.output == "Job Done"

# Testing save
@patch.object(objects.BaseObject, "save", return_value=None)
def test_save(mock_save):
    job = Job()
    payload = maps.NamedDict()
    payload['parent'] = "Test Parent Job Id"
    job.payload = payload
    with patch.object(objects.BaseObject, 'load') as mock_load:
        mock_load.return_value = load(False)
        job.status = "true"
        job.save()
    with patch.object(objects.BaseObject, 'load') as mock_load:
        mock_load.return_value = load(False)
        job.status = "failed"
        job.save()
    with patch.object(objects.BaseObject, 'load') as mock_load:
        mock_load.return_value = load(True)
        job.status = "failed"
        job.save()

# Testing render
def test_render():
    job = Job()
    assert job.render() is not None


import cv2

img = cv2.imread('../img/blank_500.jpg')

# sans-serif small
cv2.putText(img, "Plain", (50, 30), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0,0))
# sans-serif normal
cv2.putText(img, "Simplex", (50, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0,0))
# sans-serif bold
cv2.putText(img, "Duplex", (50, 110), cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0,0))
# sans-serif normall X2  ---①
cv2.putText(img, "Simplex", (200, 110), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,250))

# serif small
cv2.putText(img, "Complex Small", (50, 180), cv2.FONT_HERSHEY_COMPLEX_SMALL, \
            1, (0, 0,0))
# serif normal
cv2.putText(img, "Complex", (50, 220), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0,0))
# serif bold
cv2.putText(img, "Triplex", (50, 260), cv2.FONT_HERSHEY_TRIPLEX, 1, (0, 0,0))
# serif normal X2  ---②
cv2.putText(img, "Complex", (200, 260), cv2.FONT_HERSHEY_TRIPLEX, 2, (0,0,255))

# hand-wringing sans-serif
cv2.putText(img, "Script Simplex", (50, 330), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, \
            1, (0, 0,0))
# hand-wringing serif
cv2.putText(img, "Script Complex", (50, 370), cv2.FONT_HERSHEY_SCRIPT_COMPLEX, \
            1, (0, 0,0))

# sans-serif + italic ---③
cv2.putText(img, "Plain Italic", (50, 430), \
            cv2.FONT_HERSHEY_PLAIN | cv2.FONT_ITALIC, 1, (0, 0,0))
# sarif + italic
cv2.putText(img, "Complex Italic", (50, 470), \
            cv2.FONT_HERSHEY_COMPLEX | cv2.FONT_ITALIC, 1, (0, 0,0))

cv2.imshow('draw text', img)
cv2.waitKey()
cv2.destroyAllWindows()


from commands import Command

from prompt_toolkit.shortcuts import clear

class Update(Command):
    keywords = ["update", "up", "=", "edit"]

    help_text = """
                {keyword}
                {id}
                Summary: Get help for a command.
                Usage: {keyword} <command>

    """

    def do_command(self, *args):
        if self.validate_task_selection(*args[0]):
            self.print_style_text(
                f"<error>{self.validate_task_selection(*args[0])}</error>")
            return

        DATA = self.load_todo()
        description = ' '.join(args[1:])
        _id = int(args[0]) - 1

        try:
            DATA["tasks"][_id]["description"] = description

        except IndexError:
            self.print_style_text(
                f"<error>{self.validate_task_selection(*args[0])}</error>")
            return

        self.save_todo(DATA)

        clear()
        list_open = self.todo.commands.get("list")
        list_open.do_command("list")

        self.print_style_text(f"<success>Task {args[0]} updated</success>")


#!/usr/bin/env python

import pygame
import os
from rational import rational
from random import choice

pygame.init()

#For Compiling
def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

#Basic Information
CAPTION = "Physical pi calculator"
ICON = resource_path("icon.png")
WIDTH = 640
HEIGHT = 360
TARGET_FRAME_RATE = 144
MAGNIFYING_RATE = 60

#Colors
BLACK   = (  0,   0,   0)
RED     = (255,   0,   0)
GREEN   = (  0, 255,   0)
BLUE    = (  0,   0, 255)
YELLOW  = (255, 255,   0)
MAGENTA = (255,   0, 255)
CYAN    = (  0, 255, 255)
WHITE   = (255, 255, 255)

#Extended Colors
LIGHT_GRAY  = (240, 240, 240)
LIGHT_GREEN = (144, 238, 144)
DARK_GRAY   = (120, 120, 120)

#Preset
RUNNING = True
WINDOW = pygame.display
WINDOW.set_caption(CAPTION)
WINDOW.set_icon(pygame.image.load(ICON))
SCREEN = WINDOW.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)

#Global Variables
FLOOR_HEIGHT = HEIGHT // 4
WALL_THICKNESS = 80
PAUSED = True
STATUS = "Simulate"
TEXT_BOX_INFO = {"Size": "", "Mass": "", "Position": "", "Velocity": "", "Color": ""}
ERROR_MESSAGE_TIMER = 0
PAUSE_BUTTON_FLAG = True
ERROR_MESSAGE = ""
CLICKED_TEXT_BUTTON = None
SPEED = 1

COLLISION = 0
COLLISION_TIMER = rational(0)
PAST_COLLIDE = 0
PRED_COLLIDER = None

collider = []

#Classes
class box():
    def __init__(self, length: rational, mass: rational, position: rational, velocity: rational, color: tuple):
        self.length = length
        self.mass = mass
        self.position = position
        self.velocity = velocity
        self.color = color
    def __repr__(self):
        return "Box{%skg, %sm, %sm/s, Color: %s}" % (mass, position, velocity, color)
    def info(self):
        return (self.length, self.mass, self.position, self.velocity, self.color)
    def update(self, colider: list):
        pass
    def render(self):
        pygame.draw.rect(SCREEN, self.color, [float(WALL_THICKNESS + self.position * MAGNIFYING_RATE), float(HEIGHT - FLOOR_HEIGHT - self.length * MAGNIFYING_RATE), float(self.length * MAGNIFYING_RATE), float(self.length * MAGNIFYING_RATE)])
    def copy(self):
        return box(self.length, self.mass, self.position, self.velocity, self.color)

#Functions
def print_text(text = "", pos = (0, 0), center_align = False, font = None, font_size = 18, color = BLACK, alpha = 255):
    split_text = list(text.split("\n"))
    font = pygame.font.SysFont(font, font_size)
    for i in range(len(split_text)):
        txt = font.render(split_text[i], True, color)
        txt.set_alpha(alpha)
        if center_align:
            rect = txt.get_rect()
            rect.center = (pos[0], pos[1] + (2 * i - len(split_text) + 1) * font_size // 2)
            SCREEN.blit(txt, rect)
        else:
            SCREEN.blit(txt, (pos[0], pos[1] + font_size * i))

def FLOOR_relocation():
    global FLOOR_HEIGHT
    FLOOR_HEIGHT = HEIGHT // 4

def render_restart_button():
    pygame.draw.rect(SCREEN, RED, [WALL_THICKNESS // 8, WALL_THICKNESS // 8, WALL_THICKNESS * 3 // 4, WALL_THICKNESS * 3 // 8], 0, WALL_THICKNESS * 3 // 24)
    print_text("RS", (WALL_THICKNESS // 2, WALL_THICKNESS * 5 // 16), True, None, 24, WHITE)

def restart_button_clicked():
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()[0]
    if click and WALL_THICKNESS // 8 <= mouse[0] < WALL_THICKNESS * 7 // 8 and WALL_THICKNESS // 8 <= mouse[1] < WALL_THICKNESS // 2:
        return True
    return False

def render_pause_button():
    if PAUSED:
        bgcolor = LIGHT_GREEN
        fgcolor = BLACK
        text = "RUN"
    else:
        bgcolor = MAGENTA
        fgcolor = WHITE
        text = "PAUSE"
    pygame.draw.rect(SCREEN, bgcolor, [WALL_THICKNESS // 8, WALL_THICKNESS * 5 // 8, WALL_THICKNESS * 3 // 4, WALL_THICKNESS * 3 // 8], 0, WALL_THICKNESS * 3 // 24)
    print_text(text, (WALL_THICKNESS // 2, WALL_THICKNESS * 13 // 16), True, None, 18, fgcolor)

def pause_button_clicked():
    global PAUSE_BUTTON_FLAG
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()[0]
    if click and WALL_THICKNESS // 8 <= mouse[0] < WALL_THICKNESS * 7 // 8 and WALL_THICKNESS * 5 // 8 <= mouse[1] < WALL_THICKNESS:
        if PAUSE_BUTTON_FLAG == True:
            PAUSE_BUTTON_FLAG = False
            return True
        return False
    PAUSE_BUTTON_FLAG = True
    return False

def render_collision_counter():
    print_text("Collisions: %d" % COLLISION, (WALL_THICKNESS * 9 // 8, WALL_THICKNESS // 8), False, None, 24)

def render_pause_ui():
    if not PAUSED:
        return
    print_text("Paused", (WIDTH - 64, WALL_THICKNESS // 8), False, None, 24)

def collider_update():
    global COLLISION, COLLISION_TIMER, PRED_COLLIDER, PAST_COLLIDE, collider
    while COLLISION_TIMER < rational(1, TARGET_FRAME_RATE): #While there is not-processed collision in the current frame
        if PRED_COLLIDER != None: #if it is valid collision
            #Move to the collision
            for i in collider:
                i.position += i.velocity * PAST_COLLIDE
            #Collision with the wall
            if PRED_COLLIDER[0] == "Wall":
                PRED_COLLIDER[1].velocity = -PRED_COLLIDER[1].velocity
            #Collision between colliders
            else:
                c1 = PRED_COLLIDER[0]
                c2 = PRED_COLLIDER[1]
                m1 = c1.mass
                m2 = c2.mass
                v1 = c1.velocity
                v2 = c2.velocity
                c1.velocity = (v1 * (m1 - m2) + 2 * m2 * v2) / (m1 + m2)
                c2.velocity = (v2 * (m2 - m1) + 2 * m1 * v1) / (m2 + m1)
            COLLISION += 1
        #Next collision prediction
        NCT = rational("inf") #Next Collision Time
        PRED_COLLIDER = None
        for i in collider:
            if i.velocity < 0:
                CCT = i.position / abs(i.velocity) #Current Collision Time
                if CCT < NCT:
                    NCT = CCT
                    PRED_COLLIDER = ["Wall", i]
            for j in collider:
                L, R = sorted([i, j], key = lambda x: x.position)
                if R.velocity < L.velocity:
                    CCT = (R.position - L.position - L.length) / (L.velocity - R.velocity)
                    if CCT < NCT:
                        NCT = CCT
                        PRED_COLLIDER = [L, R]
        #End of prediction
        PAST_COLLIDE = NCT
        COLLISION_TIMER += NCT
    if COLLISION_TIMER == rational("inf"):
        for i in collider:
            i.position += i.velocity * rational(1, TARGET_FRAME_RATE)
    else:
        MOV_TIME = rational(1, TARGET_FRAME_RATE) - (COLLISION_TIMER - PAST_COLLIDE)
        for i in collider:
            i.position += i.velocity * MOV_TIME
        COLLISION_TIMER -= rational(1, TARGET_FRAME_RATE)
        PAST_COLLIDE = COLLISION_TIMER

def render_size_input_box():
    pygame.draw.rect(SCREEN, WHITE, [WIDTH * 2 // 5, HEIGHT * 7 // 24, WIDTH * 7 // 20, 24])

def size_button_clicked():
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()[0]
    if click and WIDTH * 2 // 5 <= mouse[0] < WIDTH * 3 // 4 and HEIGHT * 7 // 24 <= mouse[1] < HEIGHT * 7 // 24 + 24:
        return True
    return False

def render_mass_input_box():
    pygame.draw.rect(SCREEN, WHITE, [WIDTH * 2 // 5, HEIGHT * 9 // 24, WIDTH * 7 // 20, 24])

def mass_button_clicked():
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()[0]
    if click and WIDTH * 2 // 5 <= mouse[0] < WIDTH * 3 // 4 and HEIGHT * 9 // 24 <= mouse[1] < HEIGHT * 9 // 24 + 24:
        return True
    return False

def render_position_input_box():
    pygame.draw.rect(SCREEN, WHITE, [WIDTH * 2 // 5, HEIGHT * 11 // 24, WIDTH * 7 // 20, 24])

def position_button_clicked():
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()[0]
    if click and WIDTH * 2 // 5 <= mouse[0] < WIDTH * 3 // 4 and HEIGHT * 11 // 24 <= mouse[1] < HEIGHT * 11 // 24 + 24:
        return True
    return False

def render_velocity_input_box():
    pygame.draw.rect(SCREEN, WHITE, [WIDTH * 2 // 5, HEIGHT * 13 // 24, WIDTH * 7 // 20, 24])

def velocity_button_clicked():
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()[0]
    if click and WIDTH * 2 // 5 <= mouse[0] < WIDTH * 3 // 4 and HEIGHT * 13 // 24 <= mouse[1] < HEIGHT * 13 // 24 + 24:
        return True
    return False

def render_color_input_box():
    pygame.draw.rect(SCREEN, WHITE, [WIDTH * 2 // 5, HEIGHT * 15 // 24, WIDTH * 7 // 20, 24])

def color_button_clicked():
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()[0]
    if click and WIDTH * 2 // 5 <= mouse[0] < WIDTH * 3 // 4 and HEIGHT * 15 // 24 <= mouse[1] < HEIGHT * 15 // 24 + 24:
        return True
    return False

def render_add_button():
    pygame.draw.rect(SCREEN, RED, [WIDTH * 7 // 24, HEIGHT * 19 // 24, WIDTH // 6, HEIGHT // 12], 0, HEIGHT // 30)
    print_text("ADD", (WIDTH * 3 // 8, HEIGHT * 5 // 6), True, None, 24, WHITE)

def add_button_clicked():
    global PAUSE_BUTTON_FLAG
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()[0]
    if click and WIDTH * 7 // 24 <= mouse[0] < WIDTH * 11 // 24 and HEIGHT * 19 // 24 <= mouse[1] < HEIGHT * 21 // 24:
        if PAUSE_BUTTON_FLAG == True:
            PAUSE_BUTTON_FLAG = False
            return True
        return False
    PAUSE_BUTTON_FLAG = True
    return False

def render_done_button():
    pygame.draw.rect(SCREEN, GREEN, [WIDTH * 13 // 24, HEIGHT * 19 // 24, WIDTH // 6, HEIGHT // 12], 0, HEIGHT // 30)
    print_text("DONE", (WIDTH * 5 // 8, HEIGHT * 5 // 6), True, None, 24, BLACK)

def done_button_clicked():
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()[0]
    if click and WIDTH * 13 // 24 <= mouse[0] < WIDTH * 17 // 24 and HEIGHT * 19 // 24 <= mouse[1] < HEIGHT * 21 // 24:
        return True
    return False

def render_speed_ui():
    print_text("Speed: x%s" % SPEED, (WALL_THICKNESS * 9 // 8, WALL_THICKNESS // 8 + 18), False, None, 24)

def render_version_ui():
    print_text("Version: A1.00", (WIDTH - 50, HEIGHT - 14), True, None, 18, WHITE)

#Main Code
collider.append(box(length = rational(1), mass = rational(1), position = rational(2), velocity = rational(0), color = RED))
collider.append(box(length = rational(1), mass = rational(1), position = rational(5), velocity = rational(-1), color = BLUE))

#Main Loop
while RUNNING:
    pygame.time.Clock().tick(TARGET_FRAME_RATE)

    #Simulating
    if STATUS == "Simulate":
        #Render Background, Floor and Wall
        SCREEN.fill(LIGHT_GRAY)
        pygame.draw.rect(SCREEN, BLACK, [0, HEIGHT - FLOOR_HEIGHT, WIDTH, FLOOR_HEIGHT])
        pygame.draw.rect(SCREEN, BLACK, [0, 0, WALL_THICKNESS, HEIGHT])

        #Render Colliders
        for i in collider:
            i.render()

        #Render UIs
        render_restart_button()
        render_pause_button()
        render_collision_counter()
        render_pause_ui()
        #render_speed_ui()
        render_version_ui()

        #Update Collider Status
        if not PAUSED:
            collider_update()

        #Interrupt
        if restart_button_clicked():
            STATUS = "Reset"
            collider = []
            TEXT_BOX_INFO = {"Size": "", "Mass": "", "Position": "", "Velocity": "", "Color": ""}
            COLLISION = 0
            COLLISION_TIMER = rational(0)
            PRED_COLLIDER = None
            SPEED = 1
        if pause_button_clicked():
            if PAUSED:
                PAUSED = False
            else:
                PAUSED = True

    #New Experiment Setting
    if STATUS == "Reset":
        #Render Background and Input Textbox
        SCREEN.fill(DARK_GRAY)
        print_text("Add new collider", (WIDTH // 2, HEIGHT // 6), True, None, 36, WHITE)
        print_text("Size    : ", (WIDTH // 4, HEIGHT * 7 // 24), False, None, 24, WHITE)
        print_text("Mass    : ", (WIDTH // 4, HEIGHT * 9 // 24), False, None, 24, WHITE)
        print_text("Position: ", (WIDTH // 4, HEIGHT * 11 // 24), False, None, 24, WHITE)
        print_text("Velocity: ", (WIDTH // 4, HEIGHT * 13 // 24), False, None, 24, WHITE)
        print_text("Color   : ", (WIDTH // 4, HEIGHT * 15 // 24), False, None, 24, WHITE)
        render_size_input_box()
        render_mass_input_box()
        render_position_input_box()
        render_velocity_input_box()
        render_color_input_box()
        render_add_button()
        render_done_button()
        print_text(TEXT_BOX_INFO["Size"], (WIDTH * 2 // 5 + 4, HEIGHT * 7 // 24 + 4), False, None, 24, BLACK)
        print_text(TEXT_BOX_INFO["Mass"], (WIDTH * 2 // 5 + 4, HEIGHT * 9 // 24 + 4), False, None, 24, BLACK)
        print_text(TEXT_BOX_INFO["Position"], (WIDTH * 2 // 5 + 4, HEIGHT * 11 // 24 + 4), False, None, 24, BLACK)
        print_text(TEXT_BOX_INFO["Velocity"], (WIDTH * 2 // 5 + 4, HEIGHT * 13 // 24 + 4), False, None, 24, BLACK)
        print_text(TEXT_BOX_INFO["Color"], (WIDTH * 2 // 5 + 4, HEIGHT * 15 // 24 + 4), False, None, 24, BLACK)

        #Interrupt
        if add_button_clicked():
            try:
                if TEXT_BOX_INFO["Color"] != "":
                    exec("color = %s" % TEXT_BOX_INFO["Color"].upper())
                else:
                    color = choice([RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, LIGHT_GREEN])
                sz = float(TEXT_BOX_INFO["Size"])
                ms = float(TEXT_BOX_INFO["Mass"])
                ps = float(TEXT_BOX_INFO["Position"])
                vc = float(TEXT_BOX_INFO["Velocity"])
                collider.append(box(sz, ms, ps, vc, color))
                ERROR_MESSAGE_TIMER = TARGET_FRAME_RATE * 2
                ERROR_MESSAGE = "Collider added successfully"
                TEXT_BOX_INFO = {"Size": "", "Mass": "", "Position": "", "Velocity": "", "Color": ""}
            except:
                ERROR_MESSAGE_TIMER = TARGET_FRAME_RATE * 2
                ERROR_MESSAGE = "Some of inputs are missing or wrong!"
            CLICKED_TEXT_BUTTON = None

        if done_button_clicked():
            STATUS = "Simulate"
            PAUSED = True
            ERROR_MESSAGE_TIMER = 0
            CLICKED_TEXT_BUTTON = None
        if size_button_clicked():
            CLICKED_TEXT_BUTTON = "Size"
            TEXT_BOX_INFO["Size"] = ""
        if mass_button_clicked():
            CLICKED_TEXT_BUTTON = "Mass"
            TEXT_BOX_INFO["Mass"] = ""
        if position_button_clicked():
            CLICKED_TEXT_BUTTON = "Position"
            TEXT_BOX_INFO["Position"] = ""
        if velocity_button_clicked():
            CLICKED_TEXT_BUTTON = "Velocity"
            TEXT_BOX_INFO["Velocity"] = ""
        if color_button_clicked():
            CLICKED_TEXT_BUTTON = "Color"
            TEXT_BOX_INFO["Color"] = ""
        if CLICKED_TEXT_BUTTON != None:
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_BACKSPACE:
                        if TEXT_BOX_INFO[CLICKED_TEXT_BUTTON] != "":
                            TEXT_BOX_INFO[CLICKED_TEXT_BUTTON] = TEXT_BOX_INFO[CLICKED_TEXT_BUTTON][:-1]
                    elif event.key == pygame.K_TAB:
                        if CLICKED_TEXT_BUTTON == "Size":
                            CLICKED_TEXT_BUTTON = "Mass"
                            TEXT_BOX_INFO["Mass"] = ""
                        elif CLICKED_TEXT_BUTTON == "Mass":
                            CLICKED_TEXT_BUTTON = "Position"
                            TEXT_BOX_INFO["Position"] = ""
                        elif CLICKED_TEXT_BUTTON == "Position":
                            CLICKED_TEXT_BUTTON = "Velocity"
                            CLICKED_TEXT_BUTTON = "Velocity"
                        elif CLICKED_TEXT_BUTTON == "Velocity":
                            CLICKED_TEXT_BUTTON = "Color"
                            TEXT_BOX_INFO["Color"] = ""
                    elif event.key in [pygame.K_RETURN, pygame.K_KP_ENTER]:
                        try:
                            if TEXT_BOX_INFO["Color"] != "":
                                exec("color = %s" % TEXT_BOX_INFO["Color"].upper())
                            else:
                                color = choice([RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, LIGHT_GREEN])
                            sz = float(TEXT_BOX_INFO["Size"])
                            ms = float(TEXT_BOX_INFO["Mass"])
                            ps = float(TEXT_BOX_INFO["Position"])
                            vc = float(TEXT_BOX_INFO["Velocity"])
                            collider.append(box(sz, ms, ps, vc, color))
                            ERROR_MESSAGE_TIMER = TARGET_FRAME_RATE * 2
                            ERROR_MESSAGE = "Collider added successfully"
                            TEXT_BOX_INFO = {"Size": "", "Mass": "", "Position": "", "Velocity": "", "Color": ""}
                            CLICKED_TEXT_BUTTON = "Size"
                        except:
                            ERROR_MESSAGE_TIMER = TARGET_FRAME_RATE * 2
                            ERROR_MESSAGE = "Some of inputs are missing or wrong!"
                    else:
                        TEXT_BOX_INFO[CLICKED_TEXT_BUTTON] += event.unicode
        #Error Message
        if 0 < ERROR_MESSAGE_TIMER:
            print_text(ERROR_MESSAGE, (WIDTH // 2, HEIGHT * 3 / 4), True, None, 18, WHITE, 255 * ERROR_MESSAGE_TIMER // TARGET_FRAME_RATE)
            ERROR_MESSAGE_TIMER -= 1

    #Refresh Screen
    pygame.display.update()

    #Event Processing
    for event in pygame.event.get():

        #Quit Button Action
        if event.type == pygame.QUIT:
            pygame.display.quit()
            RUNNING = False

        #Update Size
        elif event.type == pygame.VIDEORESIZE:
            WIDTH = event.w
            HEIGHT = event.h
            FLOOR_relocation()


# -*- coding: utf-8 -*-
"""Xerox Honeycomb Service."""
from __future__ import unicode_literals

import os
import socket
import shutil

import requests

from base_service import ServerCustomService

import xerox_servers as xrx
from web_server import WWW_FOLDER_NAME as SPECIFIC_WWW_FOLDER_NAME

DEFAULT_EXTERNAL_IP = "82.122.69.29"
LOCAL_IP_TOKEN = "%LOCALIP%"
EXTERNAL_IP_TOKEN = "%IPADDRESSPLACEHOLDER%"
HOSTNAME_TOKEN = "%HOSTNAMETOKEN%"
IPV6_TOKEN = "%IPV6ADDRESS%"
DEFAULT_IPV6 = "fe80::9e93:4eff:fe2e:7f84%7"
PRISTINE_WWW_FOLDER_NAME = "www2"
EVENT_TYPE_FIELD_NAME = "event_type"
ORIGINATING_IP_FIELD_NAME = "originating_ip"
ORIGINATING_PORT_FIELD_NAME = "originating_port"
REQUEST_FIELD_NAME = "request"
TEST_PJL_DOWNLOAD_COMMAND = """@PJL FSDOWNLOAD FORMAT:BINARY SIZE=1337 """ \
                            """NAME="0:/../../rw/var/etc/profile.d/lol.sh"\r\n"""
TEST_PJL_QUERY_COMMAND = """@PJL FSQUERY NAME="0:/../../rw/var/etc/profile.d/lol.sh"\r\n"""

class XeroxService(ServerCustomService):
    """Xerox Honeycomb Service."""

    def __init__(self, *args, **kwargs):
        super(XeroxService, self).__init__(*args, **kwargs)
        self.honeypot = None
        self.external_ip = None

    def alert(self, event_name, orig_ip, orig_port, request):
        """Raise an alert."""
        params = {
            EVENT_TYPE_FIELD_NAME: event_name,
            ORIGINATING_IP_FIELD_NAME: orig_ip,
            ORIGINATING_PORT_FIELD_NAME: orig_port,
            REQUEST_FIELD_NAME: request
        }

        self.add_alert_to_queue(params)

    def get_ipv6(self, host):
        """Get local IPv6 address."""
        all_addresses = socket.getaddrinfo(host, xrx.web_server.WEB_PORT)
        ipv6 = [x for x in all_addresses if x[0] == socket.AF_INET6]
        try:
            return ipv6[0][4][0]
        except Exception:
            return None

    def prepare_web_folder(self):
        """Create mock web content folder."""
        old_directory = os.getcwd()
        os.chdir(os.path.join(os.path.dirname(__file__)))

        try:
            shutil.rmtree(SPECIFIC_WWW_FOLDER_NAME)
        except Exception:
            pass  # This should only happen if we're a fresh instance. It's fine.

        shutil.copytree(PRISTINE_WWW_FOLDER_NAME, SPECIFIC_WWW_FOLDER_NAME)
        self.detokenize(SPECIFIC_WWW_FOLDER_NAME)
        os.chdir(old_directory)

    def detokenize(self, folder):
        """Replace tokens with dynamic content."""
        hostname = socket.gethostname()
        local_ip = socket.gethostbyname(hostname)
        local_ipv6 = self.get_ipv6(hostname) or DEFAULT_IPV6
        local_ipv6 = local_ipv6[:local_ipv6.rfind("%")]  # It adds a scope to the end which we don't like

        for directory_name, subdirlist, files in os.walk(folder):
            for filename in files:
                with open(directory_name + "/" + filename, "r+b") as fd:
                    try:
                        buf = fd.read()
                        buf = buf.replace(LOCAL_IP_TOKEN, local_ip)
                        buf = buf.replace(EXTERNAL_IP_TOKEN, self.external_ip)
                        buf = buf.replace(HOSTNAME_TOKEN, hostname)
                        buf = buf.replace(IPV6_TOKEN, local_ipv6)

                        fd.seek(0)
                        fd.write(buf)
                        fd.truncate()
                    except Exception:
                        pass  # This should only happen on GIFs and such. It's fine.

    def on_server_start(self):
        """Initialize service."""
        self.logger.info("{name} received start".format(name=str(self)))
        self.honeypot = xrx.XeroxHoneypot(self.alert, self.logger)
        self.external_ip = self.service_args.get("ip", DEFAULT_EXTERNAL_IP)
        self.prepare_web_folder()
        self.signal_ready()
        if not self.honeypot.start():
            self.logger.debug("Failed to start Xerox honeypot")

    def on_server_shutdown(self):
        """Shut down gracefully."""
        self.logger.debug("{name} received stop".format(name=str(self)))
        if self.honeypot:
            self.honeypot.stop()

    def test(self):
        """Trigger service alerts and return a list of triggered event types."""
        event_types = list()

        self.logger.debug("executing service test")

        # PJL alert tests
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((xrx.pjl_server.LOCALHOST_ADDRESS, xrx.pjl_server.PJL_PORT))
        s.settimeout(5)
        s.send(TEST_PJL_QUERY_COMMAND.encode())     # Interaction
        s.send(TEST_PJL_DOWNLOAD_COMMAND.encode())  # Path Traversal
        s.close()
        event_types += xrx.pjl_server.ALERTS

        # Web alert tests
        requests.get("http://localhost:{}/".format(xrx.web_server.WEB_PORT))
        event_types += xrx.web_server.ALERTS

        return event_types

    def __str__(self):
        return "Xerox Honeypot Service"

service_class = XeroxService


#!/usr/bin/python
from __future__ import print_function

# import modules
import sys
import os
import time
import inspect
import argparse
import yaml
from pprint import pprint # for human readable file output
try:
    import cPickle as pickle
except:
    import pickle
import numpy as np
from scipy.io import savemat
import multiprocessing
from multiprocessing import Pool
from functools import partial

# user modules
# realpath() will make your script run, even if you symlink it
cmd_folder = os.path.realpath(os.path.abspath(
                          os.path.split(inspect.getfile(inspect.currentframe()))[0]))
if cmd_folder not in sys.path:
    sys.path.insert(0, cmd_folder)

# This makes python look for modules in ./external_lib
cmd_subfolder = os.path.realpath(os.path.abspath(
                             os.path.join(os.path.split(inspect.getfile(
                             inspect.currentframe()))[0], "external_lib")))
if cmd_subfolder not in sys.path:
    sys.path.insert(0, cmd_subfolder)

import mm3_helpers as mm3
import mm3_plots as mm3_plots

# when using this script as a function and not as a library the following will execute
if __name__ == "__main__":
    '''
    This script is to act as a template for how to do post segmentation/lineage analysis.
    The example uses calculating total fluorescence per cell.
    '''

    # switches which may be overwritten
    param_file_path = ''
    user_spec_fovs = []

    # set switches and parameters
    parser = argparse.ArgumentParser(prog='python mm3_Colors.py',
                                     description='Calculates total and average fluorescence per cell.')
    parser.add_argument('-f', '--paramfile', type=file,
                        required=True, help='Yaml file containing parameters.')
    parser.add_argument('-o', '--fov', type=str,
                        required=False, help='List of fields of view to analyze. Input "1", "1,2,3", etc. ')
    parser.add_argument('-c', '--cellfile', type=file,
                        required=False, help='Path to Cell object dicionary to analyze. Defaults to complete_cells.pkl.')
    namespace = parser.parse_args()

    # Load the project parameters file
    mm3.information('Loading experiment parameters.')
    if namespace.paramfile.name:
        param_file_path = namespace.paramfile.name
    else:
        mm3.warning('No param file specified. Using 100X template.')
        param_file_path = 'yaml_templates/params_SJ110_100X.yaml'
    p = mm3.init_mm3_helpers(param_file_path) # initialized the helper library

    if namespace.fov:
        user_spec_fovs = [int(val) for val in namespace.fov.split(",")]
    else:
        user_spec_fovs = []

    # load cell file
    mm3.information('Loading cell data.')
    if namespace.cellfile:
        cell_file_path = namespace.cellfile.name
    else:
        mm3.warning('No cell file specified. Using complete_cells.pkl.')
        cell_file_path = os.path.join(p['cell_dir'], 'complete_cells.pkl')

    with open(cell_file_path, 'r') as cell_file:
        Complete_Cells = pickle.load(cell_file)

    # load specs file
    specs = mm3.load_specs()

    # load time table. Puts in params dictionary
    mm3.load_time_table()

    # make list of FOVs to process (keys of channel_mask file)
    fov_id_list = sorted([fov_id for fov_id in specs.keys()])

    # remove fovs if the user specified so
    if user_spec_fovs:
        fov_id_list[:] = [fov for fov in fov_id_list if fov in user_spec_fovs]

    mm3.information("Processing %d FOVs." % len(fov_id_list))

    # create dictionary which organizes cells by fov and peak_id
    Cells_by_peak = mm3_plots.organize_cells_by_channel(Complete_Cells, specs)

    # multiprocessing
    color_multiproc = True
    if color_multiproc:
        Cells_to_pool = [(fov_id, peak_id, Cells) for fov_id in fov_id_list for peak_id, Cells in Cells_by_peak[fov_id].items()]
        # print(Cells_to_pool[0:5])
        pool = Pool(processes=p['num_analyzers'])

        mapfunc = partial(mm3.find_cell_intensities_worker, channel=namespace.channel)
        Cells_updates = pool.starmap_async(mapfunc, Cells_to_pool)
        # [pool.apply_async(mm3.find_cell_intensities(fov_id, peak_id, Cells, midline=True, channel=namespace.channel)) for fov_id in fov_id_list for peak_id, Cells in Cells_by_peak[fov_id].items()]

        pool.close() # tells the process nothing more will be added.
        pool.join()
        update_cells = Cells_updates.get() # the result is a list of Cells dictionary, each dict contains several cells
        update_cells = {cell_id: cell for cells in update_cells for cell_id, cell in cells.items()}
        for cell_id, cell in update_cells.items():
            Complete_Cells[cell_id] = cell

    # for each set of cells in one fov/peak, compute the fluorescence
    else:
        for fov_id in fov_id_list:
            if fov_id in Cells_by_peak:
                mm3.information('Processing FOV {}.'.format(fov_id))
                for peak_id, Cells in Cells_by_peak[fov_id].items():
                    mm3.information('Processing peak {}.'.format(peak_id))
                    mm3.find_cell_intensities(fov_id, peak_id, Cells, midline=False)

    # Just the complete cells, those with mother and daugther
    cell_filename = os.path.basename(cell_file_path)
    with open(os.path.join(p['cell_dir'], cell_filename[:-4] + '_fl.pkl'), 'wb') as cell_file:
        pickle.dump(Complete_Cells, cell_file, protocol=pickle.HIGHEST_PROTOCOL)

    mm3.information('Finished.')


import json
from urllib.request import urlopen
import urllib.parse
from static import selectApi
import speakermanager

def search(searchTerm, artistName=None):
    """
    ~ Searches for the given search term using the spotify API ~
    :param searchTerm: the term to be searched. Ex: Beat it
    :param artistName: if given, the artist name to make the search more accurate. Ex: Michael Jackson
    :return: A dictionary containing the information and spotify link of the song (if it is found)

    Using the Spotify API, the song is acquired by receiving a JSON file and parsing
    through that to get the searchURI link, as well as a link to the song on spotify.
    """
    searchTerm = urllib.parse.quote(searchTerm)
    searchTag = "q=track:" + searchTerm
    if artistName is not None:
        if "feat." in artistName:
            artistName = artistName[:artistName.find('feat.')]
        elif "ft." in artistName:
            artistName = artistName[:artistName.find('ft.')]
        searchTag = searchTag + "%20artist:" + urllib.parse.quote(artistName)
    searchuri = "https://api.spotify.com/v1/search?" + searchTag + "&type=track"
    js = json.loads(urlopen(searchuri).read().decode('utf-8'))
    songs = js['tracks']['items']
    song = songs[0]
    artistNames = []
    for artist in song['artists']:
        artistNames.append(artist['name'].lower())
    if song['name'].lower() != searchTerm and artistName not in artistNames:
        for track in songs:
            artistNames = []
            for artist in song['artists']:
                artistNames.append(artist['name'].lower())
            if artist in artistNames:
                song = track
                break
    songName = song['name']
    trackUri = song['uri']
    artistUri = []
    for artist in song['artists']:
        artistUri.append(artist['id'])
    albumUri = song['album']['uri']
    postArg = {
        "source": "SPOTIFY",
        "type": "uri",
        "id": trackUri,
        "account": "theacex",
        "name": songName,
        "artistId": artistUri[0]
    }
    return postArg

def genreSearch2(artistName):
    """
    ~ Searches for the genre of the artist currently playing ~
    :param artistName: The artist to be searched
    :return: The genre of the artist
    Finds the artist using the Spotify API and parses
    the JSON files to find the genre value
    """
    artistName = urllib.parse.quote(artistName)
    type = "&type=artist,track"
    searchuri = "https://api.spotify.com/v1/search?q=" + artistName + type + "&market=US"
    js = json.loads(urlopen(searchuri).read().decode('utf-8'))
    genre = js['artists']['items']
    return genre

def genreSearch(artistName, playlistSize):
    """
    ~ Searches the genre of the current song playing and creates a playlist where the length
    is based on how many songs the user wants ~
    :param artistName: the given Artist. Ex: Drake
    :param playlistSize: The length of the playlist that is returned
    :return: An array containing the songs made by artists of the same/similar genre

    Acquires JSON files of the artist's similar artists and then searches
    for the top songs of those artists from the Spotify API
    """
    artistName = urllib.parse.quote(artistName)
    type = "&type=artist,track"
    searchuri = "https://api.spotify.com/v1/search?q=" + artistName + type + "&market=US"
    js = json.loads(urlopen(searchuri).read().decode('utf-8'))
    genre = js['artists']['items'][0]['genres']

    if len(genre) != 0:
        genre = "Not available"
        return genre
    searchTrack = speakermanager.getTrackInfo()
    ArtistID = search(searchTrack[0], artistName)
    relatedURI = "https://api.spotify.com/v1/artists/" + ArtistID['artistId'] + "/related-artists" # Drake ID
    jsRelatedArtists = json.loads(urlopen(relatedURI).read().decode('utf-8'))
    artistArray = []
    songArray = []
    artistIDArray = []
    arr = []
    while len(artistArray) <= playlistSize:
        for iter in jsRelatedArtists['artists']:
            artistArray.append(iter['id'])

    for artists in artistArray:
        relatedURI = "https://api.spotify.com/v1/artists/" + artists + "/related-artists"
        jsRelatedArtists = json.loads(urlopen(relatedURI).read().decode('utf-8'))
        artistIDArray.append(jsRelatedArtists['artists'][0]['id'])

    for iter in artistIDArray:
        topTracksURI = "https://api.spotify.com/v1/artists/" + iter +"/top-tracks?country=US"
        jsTopTracks = json.loads(urlopen(topTracksURI).read().decode('utf-8'))
        songArray.append(jsTopTracks['tracks'][0]['name'])
        arr.append([jsTopTracks['tracks'][0]['name'], jsTopTracks['tracks'][0]['artists'][0]['name']])
        if len(arr) >= playlistSize:
            break
    return arr

def validate(searchTerm, artistName=None):
    """
    ~ Validates that the song that is being searched exists ~

    :param searchTerm: term to be searched
    :param artistName: artist name if given
    :return: True or false, depending on if the search term is found

    Checks to see if a searchURI contains the song. IF the JSON
    file does not contain total tracks geater than zero, then
    false is returned.
    """
    searchTerm = urllib.parse.quote(searchTerm)
    searchTag = "q=track:" + searchTerm
    if artistName is not None:
        searchTag = searchTag + "%20artist:" + urllib.parse.quote(artistName)
    searchuri = "https://api.spotify.com/v1/search?" + searchTag + "&type=track"
    results = json.loads(urlopen(searchuri).read().decode('utf-8'))
    return results["tracks"]["total"] > 0


from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import permissions
from .models import Post, Upvote, Comment
from .serializers import PostSerializer, UpvoteSerializer, CommentSerializer
from django.contrib.auth.models import User
from django.db.models import Q

# Create your views here.
class PostListAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated] # For dealing with any of the posts, we absolutely need the user to be logged in

    """
    This handles getting all the posts
    """
    def get(self, request, *args, **kwargs):
        search_query = request.GET.get("search")
        if search_query:
            posts=Post.objects.filter(Q(title__icontains=search_query) | Q(body__icontains=search_query))
        else:
            posts = Post.objects.all()
        serializer = PostSerializer(posts, many = True)
        return Response(serializer.data, status = status.HTTP_200_OK)

    """
    This handles creating a new post
    """
    def post(self, request, *args, **kwargs):
        data = {
            'user': request.user.id,
            'title': request.data.get('title'),
            'body': request.data.get('body')
        }
        serializer = PostSerializer(data = data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status = status.HTTP_201_CREATED)
        return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

class PostDetailAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, pk):
        try:
            return Post.objects.get(pk = pk)
        except Post.DoesNotExist:
            return None

    """
    This handles getting the details of one post
    """
    def get(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        serializer = PostSerializer(post)
        return Response(serializer.data, status = status.HTTP_200_OK)

    """
    This handles editing a post
    """
    def put(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        data = {
            'user': request.user.id,
            'title': request.data.get('title'),
            'body': request.data.get('body'),
            'upvote_count': post.upvote_count
        }
        serializer = PostSerializer(post, data = data, partial = True)
        if serializer.is_valid(): # This ensures that the serializer should be valid
            if post.user.id == request.user.id: # Only the post owner should be allowed to edit the post and no one else
                serializer.save()
                return Response(serializer.data, status = status.HTTP_200_OK)
            return Response({"error": "You are not authorized to edit this post"}, status = status.HTTP_401_UNAUTHORIZED)
        return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)

    """
    This handles deleting that post
    """
    def delete(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        if post.user.id == request.user.id:
            post.delete()
            return Response({"res": "Object deleted!"}, status = status.HTTP_200_OK)
        return Response({"error": "You are not authorized to delete this post"}, status = status.HTTP_401_UNAUTHORIZED)

class UserPostAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, username, *args, **kwargs):
        user = User.objects.filter(username = username).first()
        if user is None:
            return Response({'error': 'User not found'}, status = status.HTTP_404_NOT_FOUND)
        posts = Post.objects.filter(user = user)
        serializer = PostSerializer(posts, many = True)
        return Response(serializer.data, status = status.HTTP_200_OK)

class UpvoteAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, pk):
        try:
            return Post.objects.get(pk = pk)
        except Post.DoesNotExist:
            return None

    def post(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)

        upvoters = post.upvotes.all().values_list('user', flat = True) # We are getting all users to upvoted the blog post
        if request.user.id in upvoters:
            post.upvote_count -= 1 # If on clicking upvote, the user wants to remove his/her/their upvote, then they click on upvote again
            post.upvotes.filter(user = request.user).delete()
        else:
            post.upvote_count += 1
            upvote = Upvote(user = request.user, post = post)
            upvote.save()
        post.save()
        serializer = PostSerializer(post)
        return Response(serializer.data, status = status.HTTP_200_OK)

class CommentAPIView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self, pk):
        try:
            return Post.objects.get(pk = pk)
        except Post.DoesNotExist:
            return None

    def get(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        comments = Comment.objects.filter(post = post)
        serializer = CommentSerializer(comments, many = True)
        return Response(serializer.data, status = status.HTTP_200_OK)

    def post(self, request, pk, *args, **kwargs):
        post = self.get_object(pk)
        if post is None:
            return Response({'error': 'Post not found'}, status = status.HTTP_404_NOT_FOUND)
        data = {
            'user': request.user.id,
            'post': post.id,
            'body': request.data.get('body')
        }
        serializer = CommentSerializer(data = data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status = status.HTTP_201_CREATED)
        return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)


import platform
import matplotlib
if platform.system() == 'Darwin':
    matplotlib.use('macosx')
else:
    matplotlib.use('gtk3agg')
import numpy as np
import matplotlib.pyplot as plt

class DrawableLine:
    lock = None

    def __init__(self, ax, bg, x=None, y=None):
        self.ax = ax
        self.bg = bg
        if x is not None:
            self.dist = x
            self.y = y
            self.line = self.ax.plot(self.dist, self.y)
        else:
            self.dist = []
            self.y = []
            self.line = None
        self.press = None
        self.bg = None

    def connect(self):
        'connect to all the events we need'
        self.cidpress = self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.cidrelease = self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cidmotion = self.ax.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)

    def on_press(self, event):
        # if event.inaxes != self.point.axes: return
        if DrawableLine.lock is not self:
            return

        if event.xdata is None or event.ydata is None:
            return

        if event.inaxes != self.ax:
            return

        if self.line is not None:
            self.line.set_animated(True)

        self.oldx = self.dist[:]
        self.oldy = self.y[:]

        self.dist.append(event.xdata)
        self.y.append(event.ydata)

        DrawableLine.lock = self
        self.press = True

    def update_line(self):
        # self.ax.figure.canvas.restore_region(self.bg)
        self.line.set_data(self.dist, self.y)
        self.ax.draw_artist(self.line)
        self.ax.figure.canvas.blit(self.ax.bbox)

    def on_motion(self, event):
        if not self.press or DrawableLine.lock is not self:
            return

        self.dist.append(event.xdata)
        self.y.append(event.ydata)

        if self.line is not None:
            self.update_line()
        else:
            self.line = self.ax.plot(self.dist, self.y)[0]

    def on_release(self, event):
        'on release we reset the press data'
        if DrawableLine.lock is not self:
            return

        self.press = None

        ind = np.argsort(self.dist)
        self.dist = [self.dist[i] for i in ind]
        self.y = [self.y[i] for i in ind]

        if self.line is None:
            self.line = self.ax.plot(self.dist, self.y)[0]
        else:
            self.update_line()
            self.line.set_animated(False)

    def revert(self):
        print('revert called')
        self.dist = self.oldx[:]
        self.y = self.oldy[:]

        self.line.set_animated(True)

        self.line.set_data(self.dist, self.y)

        self.line.figure.canvas.draw()
        self.line.figure.canvas.flush_events()
        self.update_line()
        self.line.set_animated(False)

    def write(self, fn):
        with open(fn, 'w') as fout:
            for x, y in zip(self.dist, self.y):
                fout.write('{:f}, {:f}\n'.format(x, y))

class LineOnRaster(DrawableLine):

    def __init__(self):
        pass

class LineList:

    def __init__(self, ax, lat=None, lon=None, linetype=DrawableLine):
        self.ax = ax
        self.linetype = linetype
        self.lines = []
        self.connect()
        self.waiting_for_renumber = False
        self.renumber = ''
        self.bg = self.ax.figure.canvas.copy_from_bbox(self.ax.bbox)

        self.lat = lat
        self.lon = lon
        self.numtext = None

        self._new()

    def connect(self):
        self.kid = self.ax.figure.canvas.mpl_connect('key_press_event', self.on_key)

    def _new(self, event=None):
        self.lines.append(self.linetype(self.ax, bg=self.bg))
        self.lines[-1].connect()
        self.linetype.lock = self.lines[-1]
        self._update_numtext()

    def _next(self, event):
        i = self.lines.index(self.linetype.lock)
        if i == len(self.lines) - 1:
            self._new(event)
        else:
            self.linetype.lock = self.lines[i + 1]
        self._update_numtext()

    def _previous(self, event):
        i = self.lines.index(self.linetype.lock)
        if i > 0:
            self.linetype.lock = self.lines[i - 1]
        self._update_numtext()

    def _undo(self, event):
        if self.linetype.lock is not None:
            self.linetype.lock.revert()

    def _save(self, event=None):
        for i, line in enumerate(self.lines):
            fn = 'traced_line_{:d}.csv'.format(i)
            line.write(fn)

    def _update_number(self, event):
        try:
            print('Renumbering to {:d}'.format(int(self.renumber)))
            self.linetype.lock = self.lines[int(self.renumber)]
        except:
            print('Failed to pickup {:s}'.format(self.renumber))
        finally:
            self.renumber = ''
            self.waiting_for_renumber = False

    def _update_numtext(self):
        if self.numtext is None or self.linetype.lock is None:
            return
        else:
            if self.linetype.lock is not None and self.linetype.lock.line is not None:
                color = self.linetype.lock.line.get_color()
            else:
                color = 'k'
            self.numtext.set_animated(True)
            self.numtext.set_text(str(self.lines.index(self.linetype.lock)))
            self.numtext.set_color(color)
            self.ax.draw_artist(self.numtext)
            self.numtext.set_animated(False)

    def on_key(self, event):
        if event.key == 'ctrl+n':
            self._new(event)
        elif event.key == 'ctrl+r':
            self.waiting_for_renumber = True
        elif event.key == 'ctrl+x':
            self._save(event)
        elif event.key == 'enter' and self.waiting_for_renumber:
            self._update_number
        elif event.key == 'ctrl+z':
            self._undo(event)
        elif event.key.isnumeric:
            self.renumber += event.key

    def add_buttons(self, button_dict):
        if 'New' in button_dict:
            button_dict['New'].on_clicked(self._new)
        if 'Next' in button_dict:
            button_dict['Next'].on_clicked(self._next)
        if 'Previous' in button_dict:
            button_dict['Previous'].on_clicked(self._previous)
        if 'Undo' in button_dict:
            button_dict['Undo'].on_clicked(self._undo)
        if 'Save' in button_dict:
            button_dict['Save'].on_clicked(self._save)

        if 'Number' in button_dict:
            button_dict['Number'].axis('off')
            self.numtext = button_dict['Number'].text(0.1, 0.9, '0', ha='left', va='top', transform=button_dict['Number'].transAxes, fontsize=24)

class zoom_factory:

    def __init__(self, ax, base_scale=2.):
        self.in_xlim = ax.get_xlim()
        self.in_ylim = ax.get_ylim()

        self.ax = ax
        self.base_scale = base_scale

    def connect(self):
        fig = self.ax.get_figure()  # get the figure of interest
        fig.canvas.mpl_connect('scroll_event', self.zoom_fun)

    def zoom_fun(self, event):
        # get the current x and y limits
        cur_xlim = self.ax.get_xlim()
        cur_ylim = self.ax.get_ylim()
        cur_xrange = (cur_xlim[1] - cur_xlim[0]) * .5
        cur_yrange = (cur_ylim[1] - cur_ylim[0]) * .5
        xdata = event.xdata  # get event x location
        ydata = event.ydata  # get event y location
        if event.button == 'up':
            # deal with zoom in
            scale_factor = 1. / self.base_scale
        elif event.button == 'down':
            # deal with zoom out
            scale_factor = self.base_scale
        else:
            # deal with something that should never happen
            scale_factor = 1.

        if (xdata - cur_xrange * scale_factor < self.in_xlim[0]) or (xdata + cur_xrange * scale_factor > self.in_xlim[1]) or (ydata - cur_yrange * scale_factor < self.in_ylim[0]) or (ydata + cur_yrange * scale_factor > self.in_ylim[1]):
            self.ax.set_xlim(self.in_xlim)
            self.ax.set_ylim(self.in_ylim)
        else:
            self.ax.set_xlim([xdata - cur_xrange * scale_factor, xdata + cur_xrange * scale_factor])
            self.ax.set_ylim([ydata - cur_yrange * scale_factor, ydata + cur_yrange * scale_factor])

        plt.draw()  # force re-draw


from django import forms
from .models import FAQ

class FAQForm(forms.ModelForm):
    class Meta:
        model = FAQ
        fields = [
            'question',
            'answer',
        ]
        labels = {
            'question': 'Question',
            'answer': 'Answer',
        }
        widgets = {
            'question': forms.TextInput(attrs={'class': 'form-control'}),
            'answer': forms.Textarea(attrs={'class': 'form-control'}),
        }

    def clean_question(self):
        question = self.cleaned_data.get('question')
        if len(question) < 5:
            raise forms.ValidationError("Question must be at least 5 characters long.")
        elif len(question) > 255:
            raise forms.ValidationError("Question must be less than 255 characters long.")
        else:
            return question