#!/usr/bin/env python3
#
# matrix_rain.py

import sys
import os
import time
import json
import random
import pygame
import pygame.freetype
import pytesseract
import numpy as np
import cv2
import webcolors
import symbiote.colortools as ct

color = ct.ColorConverter()
colors = color.get_color_names(format='rgba')
colors['neon_green'] = (57, 255, 20, 255)
colors['neon_yellow'] = (255, 255, 0, 255)
colors['neon_red'] = (255, 0, 51, 255)
colors['neon_blue'] = (77, 77, 255, 255)

# Add a new variable for toggling contour detection
contour_detection = False

color_cycle = False

# Matrix parameters
matrix = 0
num_streams = 10000
speed = .01
min_speed = 0.01
max_speed = 5.0
font_size = 12
default_font_color = 'white'
font_color = default_font_color
default_decay_color = 'green'
font_decay_color = default_decay_color
default_background_color = 'black'
background_color = default_background_color
fade_intensity = .1  # The higher this value, the faster the trails will fade
random_fade = False
new_stream = True
persistent_dot = False
font = 'Courier'

setting_message = None
setting_message_expiration = 0

ocr_enabled = False
static_overlay = False

# Initialize Pygame
pygame.init()
width, height = 1024, 768
# Create a resizable window
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
clock = pygame.time.Clock()
font = pygame.freetype.SysFont('Courier', font_size)

# Frames per second
FPS = 30

# Add a new variable for toggling edge detection
edge_detection = False
edge_fps = 30 # number of frames to detect edges on 1 out of 30 for example
edge_decay = 60
edge_detection_min = 50
edge_detection_max = 150
edge_surface = pygame.Surface((width, height), pygame.SRCALPHA)

# Create streams
new_streams = []
current_resolution = 0

def save_settings():
    print(f"Settings saved.")
    settings = {
        'max_speed': max_speed,
        'font_size': font_size,
        'fade_intensity': fade_intensity,
        'num_streams': num_streams,
        'persistent_dot': persistent_dot,
        'font_color': font_color,
        'font_decay_color': font_decay_color,
        'width': width,
        'height': height,
    }
    home = os.environ['HOME']
    with open(f'{home}/.symbiote/vison_settings.json', 'w') as f:
        json.dump(settings, f)

def load_settings():
    global max_speed, font_size, fade_intensity, num_streams, persistent_dot, font_color, font_decay_color, width, height
    try:
        with open('settings.json', 'r') as f:
            settings = json.load(f)
            max_speed = settings.get('max_speed', max_speed)
            font_size = settings.get('font_size', font_size)
            fade_intensity = settings.get('fade_intensity', fade_intensity)
            num_streams = settings.get('num_streams', num_streams)
            persistent_dot = settings.get('persistent_dot', persistent_dot)
            font_color = settings.get('font_color', font_color)
            font_decay_color = settings.get('font_decay_color', font_decay_color)
            width = settings.get('width', width)
            height = settings.get('height', height)
    except FileNotFoundError:
        pass  # It's okay if the file doesn't exist

#load_settings()

def detect_edges(surface):
    # Convert the Pygame surface to an OpenCV image
    cv_image = pygame.surfarray.array3d(surface)
    cv_image = cv2.cvtColor(cv_image, cv2.COLOR_RGB2BGR)

    # Convert the image to grayscale
    gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)

    # Apply the Canny edge detection algorithm
    edges = cv2.Canny(gray, 50, 150)

    # Create a color mask for the edges
    color_mask = np.zeros((edges.shape[0], edges.shape[1], 3), dtype=np.uint8)
    color_mask[edges != 0] = [0, 0, 255]  # Red color

    # Convert the color mask to a Pygame surface
    edges_surface = pygame.surfarray.make_surface(color_mask)

    return edges_surface

def draw_edges(surface):
    # Convert the Pygame surface to an OpenCV image
    cv_image = pygame.surfarray.array3d(surface)
    cv_image = cv2.cvtColor(cv_image, cv2.COLOR_RGB2BGR)

    # Convert the image to grayscale
    gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)

    # Apply the Canny edge detection algorithm
    edges = cv2.Canny(gray, 50, 150)

    # Find the contours from the edges
    contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # Draw the contours on the original image
    #cv2.drawContours(cv_image, contours, -1, (0, 255, 0), 1)

    # Color the edges red
    edges_color = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
    edges_color[:,:,2] = edges  # Red channel

    # Convert the colored edges image to a Pygame surface
    edges_surface = pygame.surfarray.make_surface(edges_color)

    # Convert the OpenCV image back to a Pygame surface
    #cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
    #edge_surface = pygame.surfarray.make_surface(cv_image)

    return edges_surface

# Modify the create_streams function to use the num_streams variable
def create_streams(matrix):
    streams = []
    if matrix == 0 or matrix == 2:
        for _ in range(num_streams):
            speed = random.uniform(min_speed, max_speed)
            x = random.randint(0, width)  # Random value within the width of the screen
            y = random.randint(0, height)  # Random value within the height of the screen
            color = list(colors[font_color])  # Make a copy of the color
            streams.append((speed, x, y, color))
    elif matrix == 1:
        for _ in range(num_streams):
            speed = random.uniform(min_speed, max_speed)
            x = random.randint(0, width)
            y = random.randint(0, height)
            color = list(colors[font_color])
            streams.append((speed, x, y))

    return streams

def detect_and_draw_contours(surface):
    # Get the dimensions of the display
    info = pygame.display.Info()
    width = info.current_w
    height = info.current_h

    # Convert the Pygame surface to an OpenCV image
    array = pygame.surfarray.array3d(surface)
    array = np.rot90(array)
    array = np.flipud(array)
    cv_image = cv2.cvtColor(array, cv2.COLOR_RGB2BGR)

    # Convert the image to grayscale
    gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)

    # Apply a threshold to highlight the contours
    _, threshold = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY)

    # Find the contours
    contours, _ = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # Create a new surface with the same size as the original
    contour_surface = pygame.Surface((width, height), pygame.SRCALPHA)

    # Draw the contours on the new surface
    for contour in contours:
        if len(contour) > 2:  # Only draw the contour if it has more than 2 points
            points = [tuple(point[0]) for point in contour]  # Convert arrays to tuples
            pygame.draw.polygon(contour_surface, (0, 255, 0, 128), points)

    return contour_surface

# Help menu
help_menu = False

def rgb_to_hex(rgb):
    return '#{:02x}{:02x}{:02x}'.format(rgb[0], rgb[1], rgb[2])

def hex_to_rgb(hex):
    h = hex.lstrip('#')
    return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))

def display_help_menu():
    # Create a semi-transparent surface
    surface = pygame.Surface((width, height), pygame.SRCALPHA)
    surface.fill((0, 0, 0, 128))  # Semi-transparent black

    # Create a font object
    font = pygame.freetype.SysFont('Courier', 24)

    # Define the help text
    help_text = [
        "Help Menu:",
        "UP: Increase speed",
        "DOWN: Decrease speed",
        "+: Increase font size",
        "-: Decrease font size",
        "H: Show/hide this help menu",
        "I: Increase fade intensity",
        "D: Decrease fade intensity",
        "F: Toggle fullscreen",
        "K: Increase number of streams",
        "L: Decrease number of streams",
        "N: Cycle matrix vissual",
        "C: Toggle contour detection",
        "T: Toggle OCR detection",
        "R: Cycle through resolutions",
        "1: Toggle random fade intensity",
        "3: Toggle persistent dot",
        "4: Cycle font color",
        "5: Cycle decay color",
        "6: Cycle background color",
        "8: Toggle edge detection",
        "9: Toggle static overlay",
        "S: Save settings",
        "Q: Quit / Exit",
    ]

    # Render the help text
    for i, line in enumerate(help_text):
        font.render_to(surface, (10, 10 + 20*i), line, colors['neon_green'])

    return surface

def generate_tv_static(width, height, alpha=128):
    static = np.random.randint(0, 255, (width, height, 3), dtype=np.uint8)

    # Convert the noise image to a Pygame surface
    static_surface = pygame.surfarray.make_surface(static)

    # Create a semi-transparent surface with the same size
    alpha_surface = pygame.Surface(static_surface.get_size(), pygame.SRCALPHA)
    alpha_surface.fill((0, 0, 0, alpha))

    # Blend the static with the semi-transparent surface
    static_surface.blit(alpha_surface, (0, 0), special_flags=pygame.BLEND_RGBA_ADD)

    return static_surface

def cycle_color(current):
    # Get a list of color names
    color_names = list(colors.keys())

    # Find the index of the current color
    current_index = color_names.index(current)

    # Get the next color in the list, wrapping around to the start if necessary
    next_index = (current_index + 1) % len(color_names)

    # Return the name and RGB values of the next color
    return color_names[next_index], colors[color_names[next_index]]

# Main loop
running = True
setting_message = False
count = 0
recog = 0
frame_counter = 0
while running:
    frame_counter += 1

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.VIDEORESIZE:
            # Update the window size
            width, height = event.size
            # Recreate the streams to fit the new window size
            streams = create_streams(matrix)
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                max_speed = min(50, max_speed + 0.1)
                setting_message = f"Fall rate: {max_speed}"
            elif event.key == pygame.K_DOWN:
                max_speed = max(0.1, max_speed - 0.1)
                setting_message = f"Fall rate: {max_speed}"
            elif event.key == pygame.K_h:
                help_menu = not help_menu  # Toggle help menu
            elif event.key == pygame.K_EQUALS:
                font_size += 2
                font = pygame.freetype.SysFont('Courier', font_size)
                setting_message = f"Font size: {font_size}"
            elif event.key == pygame.K_MINUS:
                font_size = max(2, font_size - 2)
                font = pygame.freetype.SysFont('Courier', font_size)
                setting_message = f"Font size: {font_size}"
            elif event.key == pygame.K_n:
                new_stream = True
                if matrix >= 3:
                    matrix = 0
                else:
                    matrix += 1
                if matrix == 1:
                    num_streams = 100
                if matrix == 0 or matrix == 2:
                    num_streams = 5000
                setting_message = f"Matrix visual: {matrix}"
            elif event.key == pygame.K_i:
                fade_intensity = min(5, fade_intensity + 0.1)
                setting_message = f"Fade intensity: {fade_intensity}"
            elif event.key == pygame.K_d:
                fade_intensity = max(0.01, fade_intensity - 0.1)
                setting_message = f"Fade intensity: {fade_intensity}"
            elif event.key == pygame.K_k:
                num_streams = min(100000, num_streams + 250)  # Increase the number of streams
                setting_message = f"Number of streams: {num_streams}"
            elif event.key == pygame.K_l:
                num_streams = max(10, num_streams - 250)  # Decrease the number of streams
                setting_message = f"Number of streams: {num_streams}"
            elif event.key == pygame.K_3:
                persistent_dot = not persistent_dot
                setting_message = f"Persistent dot: {'On' if persistent_dot else 'Off'}"
            elif event.key == pygame.K_4:
                # cycle font_color
                font_color, font_rgb = cycle_color(font_color)
                setting_message = f"Font color: {font_color}"
            elif event.key == pygame.K_5:
                # cycle font_decay_color
                font_decay_color, font_decay_rgb = cycle_color(font_decay_color)
                setting_message = f"Font decay color: {font_decay_color}"
            elif event.key == pygame.K_f:
                # Toggle fullscreen
                if screen.get_flags() & pygame.FULLSCREEN:
                    pygame.display.set_mode((width, height))
                else:
                    pygame.display.set_mode((width, height), pygame.FULLSCREEN)
                setting_message = f"Fullscreen: toggle"
            elif event.key == pygame.K_6:
                # Cycle background color
                background_color, _ = cycle_color(background_color)
                setting_message = f"Background color: {background_color}"
            elif event.key == pygame.K_7:
                color_cycle = not color_cycle
                setting_message = f"Color cycle: {color_cycle}"
            elif event.key == pygame.K_r:
                # Get a list of supported resolutions
                resolutions = pygame.display.list_modes()
                # Cycle through resolutions
                current_resolution = (current_resolution - 1) % len(resolutions)
                width, height = resolutions[current_resolution]
                height = int(height * 0.90)
                screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
                setting_message = f"Resolution: {width}x{height}"
                new_stream = True
            elif event.key == pygame.K_s:
                save_settings()
            elif event.key == pygame.K_t:
                ocr_enabled = not ocr_enabled
                setting_message = f"OCR: {'On' if ocr_enabled else 'Off'}"
            elif event.key == pygame.K_c:
                contour_detection = not contour_detection
                setting_message = f"Contour detection: {'On' if contour_detection else 'Off'}"
            elif event.key == pygame.K_q:
                save_settings()
                sys.exit(0)
            elif event.key == pygame.K_8:
                edge_detection = not edge_detection
                setting_message = f"Edge detection: {'On' if edge_detection else 'Off'}"
            elif event.key == pygame.K_1:
                random_fade = not random_fade
                setting_message = f"Random fade: {random_fade} {fade_intensity}"
            elif event.key == pygame.K_0:
                font_color = default_font_color
                decay_color = default_decay_color
                background_color = default_background_color
                setting_message = f"Reset colors: {font_color} {decay_color} {background_color}"
            elif event.key == pygame.K_9:
                static_overlay = not static_overlay
                setting_message = f"Toggle static overlay: {static_overlay}"

    current_frame = screen.copy()

    if random_fade:
        fade_intensity = random.uniform(0.1, 5.0)

    if setting_message:
        print(setting_message)
        setting_message = False
    #    new_stream = True

    if new_stream:
        # Get the dimensions of the display
        info = pygame.display.Info()
        display_width = info.current_w
        display_height = info.current_h
        print(f"Screen resolution: {display_width} x {display_height}")

        streams = create_streams(matrix)
        new_stream = False

    if display_width != width or display_height != height:
        display_width = width
        display_height = height
        print(f"Screen resolution: {width} x {height}")

    # Clear screen
    screen.fill((0, 0, 0, 120))

    # Clear screen with a semi-transparent black rectangle
    fade_surface = pygame.Surface((width, height), pygame.SRCALPHA)
    fade_surface.fill((0, 0, 0, edge_decay))
    fade_surface.fill((0, 0, 0, fade_intensity))
    screen.blit(fade_surface, (0, 0))

    if color_cycle:
        # cycle font_color
        font_color, font_rgb = cycle_color(font_color)

    # Update and draw streams
    new_streams = []
    if matrix == 0:
        for speed, x, y, color in streams:
            # Update color
            color[3] = max(0, color[3] - fade_intensity)  # Reduce alpha component

            # Draw character
            char = chr(random.randint(0x20, 0x7E))  # ASCII characters
            column = 0
            font.render_to(screen, (x, int(y)), char, color)

            # Update position
            y += speed
            if y > height:
                #y = 0
                color = list(colors[font_color])  # Reset color
                y = random.randint(-height, 0)
            new_streams.append((speed, x, y, color))
        streams = new_streams
    elif matrix == 1:
        for speed, x, y in streams:
            # Draw characters
            for i in range(int(y), height, font_size):
                intensity = min(1, max(0, (i - y) / height))
                color = [int(c * intensity) for c in colors[font_color]]
                decay_color = [int(c * (1 - intensity)) for c in colors[font_decay_color]]
                final_color = [c1 + c2 for c1, c2 in zip(color, decay_color)]
                char = chr(random.randint(0x20, 0x7E))  # ASCII characters
                font.render_to(screen, (x, i), char, final_color)

            # Update stream
            y += speed
            if y > height:
                y = random.randint(-height, 0)
            new_streams.append((speed, x, int(y)))
        streams = new_streams
    elif matrix == 2:
        for speed, x, y, color in streams:
            # Update color
            color[3] = max(0, color[3] - fade_intensity) # Reduce alpha component

            # Draw character
            char = chr(random.randint(0x20, 0x7E)) # ASCII characters

            # Calculate intensity
            intensity = min(1, max(0, y / height))

            # Calculate decay color
            decay_color = [int(c * (1 - intensity)) for c in colors[font_decay_color]]

            # Calculate trail color
            trail_color = [int(c * intensity) for c in colors[font_decay_color]]

            # Calculate final color
            final_color = [min(255, c1 + c2) for c1, c2 in zip(color, decay_color)]

            # Render character with final color
            font.render_to(screen, (x, int(y)), char, final_color)

            # Render trailing characters with trail color
            for i in range(int(y), height, font_size):
                font.render_to(screen, (x, random.randint(0, height)), char, trail_color)

            # Update position
            y += speed
            if y > height:
                #y = 0
                color = list(colors[font_color]) # Reset color
                y = random.randint(-height, 0)
            new_streams.append((speed, x, y, color))
        streams = new_streams

    elif matrix == 3:
        # Generate a random noise image
        static = np.random.randint(0, 255, (width, height, 3), dtype=np.uint8)

        # Convert the noise image to a Pygame surface
        static_surface = pygame.surfarray.make_surface(static)

        # Blit the static surface onto the screen
        screen.blit(static_surface, (0, 0))

    if static_overlay:
        # Generate a semi-transparent static surface
        static_surface = generate_tv_static(width, height, alpha=128)

        # Blit the static surface onto the screen
        screen.blit(static_surface, (0, 0))

    # OCR function
    if ocr_enabled:
        recog += 1
        if recog >= 100:
            pygame.image.save(screen, '/tmp/temp.png')
            text = pytesseract.image_to_string('/tmp/temp.png')
            if text:
                print(f"Recognized text: {text}")
            recog = 0

    # Draw the persistent dot
    if persistent_dot:
        pygame.draw.circle(screen, (0, 255, 0), (width//2, height//2), 3)

    # Display help menu
    if help_menu:
        help_surface = display_help_menu()
        screen.blit(help_surface, (0, 0))

    contour_frames = 10
    if contour_detection:
        if frame_counter % contour_frames == 0:
            print(frame_counter, contour_frames)
            contour_surface = detect_and_draw_contours(screen)
            screen.blit(contour_surface, (0, 0))

    # Draw edges on the screen if edge detection is enabled
    if edge_detection:
        new_edge_surface = detect_edges(screen)
        edge_surface.blit(new_edge_surface, (0, 0))
        edge_surface.set_alpha(100)
        screen.blit(edge_surface, (0, 0))

    if frame_counter >= 1024:
        frame_counter = 0

    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()


# coding: utf-8

# In[1]:

def polarization_interception_efficiency(alpha,N_R,N_P):
    import numpy as np
    import scipy as sp
    import scipy.io as si
    import scipy.constants as sc
    #from scipy.interpolate import Rbf
    from scipy.interpolate import griddata

    pi = sc.pi
    mat_contents = si.loadmat('theta_b_PR_results_3.mat')
    matrix = mat_contents['results']
    x = np.exp(np.transpose(matrix[:,0]))
    y = np.exp(np.transpose(matrix[:,1]))
    z = np.exp(np.transpose(matrix[:,2]))
    d = np.exp(np.transpose(matrix[:,3]))
    #rbfi = Rbf(x,y,z,d)
    xi = alpha*np.ones(N_R.size)
    yi = N_R
    zi = N_P
    di = griddata((x,y,z), d, (xi, yi, zi), method='linear')
    #di = rbfi(xi,yi,zi)
    theta_b = pi/(1+di)
    eta_PR_fit = 1/np.sqrt(alpha)*np.sin(theta_b)+alpha**2*N_P*(pi-theta_b)
    return eta_PR_fit

# In[ ]:


import os
import random
from pathlib import Path
from functools import reduce
import numpy as np
import torch
import torch.nn.functional as F
from client import Client
from model import MLP

dataset_dir: Path = Path("femnist")
centralized_partition: Path = dataset_dir / 'client_data_mappings' / 'centralized'
partition_dir = dataset_dir / 'client_data_mappings' / 'fed_natural'

class Server:
    def __init__(self,client_per_round,round,device,cids=None,client_epoch=5,alpha=1.0):
        self.device = device
        self.c_per_r = client_per_round
        self.rounds = round
        cids_list = [cid for cid in os.listdir("femnist/client_data_mappings/fed_natural") if "_" not in cid]
        if not cids:
            self.cids = random.choices(cids_list,k=self.c_per_r*self.rounds)
        else:
            self.cids = cids
        print("Client ID for this ronds are ",self.cids)
        self.client_epoch = client_epoch
        self.clients = [self.generate_client(cid,client_epoch) for cid in self.cids]

        self.alpha = alpha
        self.model = MLP(alpha)
        self.save_path = "model/server_"+str(self.c_per_r)+"_"+str(self.rounds)
        if not os.path.exists(self.save_path):
            os.mkdir(self.save_path)
        self.save_model(round=0)

        # for recording the model state and performance per rounds
        self.round_weights = []
        self.round_weights.append(self.model.get_weights())
        self.round_accs = []
        self.round_loss = []

    def generate_client(self,cid,epoch=5):
        return Client(dir=partition_dir,cid=cid,device=self.device,epoch=epoch)

    def save_model(self,round=0):
        torch.save(self.model.state_dict(), self.save_path+"/round_"+str(round)+".model")

    def process(self):
        for i in range(self.rounds):
            print("Rounds: ",i+1)
            clients = self.clients[i*self.c_per_r:(i+1)*self.c_per_r] if (i+1)*self.c_per_r<len(self.clients) else self.clients[-self.c_per_r:]
            accs = []
            losss = []
            data_lens = []
            weights = []
            for c in clients:
                print("Client ID = ",c.cid)
                acc,loss,d_len,w = c.process(self.model)
                accs.append(acc)
                losss.append(loss)
                data_lens.append(d_len)
                weights.append(w)
            new_weight = self.fedavg(accs,data_lens,weights)
            self.model = self.new_model(new_weight)
            self.save_model(round=i+1)
            self.round_weights.append(self.model.get_weights())
            loss,dlens,acc = self.evaluate()
            self.round_accs.append(acc)
            self.round_loss.append(loss)
            print("Eval_Loss: {:.4f} Eval_Acc: {:.4f}".format(loss, acc))
        pruning_accs, pruning_loss = self.evaluate_pruning()
        return self.round_loss, self.round_accs, self.round_weights, pruning_accs, pruning_loss

    def fedavg(self,accs,d_lens,weights):
        ratio = np.array(d_lens)
        ratio = ratio/ratio.sum()
        new_weights = [[r*w for w in weight] for r,weight in zip(ratio,weights)]
        new_weight = []
        for i in range(len(new_weights[0])):
            w_i = []
            for j in range(len(new_weights)):
                w_i.append(new_weights[j][i])
            new_weight.append(reduce(lambda x,y:x+y,w_i))
            #print(new_weight[-1].shape)
        return new_weight

    def new_model(self,new_weight):
        for w,layer in zip(new_weight,self.model._layers):
            layer.w = torch.nn.Parameter(w)
        return self.model

    def evaluate(self):
        c0 = Client(dir=centralized_partition,cid=0,epoch=20,device=self.device)
        print("Evaluating final rounds model with full test data ...")
        return c0.evaluate(self.model)

    def prune_by_magnitude(self,percent_to_keep, weight):
        condition = torch.abs(weight.flatten())
        how_many = int(percent_to_keep * torch.numel(condition))
        top_k = torch.topk(condition, k=how_many)
        mask = torch.zeros(condition.shape).to(self.device)
        mask[top_k.indices] = 1
        return mask.reshape(weight.shape)

    def evaluate_pruning(self):
        final_weights = self.model.get_weights()
        sparsity_levels = np.geomspace(0.01, 1.0, 20).tolist()
        acc_at_sparsity = []
        loss_at_sparsity = []
        for p_to_use in sparsity_levels:
            # Half the sparsity at output layer
            percent = [p_to_use, p_to_use, p_to_use, p_to_use, min(1.0, p_to_use * 2.0)]
            masks = []
            for p, w in zip(percent, final_weights):
                masks.append(self.prune_by_magnitude(p, w))
            #masks = torch.tensor(np.array(masks))
            c0 = Client(dir=centralized_partition,cid=0,epoch=20,device=self.device)
            test_dataloader = c0.test_loader
            loss, acc = c0._test(self.model, test_dataloader, masks=masks)
            acc_at_sparsity.append(acc)
            loss_at_sparsity.append(loss)

        return acc_at_sparsity, loss_at_sparsity

if __name__ == "__main__":
    s = Server(2,5)
    loss,acc,weights,p_acc,p_loss = s.process()


'''import SecondFile
import datetime
import calendar

#from SecondFile import subject

#SecondFile.display_name("John Doe")

studentName = SecondFile.display_name
studentName("Johnny Does")

dateNow = datetime.date.today()
print(dateNow)

isLeapYear = calendar.isleap(2023)

if isLeapYear:
    print("2023 is a leap year")
else:
    print("2023 is not a leap year")'''

import tkinter as tk
from PIL import ImageTk, Image

form = tk.Tk()
form.title("Rotate Image")

imageName = "dog.jpg"
openImage = Image.open(imageName)
rotatedImage = openImage.rotate(90)

img = ImageTk.PhotoImage(rotatedImage)

labelWidget = tk.Label(form, image=img)
labelWidget.pack()

form.mainloop()


import asyncio
import re
from collections.abc import Sequence

from pywebio.output import *
from pywebio.session import get_info

requirejs_config_tpl = """

"""

requirejs_tpl = """
%s

"""

def load_notebook(resources=None, verbose=False, hide_banner=False, load_timeout=5000):
    """加载 Bokeh 资源

    :param resources: 目前不支持自定义静态资源的链接
    :param verbose: 开启 Bokeh 日志 并显示 Bokeh 加载标签
    :param hide_banner: 不支持
    :param load_timeout: 不支持
    :return: None
    """
    from bokeh import __version__
    from bokeh.util.serialization import make_id

    js_gists = ["console.log('Load BokehJS complete.')"]

    html = requirejs_config_tpl.replace('__version__', __version__)
    if verbose:
        element_id = make_id()
        html += """

            Loading BokehJS ...

        """.format(element_id=element_id)

        js_gists.append(
            "document.getElementById({element_id}).innerHTML = 'Load BokehJS complete.'".format(element_id=element_id))

        js_gists.append('Bokeh.set_log_level("info");')
        js_gists.append("console.log('Set bokeh log level to INFO because you set `output_notebook(verbose=True)`')")

    put_html(requirejs_tpl % (html, '\n'.join(js_gists)), sanitize=False)

def show_doc(obj, state, notebook_handle):
    """Show a document of Bokeh

    :param obj:
    :param state:
    :param notebook_handle: 不支持
    :return:
    """
    from bokeh.embed import components

    script, div = components(obj, wrap_script=False)
    if isinstance(obj, Sequence):
        div = '\n'.join(div)
    elif isinstance(obj, dict):
        div = '\n'.join(div[k] for k in obj.keys())

    put_html(requirejs_tpl % (div, script), sanitize=False)

def show_app(app, state, notebook_url, port=0, **kw):
    """Show Bokeh applications

    :param app: A Bokeh Application to embed in PyWebIO.
    :param state: ** Unused **
    :param notebook_url:  ** Unused **
    :param port: Bokeh Server 端口
    :param kw: 传给 Bokeh Server 的额外参数
    """

    from bokeh.server.server import Server
    from bokeh.io.notebook import _origin_url, uuid4, curstate, _server_url

    from pywebio.platform.tornado import ioloop
    loop = ioloop()
    if loop is None:
        toast("Currently only supports showing bokeh application in Tornado backend",
              color='error', duration=0)
        return
    loop.make_current()
    asyncio.set_event_loop(loop.asyncio_loop)
    # loop = IOLoop.current()

    info = get_info()

    allow_websocket_origin = [info.server_host]
    if info.origin:
        allow_websocket_origin.append(_origin_url(info.origin))

    server = Server({"/": app}, io_loop=loop, port=port, allow_websocket_origin=allow_websocket_origin, **kw)

    server_id = uuid4().hex
    curstate().uuid_to_server[server_id] = server

    server.start()

    url = _server_url(info.server_host, server.port)

    from bokeh.embed import server_document
    script = server_document(url, resources=None)

    script = re.sub(r'',  # lgtm [py/bad-tag-filter]
                    r"""

                    """, script, flags=re.I)

    put_html(script, sanitize=False)

def try_install_bokeh_hook():
    """尝试安装bokeh支持"""
    try:
        from bokeh.io import install_notebook_hook
    except ImportError:
        return False

    install_notebook_hook('pywebio', load_notebook, show_doc, show_app)
    return True


# https://metrogeek.fr/django-creer-formulaire/
from django import forms
from django.forms.widgets import TextInput

from .models import Population, Patient, Clinicien, Agenda, enAttente
from module.models import Module
from django.contrib.auth.models import User, Group
from django.contrib.auth.forms import UserCreationForm

from django.utils import timezone

#################################################################################################
################################### Enregistrement (patient view)###############################
#################################################################################################

########################################## User #############################################
class UserRegisterFrom(UserCreationForm):
    """
    Formulaire pour définition d'un nouvelle utilisateur, il est raccorder soit par un Patient soit un clinicien
    """
    class Meta:
        model = User
        fields = ['username', 'first_name', 'last_name', 'email', 'password1', 'password2']
        labels = {
            'username': "Nom d'utilisateur du site",
            'email': "Votre e-mail :",
            'password1': "Mot de passe",
            'password2': "Confirmer votre mot de passe :"
        }
########################################## Patient #############################################
class PatientRegisterFrom(forms.ModelForm):
    """
    Intégré dans la vue pt.html
    Ce formulaire crée un patient il est raccorder par UserResgisterForm
    """
    class Meta:
        model = Patient
        fields = ["universite", "parcours", 'telephone', "skype"]
        labels = {
            "universite": "Votre université : ",
            "parcours": "Dans quel parcours êtes vous ?",
            'telephone': "Votre numéro de téléphone :",
            'skype': "Veuillez donner votre nom d'utilisateur skype :"
        }
        widgets = {
            'telephone': TextInput(attrs={
                'type': 'tel',
                'class': "form-control",
                'placeholder': "Exemple : 0610101010",
                "Pattern": "/(\+\d+(\s|-))?0\d(\s|-)?(\d{2}(\s|-)?){4}/"
            }),
        }

########################################## Clincien #############################################
class ClinicienRegisterFrom(forms.ModelForm):
    """
        Intégré dans la vue cl.html
        Ce formulaire crée un clinicien il est raccorder par UserResgisterForm
        """
    class Meta:
        model = Clinicien
        fields = ["photoProfil"]
        labels = {
            "photoProfil": "Votre photo pour acquérir la confiance de vos patient "
        }

#################################################################################################
##################################### Interaction cli et patient ##############################
#################################################################################################

########################################## Agenda #############################################

class AgendaForm(forms.ModelForm):
    debut = forms.DateTimeField(initial=timezone.now(), label="Choissiez un moment :",
                                widget=TextInput(attrs={'type': 'datetime-local', 'class': "form-control"}))

    class Meta:
        model = Agenda
        fields = ['objet', 'debut', 'duree']
        labels = {
            'objet': 'Sujet de votre demande',
            'duree': 'Temps en heure et minutes',
        }
        widgets = {
            'objet': TextInput(attrs={'type': 'text', 'class': "form-control"}),
            'debut': TextInput(attrs={'type': 'datetime-local', 'class': "form-control"}),
            'duree': TextInput(attrs={'class': "form-control", "type": "time"}),
        }

    def __init__(self, *args, **kwargs):
        self.clinicien = kwargs['clinicien']
        self.patient = Patient.objects.get(pk=kwargs['patient'])
        del kwargs['patient']
        del kwargs['clinicien']
        super(AgendaForm, self).__init__(*args, **kwargs)

    def save(self, commit=True):
        instance = super(AgendaForm, self).save(commit=False)
        print(self.patient, self.clinicien)
        instance.patient = self.patient
        instance.clinicien = self.clinicien
        if commit:
            instance.save()
        return instance

##########################################  Ajout de questionnaire #############################################
class AjoutQuestForm(forms.ModelForm):
    """
    Ajout d'un questionnaire au patient
    """
    module = forms.ModelChoiceField(queryset=None, to_field_name="pk",
                                    widget=forms.Select(attrs={'class': 'form-control'}), initial=0, required=True)

    class Meta:
        model = enAttente
        fields = ['module', 'repetition', 'dateVisible']
        labels = {
            'module': "Veuillez choisir un questionnaire",
            'repetition': 'Choisiez une periode de répétition du questionnaire : (en jour)',
            'dateVisible': "Choisissez la date où le questionnaire sera visible :",
        }
        help_texts = {
            'repetition': "Vous pouvez laisser vide ce champs ainsi le questionnaire ne sera fais qu'une fois"
        }

    def __init__(self, *args, **kwargs):
        self.patient = kwargs['patient']
        del kwargs['patient']
        super(AjoutQuestForm, self).__init__(*args, **kwargs)
        pkUseByPatient = enAttente.objects.filter(patient=self.patient, dateFin__isnull=True).values("module")
        self.fields["module"].queryset = Module.objects.filter(isQuestionnaireOnly=True, isVisible=True,
                                                               nbSection__gt=0).exclude(pk__in=pkUseByPatient)

    def save(self, commit=True):
        instance = super(AjoutQuestForm, self).save(commit=False)
        instance.patient = Patient.objects.get(pk=self.patient)
        if commit:
            instance.save()
        return instance

##############################################################################################################################
###################################### Gestion ###############################################################################
##############################################################################################################################

########################################## Manage clinicien #############################################
class PatientClinicienForm(forms.ModelForm):
    clinicien_du_patient = forms.ModelMultipleChoiceField(label="Patient n'étant pas à la charge du clinicien",
                                                          queryset=None, to_field_name="pk", initial=0)

    class Meta:
        model = Clinicien
        fields = ['clinicien_du_patient']

    def __init__(self, *args, **kwargs):
        self.clincien__pk = kwargs['clinicien']
        del kwargs['clinicien']
        super(PatientClinicienForm, self).__init__(*args, **kwargs)
        self.fields['clinicien_du_patient'].widget = forms.CheckboxSelectMultiple(attrs={'class': 'form-check-input'})
        self.fields["clinicien_du_patient"].queryset = Patient.objects.exclude(clinicienACharge__pk=self.clincien__pk)

########################################## Manage Population #############################################
class PatientPopulationForm(forms.ModelForm):
    patient = forms.ModelMultipleChoiceField(label="Patient ne faisant pas partie du groupe",
                                             queryset=None, to_field_name="pk", initial=0)

    class Meta:
        model = Population
        fields = ['patient']

    def __init__(self, *args, **kwargs):
        self.population__pk = kwargs['population']
        del kwargs['population']
        super(PatientPopulationForm, self).__init__(*args, **kwargs)
        self.fields['patient'].widget = forms.CheckboxSelectMultiple(attrs={'class': 'form-check-input'})
        self.fields["patient"].queryset = Patient.objects.exclude(groupePatients=self.population__pk)

##########################################  Création Population #############################################
class GroupCreationForm(forms.ModelForm):
    class Meta:
        model = Population
        fields = ['categorie', "sequence", "lieu"]
        labels = {
            'categorie': 'Catégorie',
            "sequence": "Parcours utilisateur à definir",
            "lieu": "Population issue de"
        }

class GroupForm(forms.ModelForm):
    class Meta:
        model = Group
        fields = ['name']
        labels = {
            'name': "Le nom du groupe :"
        }
        widgets = {
            'name': forms.TextInput(attrs={"class": "form-control"})
        }


import main as m
import matplotlib.pyplot as plt

bd = m.PopulationData(name_file='bd.xls')
tbl = bd[0] + bd[1] + bd[2] # Выбираем базы данных (нужные таблицы)

tbl.limit_depth(0, 350) # Указываем глубину

tbl.print_locals()
ans = int(input('Выбор локации:'))
num_loc = ans
tbl_loc = tbl.limit_local(num_loc)

kinds = tbl.get_kinds()
tbl_loc.print_kinds()
locals = tbl.get_locals()

a = int(input('Первый график:'))
b = int(input('Второй график:'))
name_graph = 'Наложение видов ' + kinds[a] + '' + kinds[b] + ' (' + locals[num_loc] + ')'

for num in [a, b]:

    name = kinds[num] + '\n' + locals[num_loc]

    tbl_kind = tbl_loc.limit_kind(num)

    res = tbl_kind.filter_columns([0, 7, 11])
    res.group_by_column([0, 1])
    res.group_date()
    print(res)
    res.plot([2], name=name, stl='--')

plt.legend()
plt.show()
# plt.savefig(name_graph)


'''
Contains constant variables
'''

from lib import gconstants

FRIENDLY_FULLNAME =  gconstants.AGENT_INIT_FULLNAME # ""Banana Cluster Control Agent Daemon Init Control"
SHORT_NAME = gconstants.AGENT_INIT_NAME # "bccagent"
DAEMON_NAME = gconstants.AGENT_DAEMON_FULLNAME # "Banana Cluster Control Daemon"
#
VERSION = gconstants.VERSION
CONFIG_EXT = gconstants.CONFIG_EXT # "ini"
CONFIG_DIR = gconstants.CONFIG_DIR # "config"
#
CFG_SECT_INTERNAL = gconstants.AGENT_INIT_NAME # "bccagent"
LOGGING_NAME = gconstants.LOGGING_NAME # "console"
#
STATE_ON = "RUNNING"
STATE_OFF = "SHUTDOWN"
STATE_BUSY_ON = "BUSY"


# import dependencies

import requests
import pymongo
import pandas as pd

from splinter import Browser
from bs4 import BeautifulSoup
import time

# open chrome driver browser
def init_browser():
    executable_path = {'executable_path': 'chromedriver.exe'}
    return Browser('chrome', **executable_path, headless=False)

# ## NASA Mars News

def scrape():
    browser = init_browser()

    mars_news_url = "https://mars.nasa.gov/news/?page=0&per_page=40&order=publish_date+desc%2Ccreated_at+desc&search=&category=19%2C165%2C184%2C204&blank_scope=Latest'"
    browser.visit(mars_news_url)

    html = browser.html
    mars_news_soup = BeautifulSoup(html, 'html.parser')

    titles = mars_news_soup.body.find("div", class_="content_title").text

    paras = mars_news_soup.body.find("div", class_="article_teaser_body").text

    # ## Mars Space Images

    time.sleep(3)

    image_url = "https://www.jpl.nasa.gov/spaceimages/?search=&category=Mars"

    browser.visit(image_url)

    html = browser.html

    soup = BeautifulSoup(html, 'html.parser')

    image = soup.find('article', class_='carousel_item')

    image_url  = soup.find('article')['style']

    image_url= image_url.replace("background-image: url('", '')

    image_url= image_url.replace("');", "")

    full_image_url = "https://www.jpl.nasa.gov" + image_url

    full_image_url

    #browser.click_link_by_id('full_image')

    #browser.click_link_by_partial_text('more info')

    #image_html = browser.html
    #mars_image_soup = BeautifulSoup(image_html, 'html.parser')

    #image = mars_image_soup.body.find("figure", class_="lede")

    #link = image.find('a')
    #href = link['href']

    #base_url='https://www.jpl.nasa.gov'

    #featured_image_url = base_url + href

    #featured_image_url

    # ## Mars Weather

    #time.sleep(3)

    #weather_url = "https://twitter.com/marswxreport?lang=en"
    #browser.visit(weather_url)

    #html = browser.html
    #mars_weather_soup = BeautifulSoup(html, 'html.parser')

    #weather_tweet = soup.find_all('div', class_='css-1dbjc4n')

    #weather_tweet[2].find('span')

    #weather_tweet[0].find('section', class_='css-1dbjc4n')

    #tweet_braket=weather_tweet[0].find('div',class_='css-901oao r-hkyrab r-1qd0xha r-a023e6 r-16dba41 r-ad9z0x r-bcqeeo r-bnwqim r-qvutc0')

    # tweet_braket1=tweet_braket.find('span', class_='css-901oao css-16my406 r-1qd0xha r-ad9z0x r-bcqeeo r-qvutc0')

    # tweet=tweet_braket1.getText()

    # find the tweet text

    #tweet_container = mars_weather_soup.body.find('div','js-tweet-text-container')

    #mars_weather = tweet_container.find('p').text

    #mars_weather

    # ## Mars Facts
    time.sleep(3)
    facts_url = "https://space-facts.com/mars/"

    tables = pd.read_html(facts_url)

    df = tables[0]
    df.columns = ["Item", "Description"]

    facts_html=df.to_html()

    facts_html

    # ## Mars Hemispheres

    #cerberus
    time.sleep(3)

    url = 'https://astrogeology.usgs.gov/search/map/Mars/Viking/cerberus_enhanced'
    browser.visit(url)
    html = browser.html
    soup = BeautifulSoup(html, 'html.parser')
    url = soup.find('div', class_='downloads')
    link = url.find('a')
    ce_href = link['href']

    #Schiaparelli
    time.sleep(3)
    url = 'https://astrogeology.usgs.gov/search/map/Mars/Viking/Schiaparelli_enhanced'
    browser.visit(url)
    html = browser.html
    soup = BeautifulSoup(html, 'html.parser')
    url = soup.find('div', class_='downloads')
    link = url.find('a')
    sc_href = link['href']

    #Syrtis Major
    time.sleep(3)
    url = 'https://astrogeology.usgs.gov/search/map/Mars/Viking/Syrtis Major_enhanced'
    browser.visit(url)
    html = browser.html
    soup = BeautifulSoup(html, 'html.parser')
    url = soup.find('div', class_='downloads')
    link = url.find('a')
    sy_href = link['href']

    #valles_marineris
    time.sleep(3)
    url = 'https://astrogeology.usgs.gov/search/map/Mars/Viking/valles_marineris_enhanced'
    browser.visit(url)
    html = browser.html
    soup = BeautifulSoup(html, 'html.parser')
    url = soup.find('div', class_='downloads')
    link = url.find('a')
    va_href = link['href']

    hemispheres_image_urls = [
        {"title": "cerberus Hemisphere", "img_url": ce_href},
        {"title": "Schiaparelli Hemisphere", "img_url": sc_href},
        {"title": "Syrtis Major Hemisphere", "img_url": sy_href},
        {"title": "valles_marineris Hemisphere", "img_url": va_href}
    ]

    mars_dict = {
        'latestheadline': titles,
        'latestparagraph':  paras,
        'featuredimage': full_image_url,
        'factstable': facts_html,
        'weather': "tweet",
        "ce_title": "cerberus Hemisphere", "ce_img_url": ce_href,
        "sc_title": "Schiaparelli Hemisphere", "sc_img_url": sc_href,
        "sy_title": "Syrtis Major Hemisphere", "sy_img_url": sy_href,
        "va_title": "valles_marineris Hemisphere", "va_img_url": va_href
        }

    browser.quit()
    return mars_dict


import cv2
import time
import serial
import serial.tools.list_ports

def send_buzzer(freq):
    sendData = f"BUZZER={freq}\n"
    my_serial.write( sendData.encode() )

def main():
    camera = cv2.VideoCapture(0)
    camera.set(3,640)
    camera.set(4,480)

    face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
    eye_casecade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')

    sleep_count = 0
    while( camera.isOpened() ):
        _, image = camera.read()
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        faces = face_cascade.detectMultiScale(gray,scaleFactor=1.1,minNeighbors=5,minSize=(50,50),flags=cv2.CASCADE_SCALE_IMAGE)
        #print("faces detected Number: " + str(len(faces)))

        if len(faces):
            for (x,y,w,h) in faces:
                cv2.rectangle(image,(x,y),(x+w,y+h),(255,0,0),2)

                face_gray = gray[y:y+h, x:x+w]
                face_color = image[y:y+h, x:x+w]

                eyes = eye_casecade.detectMultiScale(face_gray,scaleFactor=1.2,minNeighbors=10)

                if len(eyes) == 0:
                    if sleep_count < 50:
                        sleep_count = sleep_count +1

                    if sleep_count >= 50:
                        print("졸지마")
                        send_buzzer(1000)
                else:
                    if sleep_count >= 2:
                        sleep_count = sleep_count - 2
                    send_buzzer(0)

                for (ex,ey,ew,eh) in eyes:
                    cv2.rectangle(face_color, (ex, ey), (ex+ew, ey+eh), (0,255,0), 2)
        print(sleep_count)
        cv2.imshow('result', image)

        if cv2.waitKey(10) == ord('q'):
            break

    cv2.destroyAllWindows()

if __name__ == '__main__':

    ports = list(serial.tools.list_ports.comports())
    for p in ports:
        if 'Arduino Uno' in p.description:
            print(f"{p} 포트에 연결하였습니다.")
            my_serial = serial.Serial(p.device, baudrate=9600, timeout=1.0)
            time.sleep(2.0)

    main()

    my_serial.close()

한끝아파52 소스코드/02.파이썬코드/9_2.눈을 인식하여 졸음방지 알리미 만들기/main9_2_3.py