import pygame
from .timer import Timer

# TODO: ecdf curve of per-packet latency of the last N packets
# https://en.wikipedia.org/wiki/Empirical_distribution_function

class LineGraph(object):
    def __init__(self, rect, samples, title, callback, text_transform, font_size=16):
        super(LineGraph, self).__init__()

        self.rect = rect
        self.callback = callback

        self.timer_update = Timer(.5, self._onUpdateTimeout)

        self.lines = []

        self.samples = samples

        self.colors = [(255,0,0), (0,255,0), (0,0,255)]

        self.text_transform = text_transform

        self.vmin = None
        self.vmax = None

        self.font = pygame.font.SysFont('arial', font_size)

        self.txt_title = self.font.render(title, True, (255,255,255))

        self.line_titles = []

        self.show_labels = True

    def setRange(self, vmin, vmax):
        self.vmin = vmin
        self.vmax = vmax

    def setLineTitle(self, titles):

        self.line_titles = []
        for col, txt in zip(self.colors, titles):
            self.line_titles.append(self.font.render(txt, True, col))

    def setShowLabels(self, show):
        self.show_labels = show

    def handle_event(self, evt):
        pass

    def handle_message(self, msg):
        pass

    def update(self, delta_t):

        self.timer_update.update(delta_t)

    def paint(self, surface):

        self.xscale = self.rect.width//self.samples
        self.yscale = self.rect.height

        for x in range(0, self.samples + 1, 30):
            c = (72,72,72) if x%60 == 0 else (32,32,32)
            x0 = self.rect.x + x * self.xscale
            y0 = self.rect.y
            y1 = self.rect.y + self.rect.height
            pygame.draw.line(surface, c, (x0, y0), (x0, y1))
        w = self.samples*self.xscale

        pygame.draw.line(surface, (128,128,128), (self.rect.x,self.rect.y), (self.rect.x+w, self.rect.y))
        pygame.draw.line(surface, (128,128,128), (self.rect.x,self.rect.y+self.rect.height), (self.rect.x+w, self.rect.y+self.rect.height))
        pygame.draw.line(surface, (64,64,64), (self.rect.x,self.rect.y+self.rect.height//2), (self.rect.x+w, self.rect.y+self.rect.height//2))
        pygame.draw.line(surface, (32,32,32), (self.rect.x,self.rect.y+self.rect.height//4), (self.rect.x+w, self.rect.y+self.rect.height//4))
        pygame.draw.line(surface, (32,32,32), (self.rect.x,self.rect.y+self.rect.height*3//4), (self.rect.x+w, self.rect.y+self.rect.height*3//4))

        fh = self.font.get_linesize()

        for idx, line in enumerate(self.lines):
            if not line:
                continue
            vmax, points, txt_max, txt_val = line
            points = [(x+self.rect.x, y) for (x,y) in points]
            pygame.draw.lines(surface, self.colors[idx], False, points, width=2)

            if self.show_labels:
                surface.blit(txt_max, (self.rect.x + w + 8, self.rect.y + 3*fh*idx))
                surface.blit(txt_val, (self.rect.x + w + 8, self.rect.y + 3*fh*idx + fh))

        surface.blit(self.txt_title, (self.rect.x, self.rect.y + self.rect.height - self.txt_title.get_height()))

    def _construct(self, values, color, dmax):

        if len(values) < 2:
            return

        values = values[-self.samples:]

        tmax = max(values)
        vmax = self.vmax if self.vmax is not None else dmax
        vmin = self.vmin if self.vmin is not None else 0
        vrng = vmax - vmin

        if vrng == 0:
            return

        points = [(i*self.xscale, self.rect.y + self.yscale - (self.yscale*(max(min(v,vmax),vmin)-vmin)/vrng)) for i,v in enumerate(values)]

        txt_max = self.font.render(self.text_transform(tmax), True, color)
        txt_val = self.font.render("(%s)" % self.text_transform(values[-1]), True, color)

        return (vmax, points, txt_max, txt_val)

    def _onUpdateTimeout(self):

        x = -self.samples
        data = self.callback()
        dmax = max([max(values[x:]) if len(values)>2 else 0 for values in data])
        self.lines = [self._construct(data, color, dmax) for data, color in zip(data, self.colors)]

class AreaGraph(object):
    def __init__(self, rect, samples, callback):
        super(AreaGraph, self).__init__()

        self.xpos = rect.x
        self.ypos = rect.y
        self.width = rect.width
        self.height = rect.height
        self.callback = callback

        self.samples = samples
        self.xscale = self.width//self.samples
        self.yscale = self.height

        self.plot_width = int(self.samples * self.xscale)

        self.timer_update = Timer(.5, self._onUpdateTimeout)

        self.colors = [
            (0,50,200),
            (20,128,20),
            (128,20,20),
            (128,128,128),
        ]

        self.perf_data = []

            # 1 - 2 : message handler
            # 2 - 3 : update
            # 3 - 4 : network send
            # 4 - 5 : idle time

        self.font = pygame.font.SysFont('arial', 16)
        self.labels = []
        for col, txt in zip(self.colors, ['Msg. Handler', 'Update', 'Network', 'Idle']):
            surf = self.font.render(txt, True, col)
            self.labels.append(surf)

    def handle_event(self, evt):
        pass

    def handle_message(self, msg):
        pass

    def update(self, delta_t):
        self.timer_update.update(delta_t)

    def paint(self, surface):

        x = self.xpos
        y = self.ypos + self.height

        pygame.draw.line(surface, (32,32,32), (x,y), (x+self.plot_width, y))

        for i, data in enumerate(self.perf_data):

            x = self.xscale * i
            sum_total = 1.0 # data[0]
            percents = [round(self.yscale * data[x]/sum_total) for x in [1,2,3,4]]

            y = self.ypos

            # time spent in user code, processing messages
            v = percents[0]
            if v > 0:
                pygame.draw.rect(surface, self.colors[0], (x, y, self.xscale, v))
                y += v

            # time spent in user code, processing update
            v = percents[1]
            if v > 0:
                pygame.draw.rect(surface, self.colors[1], (x, y, self.xscale, v))
                y += v

            # time spent sending datagrams
            v = percents[2]
            if v > 0:
                pygame.draw.rect(surface, self.colors[2], (x, y, self.xscale, v))
                y += v

            # time spent idle
            v = percents[3]
            if v > 0:
                if y + v > y + self.height + 10:
                    v = self.height + 10
                pygame.draw.rect(surface, self.colors[3], (x, y, self.xscale, v))
                y += v

        y = self.ypos + self.height
        x = self.xpos + 8
        for lbl in self.labels:
            surface.blit(lbl, (x, y))
            x += lbl.get_width() + 8

    def _onUpdateTimeout(self):
        self.perf_data = self.callback()


from decimal import Decimal
from xml.dom import minidom
import datetime
from dimagi.utils.parsing import json_format_datetime

def serialize(value):
    """
    Serializes a value so it can properly be parsed into XML
    """
    if isinstance(value, datetime.datetime):
        return json_format_datetime(value)
    elif isinstance(value, datetime.date):
        return value.isoformat()
    elif isinstance(value, datetime.time):
        return value.strftime('%H:%M:%S')
    elif isinstance(value, (int, Decimal, float)):
        return str(value)
    else:
        return value if value is not None else ""

def indent_xml(xml_string):
    """
    Takes an xml string and returns an indented and properly tabbed version of the string
    """
    if isinstance(xml_string, str):
        xml_string = xml_string.encode('utf-8')
    return minidom.parseString(xml_string).toprettyxml()


from flask import Flask, render_template, request
import rf_send

app = Flask(__name__)

@app.route('/', methods=["GET", "POST"])
def index():
    if request.method == "POST":
        if request.form.get("lighton"):
            rf_send.control_light("on")
        elif request.form.get("lightoff"):
            rf_send.control_light("off")

    return render_template('index.html')

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


import os
import customtkinter
import tkinter as tk
from tkVideoPlayer import TkinterVideo
from PIL import Image, ImageTk
from pyjoystick.sdl2 import Joystick, Key, run_event_loop
import threading
import psutil
import time
import atexit

arcadeBackground = "Background.mp4"
video_play_interval = 20000

game_stats = {}

def save_game_stats():
    with open("game_stats.txt", "w") as f:
        for game, count in game_stats.items():
            f.write(f"{game}: {count}\n")

game_open = False

atexit.register(save_game_stats)

def add_joystick(joystick):
    print('Added', joystick)

def remove_joystick(joystick):
    print('Removed', joystick)

x = 0

def handle_key_event(key):
    global x

    if key.keytype == Key.AXIS:
        if key.number == 0:
            if game_open == False:
                if key.value == -1:
                    print("left")
                    print(game_open)
                    x = (x - 1) % len(image_list)
                elif key.value == 1:
                    print("right")
                    print(game_open)
                    x = (x + 1) % len(image_list)

    elif key.keytype == Key.BUTTON and key.number == 0:
        if key.value == 1:
            if game_open == False:
                game_name_label.configure(text="Loading...", font=("Arcade Normal", 25),)
                print("play")
                open_files()
                update_game_stats(x)

    update_display(x)

def update_game_stats(index):
    global game_stats
    game_name = os.path.splitext(os.path.basename(folder_files_list[index][0]))[0]

    # Update play count for the game
    if game_name in game_stats:
        game_stats[game_name] += 1
    else:
        game_stats[game_name] = 1

    # Write game stats to a text file
    with open("game_stats.txt", "w") as f:
        for game, count in game_stats.items():
            f.write(f"{game}: {count}\n")

def create_thumbnail_image(image_path, size=(1100, 550), fallback_image_path="default_thumbnail.png"):
    try:
        return customtkinter.CTkImage(dark_image=Image.open(image_path), size=size)
    except FileNotFoundError:
        return customtkinter.CTkImage(dark_image=Image.open(fallback_image_path), size=size)

folder_files_list = []
for folder_path, _, files in os.walk("Pelit"):
    exe_files = [
        os.path.join(folder_path, file)
        for file in files
        if file.endswith(".exe") and not file.lower().startswith("unitycrashhandler")
    ]
    if exe_files:
        folder_files_list.append(exe_files)

image_list = [
    create_thumbnail_image(os.path.join(os.path.dirname(folder_files[0]), "thumbnail.png"), fallback_image_path="default_thumbnail.png")
    for folder_files in folder_files_list
]

def open_files():
    global game_open  # Declare the variable as global

    current_folder_files = folder_files_list[x]
    for path in current_folder_files:
        os.startfile(path)

    # Set the game_open variable to True when the game is open
    game_open = True

def joystick_loop():
    run_event_loop(add_joystick=add_joystick, remove_joystick=remove_joystick, handle_key_event=handle_key_event)

def update_display(image_number):
    global x

    image_number %= len(image_list)
    x = image_number  # Update the current image index
    current_folder_files = folder_files_list[x]
    current_folder_name = os.path.basename(current_folder_files[0])

    # Remove the .exe extension from the current_folder_name
    game_name_without_extension = os.path.splitext(current_folder_name)[0]

    my_label.configure(image=image_list[image_number])
    if game_open == False:
        game_name_label.configure(text=game_name_without_extension)

def update_display_loop():
    update_display(x)
    app.after(1000, update_display_loop)  # Schedule the next update in 1 second

def tkinter_loop():
    customtkinter.set_appearance_mode("dark")
    global app, my_label, game_name_label, background_image

    app = customtkinter.CTk()

    app.wm_attributes('-transparentcolor', '#ab23ff')
    background_image = tk.PhotoImage(file="Gradient.png")

    # Load and display the video as the background
    video_canvas = TkinterVideo(master=app, anchor="center")
    video_canvas.load("Background.mp4")  # Replace with the path to your video file
    video_canvas.pack(fill="both", expand=True)  # Expand to fill the window
    video_canvas.play()

    my_label = customtkinter.CTkLabel(app, image=image_list[0], text="")
    my_label.place(relx=0.5, rely=0.53, anchor="center")

    game_name_label = customtkinter.CTkLabel(app, text="", text_color="white")
    game_name_label.place(relx=0.5, rely=0.925, anchor="s")
    game_name_label.configure(image=background_image)

    current_folder_name = os.path.basename(folder_files_list[0][0])
    game_name_without_extension = os.path.splitext(current_folder_name)[0]
    game_name_label.configure(text=game_name_without_extension, font=("Arcade Normal", 25),)

    app.geometry("1920x1080")  # Set your desired window size here
    app.attributes("-fullscreen", "True")

    schedule_video_playback(video_canvas)

    update_display_loop()  # Start the update display loop
    app.mainloop()

def schedule_video_playback(video_canvas):
    video_canvas.play()
    app.after(video_play_interval, schedule_video_playback, video_canvas)

def check_game_open_periodically():
    global game_open

    while True:
        time.sleep(0.5)
        if game_open:
            # Check if the game process is running by its executable name
            game_process_name = os.path.basename(folder_files_list[x][0])
            for proc in psutil.process_iter(['pid', 'name']):
                if proc.info['name'] == game_process_name:
                    break
            else:
                # The game process is not running, so set game_open to False
                game_open = False

def main():
    joystick_thread = threading.Thread(target=joystick_loop)
    joystick_thread.start()

    # Start a thread to check if the game is open periodically
    game_check_thread = threading.Thread(target=check_game_open_periodically)
    game_check_thread.start()

    tkinter_loop()

if __name__ == "__main__":
    if os.path.isfile("game_stats.txt"):
        with open("game_stats.txt", "r") as f:
            for line in f:
                parts = line.strip().split(": ")
                if len(parts) == 2:
                    game_stats[parts[0]] = int(parts[1])
    main()


#!usr/lib/python2.7
# Auther    : Haonan Li <haonanl5@student.unimelb.edu.au>
# Porpuse   : Circular Binary Segmentation algorithm

import getopt
import sys
import numpy as np
import math
import matplotlib.pyplot as plt

# input the bins
def input_bin(bin_file):
    bin_f = open(bin_file,'r')
    # read table title
    bin_f.readline()
    # bins and position index
    bins = []
    index = []
    for line in bin_f.readlines():
        line = line.strip().split()
        # average copy number
        acn = float(line[3]) / (int(line[2])-int(line[1]))
        index.append((int(line[1]),int(line[2])))
        bins.append(acn)
    ch = line[0]
    bin_f.close()
    return bins,index,ch

# visulization log-ratio with highlight cnv
def plot_cnv(X, cnvi):
    x = np.array(range(len(X)))
    # y1 = np.zeros((len(X)))
    # plt.plot(x,y1)
    # plot all log-ratio
    plt.scatter(x,X,s=10)
    cnvx = []
    cnvy = []
    for seg in cnvi:
        cnvx += [i for i in range(seg[0],seg[1])]
        cnvy += list(X[seg[0]:seg[1]])
    # plot cnvs
    plt.scatter(cnvx,cnvy,s=10,marker='*',color='r')
    plt.show()

# output the segments with abs average log-ratio >= 0.1
def output(X,D,index,out_file,ch):
    out_f = open(out_file,'w')
    # change point index
    cpi = np.where(D >= 1)
    cpi = cpi[0]
    # change points
    cps = [index[i][0] for i in cpi]
    # save CNV index and CNVs
    cnvi = []
    cnv = []
    for i in range(len(cps)-1):
        T = X[cpi[i]+1:cpi[i+1]]
        if len(T)>0:
            avg = np.average(T)
        else:
            avg = 0.0
        if abs(avg) >= 0.1:
            cnvi.append((cpi[i]+1,cpi[i+1]))
            cnv.append((cps[i],cps[i+1],avg))
    # output cnv
    for seg in cnv:
        out_f.write('%s\t%d\t%d\t%f\n'%(ch,seg[0],seg[1],seg[2]))
    out_f.close()
    # plot
    # plot_cnv(X,cnvi)

# recursive cbs
def cbs(X,I,D,t):

    # too near
    n = len(X)
    if n < 2:
        return D

    # S : cumulative sum of first n bins
    S = np.zeros((n))
    for i in range(n):
        S[i] = X[0:i+1].sum()

    # Z :
    Z = np.zeros((n,n))
    for i in range(0,n-1):
        for j in range(i+1,n):
            coef = 1 / math.sqrt(1.0/(j-i) + 1.0/(n-j+i+1))
            factor = (S[j]-S[i])/(j-i) - (S[n-1]-S[j]+S[i])/(n-j+i)
            Z[i,j] = abs(coef * factor)

    # find max value and position
    maximum = np.max(Z)
    if maximum < t:
        return D
    else:
        # find change points recursively
        pos = np.where(Z == maximum)
        pos1 = min(pos[0][0],pos[1][0])+1
        pos2 = max(pos[0][0],pos[1][0])+1

        # boundary detecta and single extremum ignore
        if pos2 - pos1 <= 1 or pos2 - pos1 >= n-1:
            return D
        if pos1 > 1:
            D[I[pos1]] += 1
        if pos2 < n-1:
            D[I[pos2]] += 1

        X1 = X[pos1:pos2]
        X2 = np.append(X[0:pos1],X[pos2:n])

        I1 = I[pos1:pos2]
        I2 = np.append(I[0:pos1],I[pos2:n])

        D1 = cbs(X1,I1,D,t)
        D2 = cbs(X2,I2,D,t)

        return D1 + D2

# init the log-ratio data
def cbs_init(bin_file, threshold, out_file):

    # CBS initialization
    bins,index,ch = input_bin(bin_file)

    # speed up with numpy
    bins = np.array(bins)
    n = len(bins)

    # X : log ratios of each bin
    X = np.log2(bins / np.median(bins[:n/3]))
    X[X > 2] = 0
    X[X < -5] = 0

    # I : index of X
    I = np.arange(0,n)

    # D : segmentation index
    D = np.zeros((n))
    D[0] = 1
    D[n-1] = 1

    # call cbs
    cbs(X,I,D,threshold)

    # output
    output(X,D,index,out_file,ch)

# Usage of the tool
def usage():
   print ("usage:python phaser.py [options] ... [-b bins | -z threshold] ...")
   print ("Options and arguments:")
   print ("-h     :Help")
   print ("-b     :Input bins.")
   print ("-z     :Z threshold")
   print ("-o     :Output file")

def main(argv):
    try:
        opts, args = getopt.getopt(argv[1:], "hb:z:o:", \
        ["bin=", "threshold=", "output="])
    except getopt.GetoptError:
        usage()
        sys.exit(2)
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            usage()
            sys.exit()
        elif opt in ('-b', '--bin='):
            bin_file = arg
        elif opt in ('-z', '--threshold='):
            threshold = float(arg)
        elif opt in ('-o', '--output='):
            out_file = arg

    # Make sure the parameters were defined
    if not('bin_file' in locals().keys()) or \
       not('threshold' in locals().keys()) or \
       not('out_file' in locals().keys()):
        usage()
        sys.exit()

    # main process
    cbs_init(bin_file, threshold, out_file)

if __name__ == "__main__":
    main(sys.argv)


from django.shortcuts import render, redirect
from .models import Article
from .forms import ArticleForm

def index(request):
    articles = Article.objects.all()
    context = {
        'articles': articles,
    }
    return render(request, 'article/index.html', context)

def create(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            article = form.save()
            return redirect('article:detail', article.pk)
    else:
        form = ArticleForm()
    context = {
        'form': form,
    }
    return render(request, 'article/create.html', context)

def detail(request, article_pk):
    article = Article.objects.get(pk=article_pk)
    context = {
        'article': article,
    }
    return render(request, 'article/detail.html', context)

def delete(request, article_pk):
    article = Article.objects.get(pk=article_pk)
    article.delete()
    return redirect('article:index')

def update(request, article_pk):
    article = Article.objects.get(pk=article_pk)
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            article = form.saver()
            return redirect('article:detail', article.pk)
    else:
        form = ArticleForm(instance=article)
    context = {
        'article': article,
        'form': form,
    }
    return render(request, 'article/create.html', context)


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

from .objects import Route, Middleware, Listener

__all__ = (
    'ResourceMeta',
    "Resource",
)

class ResourceMeta(type):
    """
    A metaclass for resources.
    """
    __routes__: Dict[str, Route]
    __listeners__: List[Listener]
    __middlewares__: List[Middleware]
    __url_prefix__: str

    def __new__(cls, cls_name: str, bases: Tuple[Type[Any]], attrs: Dict[str, Any], **kwargs):
        attrs['__resource_name__'] = kwargs.get('name', cls_name)

        routes: Dict[str, Route] = {}
        listeners = []
        middlewares = []

        url_prefix = kwargs.get('url_prefix', '')
        self = super().__new__(cls, cls_name, bases, attrs, **kwargs)

        for base in reversed(self.__mro__):
            for elem, value in base.__dict__.items():
                if isinstance(value, Route):
                    path = url_prefix + value.path
                    routes[path] = value

                elif isinstance(value, Middleware):
                    middlewares.append(value)

                elif isinstance(value, Listener):
                    listeners.append(value)

        self.__url_prefix__ = url_prefix
        self.__routes__ = routes
        self.__listeners__ = listeners
        self.__middlewares__ = middlewares

        return self

class Resource(metaclass=ResourceMeta):
    """
    A resource to be subclassed and used.

    Example
    -------
    .. code-block:: python3

        import subway

        app = subway.Application()

        class MyResource(subway.Resource):

            @subway.route('/hello/{name}', 'GET')
            async def hello(self, request: subway.Request, name: str):
                return f'Hello, {name}!'

        app.add_resource(MyResource())

    """
    __routes__: Dict[str, Route]
    __listeners__: List[Listener]
    __middlewares__: List[Middleware]
    __url_prefix__: str
    __resource_name__: str

    def __repr__(self) -> str:
        return '<Resource name={0.name!r}>'.format(self)

    @property
    def name(self) -> str:
        """
        The name of the resource.
        """
        return self.__resource_name__

    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise TypeError("name must be a string")

        self.__resource_name__ = value

    @property
    def routes(self) -> List[Route]:
        """
        The routes of the resource.
        """
        return list(self.__routes__.values())

    @property
    def listeners(self) -> List[Listener]:
        """
        The listeners of the resource.
        """
        return self.__listeners__

    @property
    def middlewares(self) -> List[Middleware]:
        """
        The middlewares of the resource.
        """
        return self.__middlewares__


"""

GUI to handle visual inspection of emitters from MUSE
Assumes file written with the muse_emitters.py procedures

"""

from tkinter import *
import Tkinter
import tkFont
from Tkinter import Tk
import tkMessageBox
import tkFileDialog
import argparse
import sys
from astropy.io import fits
from astropy.table import Column
from astropy.table import Table
import numpy as np
import subprocess
import os
import signal
from mypython.ifu import muse_utils as utl

class TableEdit(Tkinter.Frame):
    def __init__(self, parent, rows=10, columns=2):
        # use black background so it "peeks through" to
        # form grid lines
        Tkinter.Frame.__init__(self, parent)
        self._table_labels = []
        self._table_cells = []
        self.clicked=None
        self.rows=rows
        self.columns=columns
        for row in range(self.rows):
            current_row_lab  = []
            current_row_cel  = []
            for column in range(self.columns):
                if column == self.columns-1:
                   width=24
                else:
                   width=8
                label = Tkinter.StringVar()
                cell  = Tkinter.Entry(self,textvariable=label,width=width)
                cell.grid(row=row, column=column, sticky="nsew", padx=1, pady=1)
                cell.bind("<Button-1>",self.lastclicked)
                label.set("0.0")
                current_row_lab.append(label)
                current_row_cel.append(cell)

            self._table_labels.append(current_row_lab)
            self._table_cells.append(current_row_cel)

        for column in range(self.columns):
            self.grid_columnconfigure(column, weight=1)

    def set(self, row, column, value):
        widget = self._table_labels[row][column]
        widget.set(value)

    def get(self, row, column):
        widget = self._table_labels[row][column]
        return widget.get()

    def lock(self, row, column):
        widget = self._table_cells[row][column]
        widget.configure(state='readonly',readonlybackground='white')

    def unlock(self, row, column):
        widget = self._table_cells[row][column]
        widget.configure(state=NORMAL)

    def hasfocus(self):
        for row in range(self.rows):
            for column in range(self.columns):
                if(self.clicked == self._table_cells[row][column]):
                    return [row,column]
        #return widget.cget('')
        #return widget.select_present()

    def lastclicked(self,event):
        self.clicked=event.widget

class TableFix(Tkinter.Frame):
    def __init__(self, parent, rows=10, columns=2):
        # use black background so it "peeks through" to
        # form grid lines
        self.frame = Tkinter.Frame.__init__(self, parent)
        self._table_labels = []
        self._table_cells = []
        for row in range(rows):
            current_row  = []
            for column in range(columns):
                if column == columns-1:
                   width=24
                else:
                   width=8
                cell  = Tkinter.Label(self,text='Text',width=width)
                cell.grid(row=row, column=column, sticky="nsew", padx=1, pady=1)
                current_row.append(cell)

            self._table_labels.append(current_row)

        for column in range(columns):
            self.grid_columnconfigure(column, weight=1)

    def set(self, row, column, value):
        widget = self._table_labels[row][column]
        widget.configure(text=value)

class Window(Tkinter.Tk):

    def __init__(self,parent,startfile=None,white=None):

        #store file name
        self.startfile=startfile
        self.white=white

        #start tk
        self.tk=Tk()

        #set min and preferred size of main gui
        self.minwinwidth=400
        self.minwinheight=300
        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()
        self.preferwinwidth=int(screen_width*0.7)
        self.preferwinheight=int(screen_height*0.5)
        self.minsize(width=self.minwinwidth, height=self.minwinheight)
        self.geometry("{}x{}".format(self.preferwinwidth,self.preferwinheight))

        #tweak the aspect ratio of the menu and data gui
        self.menuaspect=[1,0.2]     #Ruari 24/05 fixes bug where different resolutions cause the menu to be cut off
        self.dataaspect=[1,1-0.2]     #Ruari 24/05 fixes bug where different resolutions cause the menu to be cut off
        self.dpi=80

        #list for background processes
        self.processes=[]

        #Fiddle with font
        #default_font = tkFont.nametofont("TkDefaultFont")
        #scalefont = int(screen_height/1080.0*14)
        #default_font.configure(size=scalefont)

        #init gui frame
        self.initialize()

    def initialize(self):
        """ This init the basic gui """

        #create a menu frame
        self.menuframe=Tkinter.Frame(self,width=int(self.preferwinwidth*self.menuaspect[0]),
                                     height=int(self.preferwinheight*self.menuaspect[1]))
        self.menuframe.grid_propagate(0)
        self.menuframe.grid()

        #create a data frame
        self.dataframe=Tkinter.Frame(self,width=int(self.preferwinwidth*self.dataaspect[0]),
                                     height=int(self.preferwinheight*self.dataaspect[1]))
        self.dataframe.grid_propagate(0)
        self.dataframe.grid()

        #update for later use of units
        self.update()

        #now initialise the menu frame
        self.init_menuframe()
        #now initialise the data frame
        self.init_dataframe()

    def init_menuframe(self):

        # creating a quite command
        self.quitButton_w = Tkinter.Button(self.menuframe, text="Quit",command=self.client_exit).grid(row=0,column=0)

        # creating an abort command
        self.abortButton_w = Tkinter.Button(self.menuframe, text="Abort",command=self.client_abort).grid(row=1,column=0)

        # creating a save command
        self.saveButton_w = Tkinter.Button(self.menuframe, text="Save",command=self.write_current).grid(row=0,column=1)

        #create the control page
        self.nextButton_w = Tkinter.Button(self.menuframe, text=">>",command=self.next_page).grid(row=0,column=4)
        self.previousButton_w = Tkinter.Button(self.menuframe, text="<<",command=self.previous_page).grid(row=0,column=2)

        self.currentpage=Tkinter.StringVar()
        self.currentpage.set(1)
        self.allpages=Tkinter.StringVar()
        self.allpages.set(1)
        self.statuspage=Tkinter.StringVar()
        self.statuspage.set("Page {}/{}".format(self.currentpage.get(),self.allpages.get()))
        self.currentpage_w=Tkinter.Label(self.menuframe,textvariable = self.statuspage)
        self.currentpage_w.grid(column=3,row=0)

        # creating a save command
        self.firstButton_w = Tkinter.Button(self.menuframe, text="First",command=self.gotofirst).grid(row=0,column=5)
        self.lastButton_w = Tkinter.Button(self.menuframe, text="Last",command=self.gotolast).grid(row=0,column=6)

        #create inspect option
        self.inspectButton_w = Tkinter.Button(self.menuframe, text="Inspect Current",command=self.inspect_current).grid(row=1,column=1,columnspan=2)

        #set header table properties
        self.keycol=['id','x_geow','y_geow','lambda_fluxw','SNR','SNR_odd','SNR_even','SNR_med','EODeltaSN','BoxFraction','OverContinuum','relvel','inspect','confidence','redshift','CogFlux','CogErr','CogRad','type','notes']
        self.shortkey=['ID','X','Y','Lambda','SNR','SNRodd','SNReven','SNRmed','EODSN','BoxFrac','Continu','relvel','inspect','confid','redshift','CogFlux','CogErr','CogRad','type','notes']
        self.tabncol=len(self.keycol)

        #create sort by option
        llab = Tkinter.Label(self.menuframe, text="Sort by:")
        llab.grid(column=3,row=1)
        self.sortlist = Tkinter.StringVar(self.menuframe)
        self.sortlist.set("id") # default value
        self.sortlist_w = Tkinter.OptionMenu(self.menuframe, self.sortlist,'id','optimal','SNR','confidence','redshift','type','x_geow','y_geow','lambda_fluxw','notes','relvel','inspect')
        self.sortlist_w.grid(column=4,row=1)
        #set the linelist in trace state
        self.sortlist.trace("w",self.sorttab)

        #create reverse option
        self.reverselist_w = Tkinter.Button(self.menuframe,text="Reverse",command=self.reversetab)
        self.reverselist_w.grid(column=5,row=1)

        #redshift reference
        zlab = Tkinter.Label(self.menuframe, text="z_ref = ")
        zlab.grid(column=7,row=0)
        self.relvel = Tkinter.StringVar()
        self.relvelcntr = Tkinter.Entry(self.menuframe,textvariable=self.relvel)
        self.relvelcntr.grid(column=8,row=0)
        self.relvel.set("0.0000")
        #set the redshift in a trace state
        self.relvel.trace("w",self.updaterelvel)

        #reference lambda
        llab = Tkinter.Label(self.menuframe, text="lam_ref = ")
        llab.grid(column=7,row=1)
        self.rellam = Tkinter.StringVar()
        self.rellamcntr = Tkinter.Entry(self.menuframe,textvariable=self.rellam)
        self.rellamcntr.grid(column=8,row=1)
        self.rellam.set("1215.6701")
        #set the redshift in a trace state
        self.rellam.trace("w",self.updaterelvel)

    def init_dataframe(self):

        #open the file
        self.catdata=Table.read(self.startfile,format='fits')
        #save the time stamp
        self.timeio=os.path.getmtime(self.startfile)
        #check if lock file exists
        if os.path.exists(self.startfile+'.lock'):
            tkMessageBox.showinfo('I/O Warning','This catalogue is currently locked by another user. Best not to continue.')
            self.client_abort(keeplock=True)
        else:
            #make one
            open(self.startfile+'.lock','a').close()

        if('redshift' not in self.catdata.colnames):
            newcl=Column(np.zeros(len(self.catdata)))
            self.catdata.add_column(newcl,name='redshift')
        if('CogFlux' not in self.catdata.colnames):
            newcl=Column(np.zeros(len(self.catdata)))
            self.catdata.add_column(newcl,name='CogFlux')
        if('CogErr' not in self.catdata.colnames):
            newcl=Column(np.zeros(len(self.catdata)))
            self.catdata.add_column(newcl,name='CogErr')
        if('CogRad' not in self.catdata.colnames):
            newcl=Column(np.zeros(len(self.catdata)))
            self.catdata.add_column(newcl,name='CogRad')
        if('inspect' not in self.catdata.colnames):
            newcl=Column(np.zeros(len(self.catdata)))
            self.catdata.add_column(newcl,name='inspect')
        if('type' not in self.catdata.colnames):
            newcl=Column(np.full(len(self.catdata),'None',dtype="S50"))
            self.catdata.add_column(newcl,name='type')
        if('notes' not in self.catdata.colnames):
            newcl=Column(np.full(len(self.catdata),'None',dtype="S50"))
            self.catdata.add_column(newcl,name='notes')
        if('relvel' not in self.catdata.colnames):
            newcl=Column(np.zeros(len(self.catdata)))
            self.catdata.add_column(newcl,name='relvel')
            self.updaterelvel('firsttime')

        #find number of pages
        self.rowppage=15
        self.pagenum=len(self.catdata)/self.rowppage
        if((len(self.catdata) % self.rowppage) > 0):
            self.pagenum=self.pagenum+1

        #update page
        self.allpages.set(self.pagenum)
        self.statuspage.set("Page {}/{}".format(self.currentpage.get(),self.allpages.get()))

        #add table
        self.table_header = TableFix(self.dataframe, 1,self.tabncol)
        self.table_header.pack(side="top", fill="x")
        for jj,txt in enumerate(self.shortkey):
            self.table_header.set(0,jj,txt)

        self.table_data = TableEdit(self.dataframe,self.rowppage,self.tabncol)
        self.table_data.pack(side="top", fill="x")

        #write table entries
        self.update_tabdisplay()

    def record_changes(self):

        #grab ids
        idred=self.keycol.index('redshift')
        idtype=self.keycol.index('type')
        idid=self.keycol.index('id')
        idconf=self.keycol.index('confidence')
        idnote=self.keycol.index('notes')
        idflux=self.keycol.index('CogFlux')
        idferr=self.keycol.index('CogErr')
        idfrad=self.keycol.index('CogRad')

        #scan them
        for r in range(self.rowppage):
            myid=self.table_data.get(r,idid)
            myred=self.table_data.get(r,idred)
            mytype=self.table_data.get(r,idtype)
            myconf=self.table_data.get(r,idconf)
            mynote=self.table_data.get(r,idnote)
            myflux=self.table_data.get(r,idflux)
            myferr=self.table_data.get(r,idferr)
            myfrad=self.table_data.get(r,idfrad)
            #store in table by searching for id (allow for blanks)
            try:
                cidx=np.where(self.catdata['id'] == int(myid))
                self.catdata['redshift'][cidx]=myred
                self.catdata['type'][cidx]=mytype
                self.catdata['confidence'][cidx]=myconf
                self.catdata['notes'][cidx]=mynote
                self.catdata['CogFlux'][cidx]=myflux
                self.catdata['CogErr'][cidx]=myferr
                self.catdata['CogRad'][cidx]=myfrad
            except:
                pass

    def update_tabdisplay(self):

        #num columns
        ncol=len(self.keycol)

        #clear page
        nrow=self.rowppage
        for r in range(nrow):
            for c in range(ncol):
                self.table_data.set(r,c,'')

        #update current entries for this page
        self.current_starix=(int(self.currentpage.get())-1)*self.rowppage
        self.current_endix=self.current_starix+self.rowppage
        if(self.current_endix > len(self.catdata)):
            self.current_endix=len(self.catdata)
        nrow=self.current_endix-self.current_starix

        for r in range(nrow):
            for c in range(ncol):
                rowindx=self.current_starix+r
                self.table_data.set(r,c,self.catdata[self.keycol[c]][rowindx])

                #lock fields that do not need edits
                if('redshift' in self.keycol[c]):
                    self.table_data.unlock(r,c)
                elif('type' in self.keycol[c]):
                    self.table_data.unlock(r,c)
                elif('confidence' in self.keycol[c]):
                    self.table_data.unlock(r,c)
                elif('CogFlux' in self.keycol[c]):
                    self.table_data.unlock(r,c)
                elif('CogErr' in self.keycol[c]):
                    self.table_data.unlock(r,c)
                elif('CogRad' in self.keycol[c]):
                    self.table_data.unlock(r,c)
                elif('notes' in self.keycol[c]):
                    self.table_data.unlock(r,c)
                else:
                    self.table_data.lock(r,c)

    def updaterelvel(self,*args):

        #compute relative velocity with respect to reference
        currentz=float(self.relvel.get())
        currentl=float(self.rellam.get())

        zline=utl.airtovac(self.catdata['lambda_fluxw'])/currentl-1
        ratio=(1+currentz)/(1+zline)
        self.catdata['relvel']=np.abs(299792.458*(ratio**2-1)/(1+ratio**2))

        if 'firsttime' in args:
            pass
        else:
            #update entries if table has been drawn already
            self.record_changes()
            self.update_tabdisplay()

    def inspect_current(self,oldformat=False):

        #kill open processes
        for pp in self.processes:
            pp.kill()
        self.processes=[]

        #find the row where focus is
        row=self.table_data.hasfocus()[0]
        #query which ID it is
        idid=self.keycol.index('id')
        idred=self.keycol.index('redshift')
        idlambda=self.keycol.index('lambda_fluxw')
        focusid=self.table_data.get(row,idid)
        #launch displays
        try:
            #control ds9
            if(oldformat):
                rtname='objs/id{}/Pstamp_id{}'.format(focusid,focusid)
            else:
                rtname='objs/id{}/id{}_img.fits'.format(focusid,focusid)

            if(self.white is not None):
                if(oldformat):
                    ds9=subprocess.Popen(['ds9','-scale','zscale','-lock','smooth','-lock','frame','wcs',self.white,rtname+'_mean.fits',rtname+'_median.fits',rtname+'_half1.fits',rtname+'_half2.fits',rtname+'_det.fits','-smooth'])
                else:
                    ds9=subprocess.Popen(['ds9','-scale','zscale','-lock','smooth','-lock','frame','wcs',self.white,rtname+'[8]',rtname+'[9]',rtname+'[10]',rtname+'[11]',rtname+'[7]','-smooth'])
            else:
                if(oldformat):
                    ds9=subprocess.Popen(['ds9','-scale','zscale','-lock','smooth','-lock','frame','wcs',rtname+'_mean.fits',rtname+'_median.fits',rtname+'_half1.fits',rtname+'_half2.fits',rtname+'_det.fits','-smooth'])
                else:
                    ds9=subprocess.Popen(['ds9','-scale','zscale','-lock','smooth','-lock','frame','wcs',rtname+'[8]',rtname+'[9]',rtname+'[10]',rtname+'[11]',rtname+'[7]','-smooth'])
            #collect processes
            self.processes.append(ds9)

            #control spectra gui
            tmpz=float(self.table_data.get(row,idred))
            if(tmpz > 0):
                pass
            else:
                currentl=float(self.rellam.get())
                tmpz=float(self.table_data.get(row,idlambda))/currentl-1
                print(tmpz)
            spc=subprocess.Popen(['python','{}/redshifts/zfit.py'.format(os.environ['MYPYTHON']),'-i','objs/id{}/spectrum.fits'.format(focusid),'-z','{}'.format(tmpz)])
            self.processes.append(spc)

            #view cube
            if(oldformat):
                ds93d=subprocess.Popen(['ds9','-3d','objs/id{}/segcube.fits'.format(focusid),'-3d','vp','90','0','-cmap','color'])
            else:
                ds93d=subprocess.Popen(['ds9','-3d','objs/id{}/id{}_img.fits[1]'.format(focusid,focusid),'-3d','vp','90','0','-cmap','color'])
            self.processes.append(ds93d)

        except:
            pass

    def gotofirst(self):
        self.record_changes()
        self.currentpage.set(1)
        self.statuspage.set("Page {}/{}".format(self.currentpage.get(),self.allpages.get()))
        self.update_tabdisplay()

    def gotolast(self):
        self.record_changes()
        self.currentpage.set(int(self.allpages.get()))
        self.statuspage.set("Page {}/{}".format(self.currentpage.get(),self.allpages.get()))
        self.update_tabdisplay()

    def previous_page(self):

        if(int(self.currentpage.get()) > 1):
            #freeze current changes
            self.record_changes()
            #step page and update
            self.currentpage.set(int(self.currentpage.get())-1)
            self.statuspage.set("Page {}/{}".format(self.currentpage.get(),self.allpages.get()))
            self.update_tabdisplay()
        else:
            pass

    def next_page(self):

        if(int(self.currentpage.get()) < self.pagenum):
            #freeze current changes
            self.record_changes()
            #step page and update
            self.currentpage.set(int(self.currentpage.get())+1)
            self.statuspage.set("Page {}/{}".format(self.currentpage.get(),self.allpages.get()))
            self.update_tabdisplay()
        else:
            pass

    def sorttab(self,*args):

        #record changes
        self.record_changes()

        #sort table
        if('optimal' in self.sortlist.get()):
            self.catdata.sort(['inspect','OverContinuum','SNR'])
            self.catdata.reverse()
        else:
            self.catdata.sort(self.sortlist.get())

        #reset page number and update display
        self.currentpage.set(1)
        self.statuspage.set("Page {}/{}".format(self.currentpage.get(),self.allpages.get()))
        self.update_tabdisplay()

    def reversetab(self):

        #record changes
        self.record_changes()

        #reverse and update
        self.catdata.reverse()

        self.currentpage.set(1)
        self.statuspage.set("Page {}/{}".format(self.currentpage.get(),self.allpages.get()))
        self.update_tabdisplay()

    def client_exit(self):

        #record changes
        self.record_changes()

        #trigger write current on exit
        self.write_current()

        #exit
        self.client_abort()

    def client_abort(self,keeplock=False):

        """
        Kill without saving
        """

        #kill shell processes
        for pp in self.processes:
            pp.kill()

        #clean lock file
        if(keeplock):
            pass
        else:
            os.remove(self.startfile+'.lock')

        #exit for good
        self.destroy()

    def write_current(self):

        #record changes
        self.record_changes()

        #sort again table in id -- make copy not to interfere with current sorting
        tmpcopy=self.catdata.copy()
        tmpcopy.sort('id')

        #strip relvel column
        tmpcopy.remove_column('relvel')

        #check time stamp to avoid overwite of catalogue that has changed to disk
        currenttime=os.path.getmtime(self.startfile)
        if(currenttime > self.timeio):
            #possible issues due to io
            tkMessageBox.showinfo('I/O Warning','The catalogue may have changed to disk. Writing conflicting copy to disk. Resolve issue before proceding')
            tmpcopy.write(self.startfile+'_conflicted',format='fits')
            self.client_abort()
        else:
            #write to disk overwriting
            tmpcopy.write(self.startfile,format='fits',overwrite=True)
            #reset clock
            self.timeio=os.path.getmtime(self.startfile)

def start(startfile,white):

    app = Window(None,startfile=startfile,white=white)
    app.title('Emitter classifier')
    app.mainloop()

if __name__=="__main__":

    parser = argparse.ArgumentParser(description='Process input')
    parser.add_argument('ifile',default=None,help='Input file')
    parser.add_argument('-w','--white',default=None,help='White image')

    args = parser.parse_args()
    start(args.ifile,args.white)


import scrapy
from scrapy import Request
from okooo.items import OkoooItem

class QuotesSpider(scrapy.Spider):
    name = "okooo"
    start_urls = [
        'http://www.okooo.com/danchang/',
    ]

    default_headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Encoding': 'gzip, deflate, sdch, br',
        'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
        'Cache-Control': 'max-age=0',
        'Connection': 'keep-alive',
        'Host': 'www.douban.com',
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
    }

#    def start_requests(self):

#        for url in self.start_urls:
#            yield Request(url=url, headers=self.default_headers, callback=self.parse)

    def parse(self, response):
        for tr in response.css('tr.alltrObj'):
            item = OkoooItem()
            #item['matchId'] = tr.css('::attr(matchid)').extract_first()
            td1 = tr.css('td.td1')
            index = td1.css('i::text').extract_first()
            item['matchTitle'] = td1.css('a.ls::text').extract_first()
            td2 = tr.css('td.td2')
            item['matchtime'] = td2.css('span.BuyTime::attr(endtime)').extract_first()
            td3 = tr.css('td.td3')
            host = td3.css('a.sbg')
            draw = td3.css('a.pbg')
            guest = td3.css('a.fbg')
            item['host'] = host.css('span.homenameobj::attr(title)').extract_first()
            leageInfo = host.css('p.float_left i::text').extract()
            if leageInfo.len <= 1:
                item['hostLeage'] = item['matchTitle']
                item['hostLeageRank'] = leageInfo[0]
            else:
                item['hostLeage'] = leageInfo[0]
                item['hostLeageRank'] = leageInfo[1]
            item['hostRangfen'] = host.css('span.handicapobj::text').extract_first()
            item['hostSP'] = host.css('em.pltxt::text').extract_first()
            item['drawSP'] = draw.css('em::text').extract_first()
            item['guest'] = guest.css('span.awaynameobj::text').extract_first()
            item['guestSP'] = guest.css('em.pltxt::text').extract_first()
            guestInfo = guest.css('p.float_right i::text').extract()
            if guestInfo.len <= 1:
                item['guestLeage'] = item['matchTitle']
                item['guestLeageRank'] = guestInfo[0]
            else:
                item['guestLeage'] = guestInfo[0]
                item['guestLeageRank'] = guestInfo[1]
            td8 = tr.css('td.td8')
            link = tr.css('td.td8 a::attr(href)').extract()
            tem['oddsLink'] = link[0]
            tem['historyLink'] = link[1]
            td9 = tr.css('td.td9')
            item['winOdds'] = td9.css('span.noborder0::text').extract_first()
            item['drawOdds'] = td9.css('span.noborder1::text').extract_first()
            item['loseOdds'] = td9.css('span.noborder2::text').extract_first()
            yield item


# unknown_words.py
# Find English words not in dictionary or exceptions
#
# 2022-07-04 PV

from collections import Counter
# from typing import Counter
from common_fs import get_all_files
from casefix_En import set_casefix, process_exceptions_En
import os

source = r'W:\Livres\A_Trier'
doit = True

unknowns: Counter[str] = Counter()

# dme est l'ensemble des mots anglais
with open(r'words\words.en.txt', 'r', encoding='UTF-8') as f:
    sme = set(mot for mot in f.read().splitlines())
with open(r'words\extra.en.txt', 'r', encoding='UTF-8') as f:
    sme |= set(mot for mot in f.read().splitlines())

def process_word(w: str, first: bool):
    if len(w) <= 1:
        return w
    w0 = w

    while w and str.isdigit(w[0]):
        w = w[1:]
    while w and str.isdigit(w[-1]):
        w = w[:-1]
    if len(w) == 0:   # Only digits
        return w0

    w = w0

    if w in sme or w in set_casefix or ('A' <= w[0] <= 'Z' and w not in set_casefix and w[0].lower()+w[1:] in sme) or w == 'js':
        return w0
    unknowns.update([w])
    # if w == 'CS':
    #     breakpoint()
    return '«'+w0+'»'

def process_segment(s):
    w = ''
    res = ''
    firstword = True
    for c in s:
        if c in [' ', '+', '.', ',', '-']:     # Simple quote is not considered a separator, maybe should only if not sandwich between letters
            # typically {'+', '#', '.', ',', ' ', '-'}
            if w != '':
                res += process_word(w, firstword)
                w = ''
                firstword = c != ' '
            res += c
        else:
            w += c
    if w != '':
        res += process_word(w, firstword)
    # Exceptions
    res = process_exceptions_En(res)
    return res

def process_name(name: str):
    ts = name.split(' - ')
    s1 = ts[0]

    #bp = ''
    try:
        pp = s1.index('(')
        #bp = s1[pp:]
        s1 = s1[:pp]
    except Exception:
        pass
    process_segment(s1)

nt = 0
for filefp in get_all_files(source):
    folder, file = os.path.split(filefp)
    bname, ext = os.path.splitext(file)
    nt += 1
    process_name(bname)

print()
print(nt, 'file(s) processed')
# print(unknowns.most_common(20))

with open(r'c:\temp\newwords.txt', 'w', encoding='utf-8') as out:
    for w, c in unknowns.most_common():
        if c == 1:
            break
        print(w, c)
        out.write(w+'\n')


"""Verb data controller.

Controlls I/O of verb data between the model and the actual data.
"""

from tkinter import messagebox
import typing as t
from model import Verb
import pandas as pd
from ast import literal_eval

class VerbDataController:
    """Verb data controller class.

    Controlls I/O of verb data between the model and the actual data.
    """

    data_path: str
    verbs: t.List[Verb]
    #is_modified: bool

    def __init__(self, data_path: str = "data/verbs.csv"):
        """Create a verb data controller.

        Controlls I/O of verb data between the model and the actual data.

        Parameters:
            data_path: Path to the verb data CSV.
        """
        self.data_path = data_path
        self.verbs = []
        self.load()

    def save(self):
        """Save the verb data to the CSV file."""
        if messagebox.askokcancel(
                message=f'Are you sure you want to overwrite the data?',
                icon='warning', title='Save'):
            df = pd.DataFrame.from_records([v.__dict__ for v in self.verbs])
            df.to_csv(self.data_path)

    def load(self):
        """Load the verb data from the CSV file."""
        self.verbs = []
        try:
            df = pd.read_csv(self.data_path, index_col=0, keep_default_na=False,
                converters={'nvn_syllables': literal_eval})
            for index, row in df.iterrows():
                verb_dict = {name: value for name, value in zip(df.columns, row)}
                verb = Verb(**verb_dict)
                self.verbs.append(verb)
        except Exception as e:
            print(f"Unable to load file '{self.data_path}', aborting: {e}")


import os
from django.http import FileResponse
import pandas as pd
import numpy as np
from rest_framework.views import APIView, Response, status
from django.core.paginator import Paginator
import re

from .models import Algorithm, Result
from file.models import File
from .lib.run import train
from utils.db import redis_conn

class GetAlgorithmView(APIView):
    """查询全部模型及算法
    """
    def post(self, request):
        pages = Paginator(Algorithm.objects.order_by("name"), int(request.data.get("size")))
        algos = pages.get_page(request.data.get("currentPage")).object_list
        res = {
            "status": 200,
            "message": "查询成功",
            "content": {
                "columns": [
                    {
                        "name": "模型名称",
                        "value": "modelName"
                    },
                    {
                        "name": "模型描述",
                        "value": "modelDescription",
                        "tooltip": True
                    },
                    {
                        "name": "数据集",
                        "value": "dataset"
                    },
                    {
                        "name": "算法名称",
                        "value": "neuralNetwork"
                    },
                    {
                        "name": "预测目标",
                        "value": "target"
                    },
                    {
                        "name": "训练状态",
                        "value": "status",
                    },
                    {
                        "name": "训练进度",
                        "value": "progress",
                    }
                    ],
                "data": [],
                "componentTitle": "",
                "pageTotal": pages.count
            }
        }

        for algo in algos:
            a = {
                "id": algo.pk,
                "modelName": algo.name,
                "modelDescription": algo.description,
                "dataset": algo.dataset.name,
                "neuralNetwork": algo.neuralNetwork,
                "target": algo.target,
                "status": algo.status
            }
            if redis_conn.get(algo.pk) is not None:
                progress = redis_conn.get(algo.pk).decode()
                a['progress'] = progress
            else:
                a['progress'] = 0

            res["content"]["data"].append(a)
        return Response(res, status=status.HTTP_200_OK)

class AlgorithmView(APIView):
    def post(self, request):
        '''新增模型和算法
        '''
        # 未对算法/target等进行进一步验证，日后可进行完善
        dataset = File.objects.get(pk=request.data.get("dataset"))
        if dataset is not None:
            algo = Algorithm(
                description = request.data.get("description"),
                name = request.data.get("name"),
                neuralNetwork = request.data.get("neuralNetwork"),
                layers = request.data.get("layers"),
                learningRate = request.data.get("learningRate"),
                neurons = request.data.get("neurons"),
                epoch = request.data.get("rounds"),
                batchSize = request.data.get("batchSize"),
                optimization = request.data.get("optimization"),
                selected = request.data.get("selected"),
                target = request.data.get("target"),
                verificationRate = request.data.get("verificationRate"),
                dataset = dataset,
            )
            try:
                algo.save()
                res = {
                    "status": 200,
                    "content": "上传成功"
                }
                return Response(res, status=status.HTTP_200_OK)
            except:
                res = {
                    "status": 500,
                    "content": "模型名称重复"
                }
                return Response(res, status=status.HTTP_400_BAD_REQUEST)
        else:
            res = {
                "status": 500,
                "content": "数据集不存在"
            }
            return Response(res, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request):
        """删除模型及算法
        """
        algo = Algorithm.objects.get(pk=request.data.get("id"))
        result = Result.objects.get(algo=algo)
        if algo is not None and result is not None:
            print(algo)
            # 删除神经网络模型
            img_path = [result.difference, result.loss]
            for p in img_path:
                os.remove(p[6:])
            model_name = algo.name + '.pth'
            os.remove('./torch_models/'+model_name)
            # 删除数据库内算法记录
            algo.delete()
            res = {
                "status": 200,
                "content": "删除成功"
            }
            return Response(res, status=status.HTTP_200_OK)
        else:
            res = {
                "status": 500,
                "content": "数据集不存在"
            }
            return Response(res, status=status.HTTP_400_BAD_REQUEST)

def analyze_csv(file_path):
    '''分析CSV文件
    '''
    # 读取 CSV 文件
    df = pd.read_csv(file_path)
    global current_file_row, current_file_column
    current_file_row, current_file_column = df.shape
    # 存放所有列的分析结果
    all_columns_analysis = []
    # 从数据框中随机选择一个实例值
    random_row = df.sample(1).iloc[0]
    count_i = 1
    for column in df.columns:
        # 列的分析结果
        column_analysis = {"columnName": column}
        column_analysis["id"] = str(count_i)
        # 数据类型
        dtype = df[column].dtype
        if dtype == "object":
            column_analysis["dataType"] = "string"
            column_analysis["min"] = "-"
            column_analysis["max"] = "-"
        else:
            column_analysis["dataType"] = str(dtype)
            column_analysis["min"] = str(np.float64(df[column].min()))
            column_analysis["max"] = str(np.float64(df[column].max()))
        # 为每一列添加一个实例值
        column_analysis["exampleData"] = str(random_row[column])
        count_i += 1
        # 将列的分析结果添加到总列表中
        all_columns_analysis.append(column_analysis)
    return np.array(all_columns_analysis)

class TrainingView(APIView):
    def post(self, request):
        algo: Algorithm = Algorithm.objects.get(pk=request.data.get("id"))
        if algo is not None:
            # 搭配celery在后台进行训练
            train.delay(algo.pk)
            res = {
                "status": 200,
                "message": "提交成功",
            }
            return Response(res, status=status.HTTP_200_OK)
        else:
            res = {
                "status": 500,
                "content": "算法不存在"
            }
            return Response(res, status=status.HTTP_400_BAD_REQUEST)

class ResultView(APIView):
    def get(self, request, image):
        print("--", os.getcwd())
        image_path = os.path.join('result', image)

        print(image_path)
        if os.path.exists(image_path):
            response = FileResponse(open(image_path, 'rb'), content_type='image/png')
            response['Content-Disposition'] = f'inline; filename={image}'
            return response
        else:
            return Response(status=status.HTTP_404_NOT_FOUND)
    def post(self, request):
        algo: Algorithm = Algorithm.objects.get(pk=request.data.get("id"))
        if algo is not None:
            result = Result.objects.get(algo=algo)
            if result is not None:
                res = {
                    "status": 200,
                    "message": "查询成功",
                    "content": {
                        "mse": str(result.mse),
                        "rmse": str(result.rmse),
                        "mae": str(result.mae),
                        "difference": result.difference,
                        "loss": result.loss,
                    },
                }
                return Response(res, status=status.HTTP_200_OK)
            else:
                res = {
                    "status": 500,
                    "message": "训练未完成"
                }
                return Response(res, status=status.HTTP_400_BAD_REQUEST)
        else:
            res = {
                "status": 500,
                "message": "算法不存在"
            }
            return Response(res, status=status.HTTP_400_BAD_REQUEST)