#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
import json
import os
import logging
import traceback
import uuid
import requests
import queue
import time

sys.path.append('./lib')
sys.path.append('./res')
sys.path.append('./ui')

import res.resources
from ui.mainUI import Ui_stackedUI
from logging import handlers
from PyQt5 import QtCore, QtGui, QtWidgets
from lib.outlook_assistant import poll_availability, make_a_reservation, clear_reservations
from lib.access_tokens import uaid

class QtWidgetLogger(logging.Handler):
    """Custom Logging handler that is able to output stdout to a given QWidget for ex. QTextView"""
    def __init__(self):
        logging.Handler.__init__(self)
        self.outputWidget = None

    def emit(self, record):
        record = self.format(record)
        if record and self.outputWidget is not None:
            if len(record) == 1 and ord(str(record)) == 10:
                return
            else:
                cursor = self.outputWidget.textCursor()
                cursor.movePosition(QtGui.QTextCursor.End)
                cursor.insertText(record + "\r\n")
                self.outputWidget.setTextCursor(cursor)
                self.outputWidget.ensureCursorVisible()

# change workdir to scripts location
os.chdir(os.path.dirname(r'{0}'.format(sys.path[0])))

if not os.path.exists(sys.path[0] + "/logs/"):
    os.makedirs(sys.path[0] + "/logs/")

# setup logging
logger = logging.getLogger('Naurunappula')
logger.setLevel(logging.DEBUG)

fh = handlers.TimedRotatingFileHandler(sys.path[0] + '/logs/debug.log', when="d", interval=1, backupCount=7)
fh.setLevel(logging.DEBUG)

ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

qh = QtWidgetLogger()
qh.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)

logger.addHandler(fh)
logger.addHandler(ch)
logger.addHandler(qh)

data_container = queue.Queue() # used by GAThread

# Subclass QThread to send analytics data to Google Analytics
class GAThread(QtCore.QThread):
    def __init__(self, uaid, parent = None):
        QtCore.QThread.__init__(self, parent)
        self.uaid = uaid
        self.endpoint = 'https://www.google-analytics.com/collect'

    def run(self):
        """Send events to Google Analytics platform"""
        while True:
            try:
                data = data_container.get_nowait()
                if data is not None:
                    payload = {'v':'1',
                               'tid': self.uaid,         # tracking ID
                               'cid': data["uuid"],      # device uuid
                               't': 'event',             # hit type
                               'ec': data["category"],   # event category
                               'ea': data["action"],     # event action
                               'el': data["label"],      # event label
                               'ev': data["value"]       # event value
                        }
                    try:
                        logger.debug("Sending data to Google Analytics: {0}".format(data))
                        requests.post(url = self.endpoint, data = payload, headers = {'User-Agent': 'My User Agent 1.0'})
                    except:
                        logger.error("Failed to send data to Google Analytics. Trying again later.")
                data_container.task_done()
            except queue.Empty:
                pass
            finally:
                time.sleep(1)

# Subclass QThread to poll calendar availability
class CalendarPollerThread(QtCore.QThread):
    statusupdate = QtCore.pyqtSignal(dict)

    def __init__(self, preferences, interval, parent = None):
        QtCore.QThread.__init__(self, parent)
        self.preferences = preferences
        self.interval = interval
        self.working = False
        self.paused = False

    def run(self):
        while True:
            if not self.paused:
                logger.info("Polling meetingroom status")
                self.check_status()
            else:
                logger.info("Calendar polling currently paused")
            time.sleep(self.interval)

    def refresh_status(self):
        self.check_status()

    def check_status(self):
        """Polling method that checks what is the current status of the meetingroom. Reoccurs every 30 seconds"""
        if self.working:
            return

        self.working = True

        result = json.loads(poll_availability(self.preferences))
        self.statusupdate.emit(result)

        self.working = False

    def pause(self):
        self.paused = True

    def unpause(self):
        self.paused = False

    def ispaused(self):
        return self.paused

def sendToGoogleAnalytics(uuid, category, action, label, value = 0):
    payload = {}
    payload["uuid"] = uuid
    payload["category"] = category
    payload["action"] = action
    payload["label"] = label
    payload["value"] = value
    data_container.put(payload)

class Naurunappula(QtWidgets.QMainWindow, Ui_stackedUI):
    """Main class definition that implements Ui_stackedUI and all related events related to widgets defined in it"""
    def __init__(self, workdir="", parent=None):
        QtWidgets.QMainWindow.__init__(self, parent)

        self.uuid = ""

        self.setupUi(self)
        self.setup_uuid()

        self.working = False

        qh.outputWidget = self.txtLogOutput # assign a widget to receive log output. Can be accessed from settings

        # connect QT signals to handler methods
        self.green_btnPage1.clicked.connect(self.start_page2)
        self.red_btnPage1.clicked.connect(self.start_page2)
        self.red_btnPage2.clicked.connect(self.start_page1)
        self.red_btnCancel.clicked.connect(self.clear_reservation)
        self.green_btnDecline.clicked.connect(self.decline_reservation)
        self.green_btnAccept.clicked.connect(self.make_reservation)
        self.green_dialPage2.valueChanged.connect(self.dial_value_changed)

        # connect signals for settings1controls
        self.btnSettings1_Next.clicked.connect(self.start_page2)
        self.btnSettings2_Previous.clicked.connect(self.start_page1)
        self.btnExit.clicked.connect(self.close)
        self.buttonBox.accepted.connect(self.settings_acccepted)
        self.buttonBox.rejected.connect(self.settings_rejected)

        # create an invisible settings button that is always present in the top left corner of the screen
        self.btnSettings = QtWidgets.QPushButton('', self)
        self.btnSettings.setMinimumSize(QtCore.QSize(240, 80))
        self.btnSettings.setMaximumSize(QtCore.QSize(240, 80))
        self.btnSettings.setObjectName("btnSettings")
        self.btnSettings.clicked.connect(self.start_settings_page)

        style = "QPushButton#btnSettings { \
            border-image: url(:/images/blank.png); \
        }"

        self.btnSettings.setStyleSheet(style)
        self.btnSettings.move(40,0) # set absolute position for the button so it stays visible always for the user

        self.settingsPageClicked = 1
        self.settingsPageThreshold = 5 # how many times to push the hidden button to access settings
        self.settingsOpened = False # variable that indicates if user is manipulating settings at the moment

        # preferences dictionary
        self.preferences = {}
        self.preferences["name"] = ""
        self.preferences["email"] = ""
        self.preferences["server"] = ""
        self.preferences["username"] = ""
        self.preferences["password"] = ""

        self.settingsLoaded = False
        self.settingsLoaded = self.load_settings() # load settings from file. If this fails, just assume its a new installation and use default values.

        self.amountofErrors = 0 # amount of network errors happened thusfar in a row
        self.errorThreshold = 2 # how many errors in a row can happen before we show an error screen to the user

        # enable dropshadows
        self.enable_drop_shadow()

        self.initialCheckOk = False

        # thread that updates Google Analytics
        self.AnalyticsThread = GAThread(uaid)
        self.AnalyticsThread.start()

        # thread that updates calendar status
        self.green_lblState_Page2.setText("Varaa Tila
" + str(self.green_dialPage2.value() * 5) + " min")
        self.red_lblState_Page2.setText("{0}\nVaraustiedot".format(self.preferences["name"]))
        self.CalendarPollerThread = CalendarPollerThread(self.preferences, 30)
        self.CalendarPollerThread.statusupdate.connect(self.update_ui)
        self.CalendarPollerThread.start()

        # timer that controls how long we spend time in various of places.
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.start_page1)

    def __del__(self):
        """When exiting, reroute stdout back to sys.__stdout__"""
        sys.stdout = sys.__stdout__

    def setup_uuid(self):
        """Setup UUID for the device. If uuid is already defined, read it from a file"""
        logger.info("Attempting to read device ID")
        file = sys.path[0] + "/uuid"
        if os.path.isfile(file):
            try:
                with open(file) as fileInput:
                    self.uuid = str(fileInput.readline())
                assert uuid.UUID(self.uuid).version == 4
                logger.info("Device ID found {0}".format(self.uuid))
            except Exception:
                logger.error("Failed to read device ID! Check file permissions {0}".format(e))
                sys.exit()
        else:
            logger.info("No device ID detected. Creating a new one")
            try:
                self.uuid = uuid.uuid4()
                with open(file, "w") as fileOutput:
                    fileOutput.write(str(self.uuid))
                logger.info("Device ID {0} assigned to this device.".format(self.uuid))
            except Exception as e:
                logger.error("Failed to save device ID! Check file permissions: {0}".format(e))
                sys.exit()

    """
     QT signal handlers
    """
    def close(self):
        self.close()

    def dial_value_changed(self, value):
        value = value * 5
        self.green_lblState_Page2.setText("Varaa Tila
" + str(value) + " min")
        # reset timer that controls idle timeout
        logger.info("Idle timeout restarted")
        self.timer.start(30000)

    def settings_acccepted(self):
        self.preferences["name"] = self.txtMeetingroom.text()
        self.preferences["email"] = self.txtMeetingroomEmail.text()
        self.preferences["server"] = self.txtServer.text()
        self.preferences["username"] = self.lineUsername.text()
        self.preferences["password"] = self.linePassword.text()
        self.settingsOpened = False
        self.save_settings()
        logger.info("Settings saved!")
        sendToGoogleAnalytics(self.uuid, "Interaction", "Settings", "Settings Accepted", 0)
        self.CalendarPollerThread.refresh_status()
        self.CalendarPollerThread.unpause()

    def settings_rejected(self):
        logger.info("Settings discarded!")
        self.settingsOpened = False
        sendToGoogleAnalytics(self.uuid, "Interaction", "Settings", "Settings Rejected", 0)
        self.CalendarPollerThread.refresh_status()
        self.CalendarPollerThread.unpause()
    """
     Load and save methods for device preferences
    """
    def load_settings(self):
        """Load settings from a predefined file called settings.dat"""
        content = []
        temp_pw = ""
        file = sys.path[0] + "/settings.dat"
        logger.info("Loading settings from file: {0}".format(file))

        try:
            with open(file) as fileInput:
                for line in fileInput:
                    content.append(line)
        except FileNotFoundError:
            logger.error("Failed to load configuration file: {0}".format(traceback.print_exc()))
            return False

        content[4] = content[4].strip()

        # decode password
        for c in content[4]:
            temp_pw += chr(ord(c) - 5)

        self.preferences["name"] = content[0].rstrip('\r\n')
        self.preferences["email"] = content[1].rstrip('\r\n')
        self.preferences["server"] = content[2].rstrip('\r\n')
        self.preferences["username"] = content[3].rstrip('\r\n')
        self.preferences["password"] = temp_pw
        return True

    def save_settings(self):
        """Save settings to file for ex. server address and credentials to access a given resource calendar"""
        content = []
        temp_pw = ""

        # encode password
        for c in self.preferences["password"]:
            temp_pw += chr(ord(c) + 5)

        content.append(self.preferences["name"].strip())
        content.append(self.preferences["email"].strip())
        content.append(self.preferences["server"].strip())
        content.append(self.preferences["username"].strip())
        content.append(temp_pw)

        file = sys.path[0] + "/settings.dat"
        logger.info("Saving settings to file: {0}".format(file))
        with open(file, "w") as fileOutput:
            for row in content:
                fileOutput.write(row + "\n")

    """
     Methods used for creating, cancelling and checking reservations
    """
    def clear_reservation(self):
        """Attempt to clear the current reservation"""
        logger.info("Cancel requested!")
        if clear_reservations(self.preferences):
            sendToGoogleAnalytics(self.uuid, "Interaction", "Cancellation", "OK", 1)
        else:
            sendToGoogleAnalytics(self.uuid, "Interaction", "Cancellation", "NOK", 0)

        self.CalendarPollerThread.refresh_status()

    def make_reservation(self):
        """Attempt to make a reservation"""
        logger.info("Reservation requested!")
        duration = int(self.green_dialPage2.value() * 5)

        if make_a_reservation(self.preferences, duration):
            logger.info("Reservation successful!")
            sendToGoogleAnalytics(self.uuid, "Interaction", "Reservation", "{0} min".format(duration), duration)
        else:
            logger.info("Reservation unsuccessful!")
            sendToGoogleAnalytics(self.uuid, "Interaction", "Reservation", "Failed Reservation", 0)

        self.CalendarPollerThread.refresh_status()

    def decline_reservation(self):
        """User clicked back button to cancel the reservation"""
        logger.info("Reservation cancelled!")
        sendToGoogleAnalytics(self.uuid, "Interaction", "Reservation", "Cancelled Reservation", 0)
        self.start_page1()

    def update_ui(self, result):
        """Handler method that receives input from CalendarPoller thread"""
        if not self.settingsLoaded: # if the device is not configured -> return
            self.green_lblState_Page1.setText("Laite\nKonfiguroimatta")
            self.red_lblState_Page1.setText("Laite\nKonfiguroimatta")
            return

        if result["available"] == "True":
            if result["subject"] == "":
                self.green_lblState_Page1.setText("{0}\nVapaa".format(self.preferences["name"]))
                self.green_lblSubject_Page1.setText("Tila")
            else:
                self.green_lblState_Page1.setText("{0}\nVapaa {1} min".format(self.preferences["name"], int(result["duration"])))
                self.green_lblSubject_Page1.setText(result["subject"])

            self.green_lblNext_Page1.setText("Seuraava varaus")

            if result["start"] == "":
                self.green_lblDuration_Page1.setText("Vapaana")
            else:
                self.green_lblDuration_Page1.setText("{0} - {1}".format(result["start"], result["end"]))

            if int(result["duration"]) == 0 or int(result["duration"]) > 120:
                self.green_dialPage2.setRange(1, 24)
                self.green_dialPage2.setValue(6)
            else:
                numofTicks = self.round_down((int(result["duration"])), 5) / 5
                self.green_dialPage2.setRange(1, numofTicks)
                if numofTicks == 1: # special case: 5 minutes until next meeting
                    self.green_dialPage2.setValue(1)
                elif numofTicks == 2: # special case: 10 minutes until next meeting
                    self.green_dialPage2.setValue(2)
                else:
                    self.green_dialPage2.setValue(3)

            if self.centralstack.currentIndex() != 0:
                self.remove_graphics_effects() # remove fadein effects
                self.centralstack.setCurrentIndex(0) # green background
                self.centralstack.currentWidget().setCurrentIndex(0)
                self.enable_drop_shadow() # and enable dropshadows
                self.CalendarPollerThread.unpause()

            self.initialCheckOk = True
        else:
            if result["subject"] == "Error":
                sendToGoogleAnalytics(self.uuid, "Network", "Status", "Communication Failure", 0)
                self.amountofErrors += 1
                if self.amountofErrors >= self.errorThreshold or self.initialCheckOk is False: # two failures has happened with a communication error.
                    self.red_lblState_Page1.setText("Virhetilanne")
                    self.red_lblSubject_Page1.setText("Virhe")
                    self.red_lblDuration_Page1.setText("Tiedonsiirrossa")
            else:
                self.amountofErrors = 0 # reset error count
                self.red_lblState_Page1.setText("{0}\nVarattu {1} min".format(self.preferences["name"], int(result["duration"])))
                self.red_lblSubject_Page1.setText(result["subject"])
                self.red_lblDuration_Page1.setText("{0} - {1}".format(result["start"], result["end"]))

            if self.centralstack.currentIndex() != 1:
                self.remove_graphics_effects() # remove fadein effects
                self.centralstack.setCurrentIndex(1) # red background
                self.centralstack.currentWidget().setCurrentIndex(0)
                self.enable_drop_shadow() # and enable dropshadows
                self.CalendarPollerThread.unpause()
    """
     Methods used for changing layouts and transitioning from one page to another
    """
    def start_page1(self):
        """UI is based on QStackedWidget. This method changes the "page" to Page 1 of the stack"""
        self.remove_graphics_effects()
        currentLayout = self.centralstack.currentWidget().currentWidget()
        currentStack = self.centralstack.currentWidget()
        self.fade_out(currentLayout).finished.connect(lambda: self.change_layout_anim(currentStack, currentLayout, layout=0))
        self.CalendarPollerThread.unpause()
        logger.info("Idle timeout stopped")
        self.timer.stop()

    def start_page2(self):
        """UI is based on QStackedWidget. This method changes the "page" to Page 2 of the stack"""
        self.remove_graphics_effects()
        currentLayout = self.centralstack.currentWidget().currentWidget()
        currentStack = self.centralstack.currentWidget()
        self.fade_out(currentLayout).finished.connect(lambda: self.change_layout_anim(currentStack, currentLayout, layout=1))
        self.CalendarPollerThread.pause()
        logger.info("Idle timeout started")
        self.timer.start(30000)

    def start_settings_page(self):
        """Change the UI to Settings page"""
        if self.centralstack.currentIndex() == 2: # if settings page is already open, return
            return
        if self.settingsPageClicked <= self.settingsPageThreshold:
            logger.info("Settings page requested! {0}/{1}".format(self.settingsPageClicked, self.settingsPageThreshold))
            self.settingsPageClicked += 1
            return
        else:
            logger.info("Settings page accessible!")
            sendToGoogleAnalytics(self.uuid, "Interaction", "Settings", "Settings Entered", 1)
            self.txtMeetingroom.setText(self.preferences["name"])
            self.txtMeetingroomEmail.setText(self.preferences["email"])
            self.txtServer.setText(self.preferences["server"])
            self.lineUsername.setText(self.preferences["username"])
            self.linePassword.setText(self.preferences["password"])
            self.remove_graphics_effects() # remove fadein effects
            self.centralstack.setCurrentIndex(2) # change to settings page
            self.centralstack.currentWidget().setCurrentIndex(0)
            self.settingsPageClicked = 0
            self.settingsOpened = True
            self.CalendarPollerThread.pause()

    """
     Utility methods used when changing layouts
    """
    def change_layout_anim(self, stack = None, prevlayout = None, layout = 0):
        """When changing the laytout, or page, utilize nice GraphicsEffects to make the transition smoother"""
        if stack == None:
            stack = self.centralstack.currentWidget()
        if prevlayout == None:
            prevLayout = self.centralstack.currentWidget().currentWidget()

        stack.setCurrentIndex(layout)

        self.fade_in(stack.currentWidget()).finished.connect(lambda: self.fade_in(prevlayout).finished.connect(lambda: self.finalize_layout_change(prevlayout)))

    def finalize_layout_change(self, layout):
        self.remove_graphics_effects() # remove fadein effects
        if not self.settingsOpened: # we don't need graphics effect in settings.
            self.enable_drop_shadow() # and enable dropshadows

    """
     Graphics effect utility functions used for fading in / out widgets
     and adding dropshadows to widgets.
    """
    def fade_in(self, widget):
        """Adds a Fade In effect to a given widget"""
        eff = QtWidgets.QGraphicsOpacityEffect()
        widget.setGraphicsEffect(eff)
        self.anim = QtCore.QPropertyAnimation(eff, b"opacity")
        self.anim.setDuration(150)
        self.anim.setStartValue(0)
        self.anim.setEndValue(1)
        self.anim.setEasingCurve(QtCore.QEasingCurve.InOutSine)
        self.anim.start()
        return self.anim

    def fade_out(self, widget):
        """Adds a Fade Out effect to a given widget"""
        eff = QtWidgets.QGraphicsOpacityEffect()
        widget.setGraphicsEffect(eff)
        self.anim = QtCore.QPropertyAnimation(eff, b"opacity")
        self.anim.setDuration(150)
        self.anim.setStartValue(1)
        self.anim.setEndValue(0)
        self.anim.setEasingCurve(QtCore.QEasingCurve.InOutSine)
        self.anim.start()
        return self.anim

    def enable_drop_shadow(self):
        """Enables dropshadows to every suitable QWidget"""
        for widget in self.centralwidget.findChildren(QtWidgets.QWidget):
            effect = QtWidgets.QGraphicsDropShadowEffect();
            effect.setBlurRadius(0);
            effect.setColor(QtGui.QColor(0, 0, 0, 64))
            effect.setOffset(1.5,1.5);
            widget.setGraphicsEffect(effect);

    def remove_graphics_effects(self):
        """Before changing layouts it is necessary to remove any GraphicsEffect that maybe in effect"""
        for widget in self.centralwidget.findChildren(QtWidgets.QWidget):
            widget.setGraphicsEffect(None)

    """
     Other utility methods
    """
    def round_down(self, num, divisor):
        """Utility method used when making reservations. Rounds down a given number to the nearest divisor"""
        return num - (num % divisor)

    def round_up(self, num, divisor):
        """Utility method used when making reservations. Rounds up a given number to the nearest divisor"""
        return num + (divisor % (num % divisor))

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    QtGui.QFontDatabase.addApplicationFont(":fonts/BebasNeue Bold.ttf")
    QtGui.QFontDatabase.addApplicationFont(":fonts/BebasNeue Regular.ttf")
    myWindow = Naurunappula(None, None)

    try:
        if os.uname()[4][:3] == 'arm':
            myWindow.showFullScreen() #if we are running on rpi, run the program fullscreen
    except AttributeError: #attribute error is raised if we try to do this in windows
        pass

    myWindow.show()
    app.exec_()


########################################################################################
#                            DSP1 - Programming Assigment
# By:
#   Yoav Ellinson         206036949
#   Chen Yaakov Sror      203531645
########################################################################################

import numpy as np
import matplotlib.pyplot as plt
# from matlabcall_test import dist_image_1, dist_image_2, noised_image, imp_resp_image
# after using the matlab engine we diabled it to save time - all the images are saved as npz files
from utils import *

d_1 = 0.206036949
d_2 = 0.203531645
d = (d_1 + d_2) % 0.5

dist_image_1 = np.load('dist_image_1_yoav.npz')['arr_0']
dist_image_2 = np.load('dist_image_2_yoav.npz')['arr_0']
imp_resp_image =np.load('imp_resp_image_yoav.npz')['arr_0']
noised_image =np.load('noised_image_yoav.npz')['arr_0']

print("If value is True - we will do Decimation in time: ", d % 0.1 < 0.05) # True

########################################################################################
#           Introduction Part - Implementation of FFT and IFFT Algorithm
########################################################################################

def FFT(x, flag = True):
    if flag:
    # Check if the input serie is not a power of 2, and pad it to a power of 2 accordingly
        if not ispowerof2(len(x)):
            zeros_to_pad = getnextpow2(len(x)) - len(x)
            x = np.pad(x, (0, zeros_to_pad), "constant")
            flag = False

    N = len(x)
    if N == 1:
        return x
    else:
        X_even = FFT(x[::2], flag)
        X_odd = FFT(x[1::2], flag)
        factor = np.exp(-2j*np.pi*np.arange(N)/ N)
        X = np.concatenate([X_even+factor[:int(N/2)]*X_odd, X_even+factor[int(N/2):]*X_odd])
        return X

def IFFT(x):
    N = len(x)
    return (1/N)*FFT(x.conj()).conj()

########################################################################################
#           Part 1 - Image proccessing using 2-D DTDT
########################################################################################

def cyclic_convolution(x,h):
    total_length = max(len(x), len(h))
    x = np.pad(x, (0, total_length - len(x)), 'constant')
    h = np.pad(h, (0, total_length - len(h)), 'constant')
    X = FFT(x)
    H = FFT(h)
    Y = IFFT(X * H)
    return Y

def restore_signal(x,h):
    total_length = max(len(x), len(h))
    x = np.pad(x, (0, total_length - len(x)), 'constant')
    h = np.pad(h, (0, total_length - len(h)), 'constant')
    X = FFT(x)
    H = FFT(h)
    result = np.divide(X, H, out=np.zeros_like(X), where=H != 0)
    Y = IFFT(result)
    return Y

def restore_signal_2D(x,h):
    total_length_row = max(x.shape[0], h.shape[0])
    total_length_col = max(x.shape[1], h.shape[1])
    x = np.pad(x, ((0, total_length_row - x.shape[0]), (0, total_length_col - x.shape[1])), "constant")
    h = np.pad(h, ((0, total_length_row - h.shape[0]), (0, total_length_col - h.shape[1])), "constant")
    X = FFT_2D(x)
    H = FFT_2D(h)
    result = np.divide(X, H, out=np.zeros_like(X), where=H != 0)
    Y = IFFT_2D(result)
    return Y

########################################################################################
#           Part 1 - Image analysis and processing using two-dimensional DTFT.
########################################################################################

B1 = 7
B2 = 7
M = 64
N = 32
x = np.zeros((N,M))

for i in range(N):
    for j in range(M):
        if (0 <= i and i < B1) and (0 <= j and j < B2):
            x[i][j] = 1
        else:
            x[i][j] = 0

# Part 1 d:
def FFT_2D(x):
    N, M = x.shape[0], x.shape[1]
    N, M = getnextpow2(N), getnextpow2(M)
    mat = np.zeros((N, M), dtype=np.complex128)
    for i in range(x.shape[1]):
        mat[:, i] = FFT(x[:, i])
    for i in range(x.shape[0]):
        mat[i, :] = FFT(mat[i, :])
    return mat

def IFFT_2D(x):
    N, M = x.shape[0], x.shape[1]
    return ( 1 / (N*M) ) * FFT_2D(x.conj()).conj()

mat_x = FFT_2D(x)
magnitude_spectrum = np.abs(mat_x)
plt.imshow(magnitude_spectrum, cmap='turbo')
plt.colorbar()
plt.title('2D DFT Magnitude Spectrum')
plt.savefig('pictures/q1_D_2d_dft.png')
plt.close()

fig, axs = plt.subplots(2, 2)
plt.subplots_adjust(wspace=0.2, hspace=0.4)
axs[0, 0].imshow(dist_image_1)
axs[0, 0].set_title('Dist imgge 1', verticalalignment='top')
axs[0, 1].imshow(dist_image_2)
axs[0, 1].set_title('Dist imgge 2', verticalalignment='top')
axs[1, 0].imshow(noised_image)
axs[1, 0].set_title('Noised image', verticalalignment='top')
axs[1, 1].imshow(imp_resp_image)
axs[1, 1].set_title('imp resp image', verticalalignment='top')
plt.savefig('pictures/q1_D_all_images.png')
plt.close()

# Part 1 e:
h_0 = imp_resp_image[:, 0]
samples_vector = np.array([0, 2*np.pi/6, 2* 2*np.pi/6, 4 * 2*np.pi/6])
samples_vector_transposed = np.transpose(samples_vector)
calc_ans = FFT(h_0) * samples_vector_transposed
calc_ans_abs = abs(calc_ans)

plt.stem(calc_ans_abs, basefmt=' ', use_line_collection=True)
plt.title(r'${H}_{0}(e^{j\omega})$')
plt.ylabel("Amplitude")
plt.xlabel(r'${\omega}$')
plt.savefig('pictures/q1_E_H_0_fft.png')
plt.close()

# Part 1 f:
w = np.zeros(32)
w[0] = 1
w[29] = 1

conv_ans = cyclic_convolution(h_0, w)

plt.stem(conv_ans, basefmt=' ', use_line_collection=True)
plt.title(r'${h}_{0} \circledast w[n]$')
plt.ylabel("Amplitude")
plt.xlabel(r'${\omega}$')
plt.savefig('pictures/q1_F_wn_conv_h0n.png')
plt.close()

# Part 1 g:
x1_restored = restore_signal_2D(dist_image_1, imp_resp_image)
x1_restored_abs = np.abs(x1_restored)
plt.imshow(x1_restored_abs,cmap='gray')
plt.title(r'${x_1}[n, m] = \frac{y[n, m]}{h[n, m]}$')
plt.ylabel('n')
plt.xlabel('m')
plt.savefig('pictures/q1_G_x1.png')
plt.close()

fig, axs1 = plt.subplots(1, 2)
plt.subplots_adjust(wspace=0.2, hspace=0.4)
x2_restored = restore_signal_2D(dist_image_2, imp_resp_image)
x2_restored_abs = np.real(x2_restored)
x2_resized = np.resize(x2_restored_abs,(70,170))
x2_restored_abs = x2_restored_abs[:70, :170]
axs1[0].imshow(x2_restored_abs, cmap = 'gray')
axs1[0].set_title('Our: ' + r'${x_2}[n, m]$')

x2_restored_fft2 = np.fft.ifft2(np.fft.fft2(dist_image_2) / np.fft.fft2(np.pad(imp_resp_image, ((0,70-3), (0, 170-5)), 'constant')))
x2_restored_fft2_abs = np.abs(x2_restored_fft2)
x2_restored_fft2_abs = x2_restored_fft2_abs[:70, :170]
axs1[1].imshow(x2_restored_fft2_abs, cmap = 'gray')
axs1[1].set_title('np.fft.fft2(): '+ r'${x_2}[n, m]$')

plt.savefig('pictures/q1_G_x2.png')
plt.close()

########################################################################################
#           Part 2 - Speech analysis
########################################################################################
import soundfile as sf

def create_w_linspace(N):
    return np.concatenate((np.linspace(0,2*np.pi,N)[:int(N/2)+1],np.linspace(-2*np.pi,0,N)[int(N/2)+1:]))

def anti_aliasing(x,dec_factor):
    X = FFT(x)
    a_filter = np.ones_like(x)
    w_m = create_w_linspace(len(x))
    a_filter[np.where(abs(w_m)>np.pi/dec_factor)]=0
    x_filtered =IFFT(X*a_filter).real
    return x_filtered

# Part2 a:
wav,sr = sf.read('wavs/out.wav')
N =2**16
x_n = np.array(wav[:N])
Px = (1/N) * np.sum(x_n**2)

# Define z[n]
w1=1.6+0.1*d_1
w2=1.6+0.1*d
w3=3
n = np.arange(N)
z_n = 50*np.sqrt(Px)*(np.cos(w1*n)+np.cos(w2*n)+np.cos(w3*n))

# Part2 a:
# Define y[n] = x[n] + z[n]
y_n = x_n + z_n
sf.write('wavs/x_n.wav',x_n,16000)
sf.write('wavs/y_n.wav',y_n,16000)

# Plotting Y[n]
fig = plt.figure()
fig.suptitle(r'$y[n] = x[n] + z[n]$')
plt.plot(y_n)
plt.ylabel("Amplitude")
plt.xlabel(r'time')
plt.savefig('pictures/q2_y_n.png')
plt.close()

# Part2 d:
# Plotting Y[e^(jw)]
Y_k = FFT(y_n)
w_m = create_w_linspace(128)
fig = plt.figure()
fig.suptitle(r'$Y(e^{j\omega})$ k= [0,128]')
plt.stem(w_m,abs(Y_k)[::512])
plt.ylabel("Amplitude")
plt.xlabel(r'${\omega}$')
plt.savefig('pictures/q2_Y_ejw.png')
plt.close()

# Plotting the noise - extra:
Z_k = FFT(z_n)
w_m = create_w_linspace(len(Z_k))
fig = plt.figure()
fig.suptitle(r'$Z(e^{j\omega}) k=2^{16}$')
plt.stem(w_m,abs(Z_k))
plt.ylabel("Amplitude")
plt.xlabel(r'${\omega}$')
plt.savefig('pictures/q2_Z_ejw.png')
plt.close()

# Part2 e and f:
y_n_2 = anti_aliasing(y_n,dec_factor =2)[::2]
sf.write('wavs/y_2.wav',y_n_2,8000)
z_n_2 = anti_aliasing(z_n,2)[::2]
Y_2_k = FFT(y_n_2)
Z_2_k =FFT(z_n_2)
fig = plt.figure()
fig.suptitle(r'$y_{2}[n]$')
plt.plot(y_n_2)
plt.ylabel("Amplitude")
plt.xlabel(r'time')
plt.savefig('pictures/q2_F_y2.png')
plt.close()

w_m = create_w_linspace(128)
fig = plt.figure()
fig.suptitle(r'$Y_{2}(e^{j\omega})$ k = [0,128]')
plt.stem(w_m,abs(Y_2_k[::256]))
plt.ylabel("Amplitude")
plt.xlabel(r'${\omega}$')
plt.savefig('pictures/q2_F_Y_2_ejw.png')
plt.close()

fig = plt.figure()
fig.suptitle(r'$Z_{2}(e^{j\omega})$ k = [0,128]')
plt.stem(w_m,abs(Z_2_k[::256]))
plt.ylabel("Amplitude")
plt.xlabel(r'${\omega}$')
plt.savefig('pictures/q2_F_Z_2_ejw.png')
plt.close()


#!/usr/bin/env python3
from http.server import BaseHTTPRequestHandler, HTTPServer
import cgi

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from database_setup import Base, Restaurant, MenuItem

# connect to DB and create a session
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.bind = engine
Session = sessionmaker(bind=engine)
session = Session()

class webserverHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        try:
            if self.path.endswith('/restaurants'):
                restaurants = session.query(Restaurant).all()
                self.send_response(200)
                self.send_header('Content-type', 'text/html')
                self.end_headers()
                list = '
'
                for r in restaurants:
                    # if type(r.name)!='str':
                    #     session.delete(r)
                    #     session.commit()
                    list += '
' + r.name + '
'
                    list += "Edit
".format(r.id)
                    list += "Delete

".format(r.id)
                list += '
'
                output = "
Make a New Restaurant Here !
"
                output += list
                output += ''
                self.wfile.write(output.encode('utf-8'))
                return
            if self.path.endswith('/restaurants/new'):
                self.send_response(200)
                self.send_header('Content-type', 'text/html')
                self.end_headers()
                output = ""
                output += ""
                output += "
Make a New Restaurant
"
                output += """
                             <form method='POST' action='/restaurants/new'' enctype='multipart/form-data'>
                                <input name='newRestaurantName' type='text'>
                                <input type='submit' value='Create'>
                             </form>
                """
                output += ""
                self.wfile.write(output.encode('utf-8'))
                return
            if self.path.endswith('/edit'):
                id = self.path.split('/')[2]
                restaurant = session.query(Restaurant).filter_by(id=id).one()
                self.send_response(200)
                self.send_header('Content-type', 'text/html')
                self.end_headers()
                output = ""
                output += ""
                output += "
" + restaurant.name + "
"
                output += """
                             <form method='POST' action='/restaurants/{}/edit'' enctype='multipart/form-data'>
                                <input name='restaurantName' value='{}' type='text'>
                                <input type='submit' value='Update'>
                             </form>
                """.format(id, restaurant.name)
                output += ""
                self.wfile.write(output.encode('utf-8'))
                return
            if self.path.endswith('/delete'):
                id = self.path.split('/')[2]
                restaurant = session.query(Restaurant).filter_by(id=id).one()
                self.send_response(200)
                self.send_header('Content-type', 'text/html')
                self.end_headers()
                output = ""
                output += ""
                output += "
Are you sure you want to delete {} ?
".format(restaurant.name)
                output += """
                             <form method='POST' action='/restaurants/{}/delete'' enctype='multipart/form-data'>
                                <input type='submit' value='Delete'>
                             </form>
                """.format(id)
                output += ""
                self.wfile.write(output.encode('utf-8'))
                return
        except IOError:
            self.send_error(404, 'File not found {}'.format(self.path))

    def do_POST(self):
        # try:
        if self.path.endswith('/restaurants/new'):
            ctype, pdict = cgi.parse_header(self.headers.get('content-type', ''))
            pdict['boundary'] = pdict['boundary'].encode()
            if ctype=='multipart/form-data':
                fields = cgi.parse_multipart(self.rfile, pdict) # DEALS with bytes
                messagecontent = fields.get('newRestaurantName')[0].decode()
            new_restaurant = Restaurant(name=messagecontent)
            session.add(new_restaurant)
            session.commit()
            self.send_response(301)
            self.send_header('location', '/restaurants')
            self.end_headers()
            return
        if self.path.endswith('/edit'):
            ctype, pdict = cgi.parse_header(self.headers.get('content-type', ''))
            pdict['boundary'] = pdict['boundary'].encode()
            if ctype=='multipart/form-data':
                fields = cgi.parse_multipart(self.rfile, pdict) # DEALS with bytes
                messagecontent = fields.get('restaurantName')[0].decode()
            id = self.path.split('/')[2]
            restaurant = session.query(Restaurant).filter_by(id=id).one()
            restaurant.name = messagecontent
            session.add(restaurant)
            session.commit()
            self.send_response(301)
            self.send_header('location', '/restaurants')
            self.end_headers()
            return
        if self.path.endswith('/delete'):
            id = self.path.split('/')[2]
            restaurant = session.query(Restaurant).filter_by(id=id).one()
            session.delete(restaurant)
            session.commit()
            self.send_response(301)
            self.send_header('location', '/restaurants')
            self.end_headers()
            return
        # except:
            # pass

def main():
    try:
        port = 8080
        server = HTTPServer(('', port), webserverHandler)
        print("The server is running on port {}".format(port))
        server.serve_forever()
    except KeyboardInterrupt:
        print('^C entered, stopping the web server ...')
        server.socket.close()

if __name__ == '__main__':
    main()


import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import plotly.figure_factory as ff

def main():
    st.title("Laadpaal Data")
    st.header("Importeren, opschonen en filteren van data")
    paalData = pd.read_csv('laadpaaldata.csv')
    paalData.head()
    st.code("paalData = pd.read_csv('laadpaaldata.csv')"
    "\npaalData.head()")
    fig = px.scatter(x=paalData['ChargeTime'],y=paalData['ConnectedTime'],labels={'x':'Laadtijd','y':'Aansluit tijd'})
    st.plotly_chart(fig)
    st.caption("Er is hier te zien dat er duidelijke outliers zijn in de laadtijd, deze worden gefilterd door middel van:")
    st.code("paalData = paalData[paalData['ChargeTime'] > 0]"
            "\npaalData = paalData[paalData['ChargeTime'] < 7]")
    paalData = paalData[paalData['ChargeTime'] > 0]
    paalData = paalData[paalData['ChargeTime'] < 7]

    st.caption("We houden nu het volgende DataFrame over:")
    st.dataframe(paalData)

    with st.echo():
        st.write(type(paalData.loc[0]['Started']))

    st.caption("Het type data in de Started en Ended kolommen zijn nu nog String. Dit is te fixen door pd.to_datetime te gebruiken:")
    st.code("paalData = paalData.assign(Started = lambda x: pd.to_datetime(x['Started'],errors='coerce'))"
            "\npaalData = paalData.assign(Ended = lambda x: pd.to_datetime(x['Ended'],errors='coerce'))")
    paalData = paalData.assign(Started=lambda x: pd.to_datetime(x['Started'], errors='coerce'))
    paalData = paalData.assign(Ended=lambda x: pd.to_datetime(x['Ended'], errors='coerce'))
    paalData = paalData[paalData['Started'] >= "2017-01-01 08:45:26"]
    paalData = paalData[paalData['Ended'] >= "2017-01-01 08:45:26"]

    st.caption("Nu kan deze functie gebruikt worden om te checken of er geen ")
    with st.echo():
        st.write(paalData.isna().sum())

    winter = paalData[paalData['Started'] <= "2018-03-21 23:59:59"]
    lente = paalData[paalData['Started'] <= "2018-06-21 23:59:59"]
    lente = lente[lente['Started'] > "2018-03-21 23:59:59"]
    zomer = paalData[paalData['Started'] > "2018-06-21 23:59:59"]
    zomer = zomer[zomer['Started'] <= "2018-09-21 23:59:59"]
    herfst = paalData[paalData['Started'] > "2018-09-21 23:59:59"]
    herfst = herfst[herfst['Started'] <= "2018-12-21 23:59:59"]

    st.caption("Nu de kolommen de juiste datatypes hebben, kan het dataframe per seizoen gefilterd worden:")
    st.code("winter = paalData[paalData['Started'] <= '2018-03-21 23:59:59']"
            "\n"
            "\nlente = paalData[paalData['Started'] <= '2018-06-21 23:59:59']"
            "\nlente = lente[lente['Started'] > '2018-03-21 23:59:59']")

    st.header("Laadtijd plots")

    st.caption('Als eerste is een histogram gemaakt met de verdeling van laadtijden in 2018, die te filteren is op seizoen. Het gemiddelde en de mediaan zijn weergegeven met pijlen in elke plot.')

    fig = go.Figure()

    # Add traces of charge time for the different seasons
    fig.add_trace(go.Histogram(x=paalData['ChargeTime'], name='2018 totaal', nbinsx=10, ))
    fig.add_trace(go.Histogram(x=winter['ChargeTime'], name='winter', nbinsx=10))
    fig.add_trace(go.Histogram(x=lente['ChargeTime'], name='lente', nbinsx=10))
    fig.add_trace(go.Histogram(x=zomer['ChargeTime'], name='zomer', nbinsx=10))
    fig.add_trace(go.Histogram(x=herfst['ChargeTime'], name='herfst', nbinsx=10))

    # Add annotation for the mean
    laadMean = str(paalData['ChargeTime'].mean())[:3]
    laadMedian = str(paalData['ChargeTime'].median())[:3]
    annotationMean = {'x': paalData['ChargeTime'].mean(),
                      'y': 400,
                      'showarrow': True,
                      'arrowhead': 4,
                      'font': {'size': 15, 'color': 'red'},
                      'text': 'Gemiddelde laadtijd = ' + laadMean + " uur"}
    annotationMedian = {'x': paalData['ChargeTime'].median(),
                        'y': 800,
                        'showarrow': True,
                        'arrowhead': 2,
                        'font': {'size': 15, 'color': 'green'},
                        'text': 'Mediaan van laadtijd = ' + laadMedian + " uur"}
    fig.update_layout({'annotations': [annotationMean, annotationMedian]})

    my_legend = {'x': 1, 'y': 0, 'bgcolor': 'rgb(0, 0, 0)', 'borderwidth': 5}
    fig.update_layout({'showlegend': True, 'legend': my_legend},
                      title="Verdeling van laadtijden in 2018",
                      xaxis_title_text='Laad tijd (uur)',
                      yaxis_title_text='Aantal waarnemingen', )

    sliders = [
        {'steps': [
            {'method': 'update', 'label': '2018 totaal', 'args': [{'visible': [True, False, False, False, False]}]},
            {'method': 'update', 'label': 'winter', 'args': [{'visible': [False, True, False, False, False]}]},
            {'method': 'update', 'label': 'lente', 'args': [{'visible': [False, False, True, False, False]}]},
            {'method': 'update', 'label': 'zomer', 'args': [{'visible': [False, False, False, True, False]}]},
            {'method': 'update', 'label': 'herfst', 'args': [{'visible': [False, False, False, False, True]}]}]}]

    fig.update_layout({'sliders': sliders})

    st.plotly_chart(fig)
    st.caption("Zoals te zien is is de gemiddelde laadtijd 2.3 uur, en is de mediaan 2.2 uur. Ook is te zien dat de laadtijden niet veranderen per seizoen.")
    st.caption("")
    st.caption("De volgende plot geeft de kansdichtheidsfunctie weer van de laadtijden in 2018. Er is een piek te zien bij het gemiddelde van 2.3 uur.")
    # Add histogram data
    x1 = paalData['ChargeTime']

    # Group data together
    hist_data = [x1]

    group_labels = ["2018 laadtijd kansdichtheidsfunctie"]

    # Create distplot with custom bin_size
    fig = ff.create_distplot(hist_data, group_labels, bin_size=.2)
    st.plotly_chart(fig)


import sys
# sys.path.append("..")#这一句
from IPython import display
from matplotlib import pyplot as plt
import random
import torch
from torch import nn
import torchvision
import torchvision.transforms as transforms
import time
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
import random
import zipfile
import numpy as np
import math
from collections import namedtuple

# dim=0，表示的是行，函数的意思就是不要行这一维度，保留列
#。data = 通过对修改参数的data来来写模型参数同时不会影响梯度

def use_svg_display():
    # 用矢量图显示
    display.set_matplotlib_formats('svg')

def set_figsize(figsize=(5, 4)):
    use_svg_display()
    # 设置图的尺寸
    plt.rcParams['figure.figsize'] = figsize

def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  # 样本的读取顺序是随机的
    for i in range(0, num_examples, batch_size):
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # 最后一次可能不足一个batch
        yield  features.index_select(0, j), labels.index_select(0, j)

def linreg(X, w, b):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    return torch.mm(X, w) + b

def squared_loss(y_hat, y):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    # 注意这里返回的是向量, 另外, pytorch里的MSELoss并没有除以 2
    return (y_hat - y.view(y_hat.size())) ** 2 / 2

def sgd(params, lr, batch_size):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    for param in params:
        param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data

def get_fashion_mnist_labels(labels):
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]

def show_fashion_mnist(images, labels):
    use_svg_display()
    # 这里的_表示我们忽略（不使用）的变量
    _, figs = plt.subplots(1, len(images), figsize=(12, 12))
    # print(figs)
    for f, img, lbl in zip(figs, images, labels):
        # print("f",f)
        # print("img",img)
        # print("lbl",lbl)
        f.imshow(img.view((28, 28)).numpy())
        f.set_title(lbl)
        f.axes.get_xaxis().set_visible(False)
        f.axes.get_yaxis().set_visible(False)
    plt.show()

# def load_data_fashion_mnist(batch_size):
#     mnist_train = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=True, download=True,
#                                                     transform=transforms.ToTensor())
#     mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=False, download=True,
#                                                    transform=transforms.ToTensor())
#     if sys.platform.startswith('win'):
#         num_workers = 0  # 0表示不用额外的进程来加速读取数据
#     else:
#         num_workers = 4
#     train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
#     test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
#     return train_iter,test_iter

# softmax运算
def softmax(X):
    X_exp = X.exp()
    partition = X_exp.sum(dim=1, keepdim=True)
    return X_exp / partition  # 这里应用了广播机制

# 定义模型
# def net(X):
#     return softmax(torch.mm(X.view((-1, num_inputs)), W) + b)

# 定义损失函数
def cross_entropy(y_hat, y):
    return - torch.log(y_hat.gather(1, y.view(-1, 1)))

# 计算分类准去率
def accuracy(y_hat, y):
    return (y_hat.argmax(dim=1) == y).float().mean().item()

# 评价模型net在数据集data_iter上的准确率
def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0
    for X, y in data_iter:
        if isinstance(net, torch.nn.Module):
            net.eval()  # 评估模式, 这会关闭dropout
            acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
            net.train()  # 改回训练模式
        else:  # 自定义的模型
            if ('is_training' in net.__code__.co_varnames):  # 如果有is_training这个参数
                # 将is_training设置成False
                acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
            else:
                acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
        n += y.shape[0]
    return acc_sum / n

# 训练模型
def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,
              params=None, lr=None, optimizer=None):
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
        for X, y in train_iter:
            y_hat = net(X)
            l = loss(y_hat, y).sum()

            # 梯度清零
            if optimizer is not None:
                optimizer.zero_grad()
            elif params is not None and params[0].grad is not None:
                for param in params:
                    param.grad.data.zero_()

            l.backward()
            if optimizer is None:
                sgd(params, lr, batch_size)
            else:
                optimizer.step()  # “softmax回归的简洁实现”一节将用到，更新参数

            train_l_sum += l.item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
        test_acc = evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
              % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))

#作图函数，其中y轴使用了对数尺度
def semilogy(x_vals, y_vals, x_label, y_label, x2_vals=None, y2_vals=None,
             legend=None, figsize=(5, 5)):
    set_figsize(figsize)
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.semilogy(x_vals, y_vals)
    if x2_vals and y2_vals:
        plt.semilogy(x2_vals, y2_vals, linestyle=':')
        plt.legend(legend)

def corr2d(X, K):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    h, w = K.shape
    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            Y[i, j] = (X[i: i + h, j: j + w] * K).sum()
    return Y

def evaluate_accuracy(data_iter, net, device=None):
    if device is None and isinstance(net, torch.nn.Module):
        # 如果没指定device就使用net的device
        device = list(net.parameters())[0].device #先把net.parameters转成list型
    acc_sum, n = 0.0, 0
    with torch.no_grad():
        for X, y in data_iter:
            if isinstance(net, torch.nn.Module):
                net.eval() # 评估模式, 这会关闭dropout
                acc_sum += (net(X.to(device)).argmax(dim=1) == y.to(device)).float().sum().cpu().item()
                net.train() # 改回训练模式
            else: # 自定义的模型, 3.13节之后不会用到, 不考虑GPU
                if('is_training' in net.__code__.co_varnames): # 如果有is_training这个参数
                    # 将is_training设置成False
                    acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
                else:
                    acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
            n += y.shape[0]
    return acc_sum / n

def train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs):
    net = net.to(device)
    print("training on ", device)
    # print(train_iter.device)
    loss = torch.nn.CrossEntropyLoss()
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n, batch_count, start = 0.0, 0.0, 0, 0, time.time()
        for X, y in train_iter:
            X = X.to(device)
            y = y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            train_l_sum += l.cpu().item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
            n += y.shape[0]
            batch_count += 1
        test_acc = evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec'
              % (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))

def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):
    #我们在ToTensor实例前使用Resize实例,将图像扩大到224*224
    """Download the fashion mnist dataset and then load into memory."""
    trans = []
    if resize:
        trans.append(torchvision.transforms.Resize(size=resize))
    trans.append(torchvision.transforms.ToTensor())

    transform = torchvision.transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)
    mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)
    if sys.platform.startswith('win'):
        num_workers = 0  # 0表示不用额外的进程来加速读取数据
    else:
        num_workers = 4
    train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
    test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)

    return train_iter, test_iter

import torch.nn.functional as F
class GlobalAvgPool2d(nn.Module):
    # 全局平均池化层可通过将池化窗口形状设置成输入的高和宽实现
    def __init__(self):
        super(GlobalAvgPool2d, self).__init__()
    def forward(self, x):
        return F.avg_pool2d(x, kernel_size=x.size()[2:])

    # 残差块
class Residual(nn.Module):  # 本类已保存在d2lzh_pytorch包中方便以后使用
    def __init__(self, in_channels, out_channels, use_1x1conv=False, stride=1):
        super(Residual, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, stride=stride)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)
        if use_1x1conv:
            self.conv3 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride)
        else:
            self.conv3 = None
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.bn2 = nn.BatchNorm2d(out_channels)

    def forward(self, X):
        Y = F.relu(self.bn1(self.conv1(X)))
        Y = self.bn2(self.conv2(Y))
        if self.conv3:
            X = self.conv3(X)
        return F.relu(Y + X)

def load_data_jay_lyrics():
    with zipfile.ZipFile('jaychou_lyrics.txt.zip') as zin:
        with zin.open('jaychou_lyrics.txt') as f:
            corpus_chars = f.read().decode('utf-8')
    corpus_chars = corpus_chars.replace('\n', ' ').replace('\r', ' ')
    corpus_chars = corpus_chars[0:10000]
    idx_to_char = list(set(corpus_chars))
    char_to_idx = dict([(char, i) for i, char in enumerate(idx_to_char)])
    vocab_size = len(char_to_idx)
    corpus_indices = [char_to_idx[char] for char in corpus_chars]
    return corpus_indices,char_to_idx,idx_to_char,vocab_size

def train_2d(trainer):  # 本函数将保存在d2lzh_pytorch包中方便以后使用
    x1, x2, s1, s2 = -5, -2, 0, 0  # s1和s2是自变量状态，本章后续几节会使用
    results = [(x1, x2)]
    for i in range(20):
        x1, x2, s1, s2 = trainer(x1, x2, s1, s2)
        results.append((x1, x2))
    print('epoch %d, x1 %f, x2 %f' % (i + 1, x1, x2))
    return results

def show_trace_2d(f, results):  # 本函数将保存在d2lzh_pytorch包中方便以后使用
    d2l.plt.plot(*zip(*results), '-o', color='#ff7f0e')
    x1, x2 = np.meshgrid(np.arange(-5.5, 1.0, 0.1), np.arange(-3.0, 1.0, 0.1))
    d2l.plt.contour(x1, x2, f(x1, x2), colors='#1f77b4')
    d2l.plt.xlabel('x1')
    d2l.plt.ylabel('x2')
    d2l.plt.show()

def get_data_ch7():  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    data = np.genfromtxt('../airfoil_self_noise.dat', delimiter='\t')
    data = (data - data.mean(axis=0)) / data.std(axis=0)
    return torch.tensor(data[:1500, :-1], dtype=torch.float32), \
    torch.tensor(data[:1500, -1], dtype=torch.float32) # 前1500个样本(每个样本5个特征)

def train_pytorch_ch7(optimizer_fn, optimizer_hyperparams, features, labels,
                    batch_size=10, num_epochs=2):
    # 初始化模型
    net = nn.Sequential(
        nn.Linear(features.shape[-1], 1)
    )
    loss = nn.MSELoss()
    optimizer = optimizer_fn(net.parameters(), **optimizer_hyperparams)

    def eval_loss():
        return loss(net(features).view(-1), labels).item() / 2

    ls = [eval_loss()]
    data_iter = torch.utils.data.DataLoader(
        torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)

    for _ in range(num_epochs):
        start = time.time()
        for batch_i, (X, y) in enumerate(data_iter):
            # 除以2是为了和train_ch7保持一致, 因为squared_loss中除了2
            l = loss(net(X).view(-1), y) / 2

            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            if (batch_i + 1) * batch_size % 100 == 0:
                ls.append(eval_loss())
    # 打印结果和作图
    print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
    d2l.set_figsize()
    d2l.plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
    d2l.plt.xlabel('epoch')
    d2l.plt.ylabel('loss')
    d2l.plt.show()

def show_images(imgs, num_rows, num_cols, scale=2):
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    for i in range(num_rows):
        for j in range(num_cols):
            axes[i][j].imshow(imgs[i * num_cols + j])
            axes[i][j].axes.get_xaxis().set_visible(False)
            axes[i][j].axes.get_yaxis().set_visible(False)
    return axes

def train(train_iter, test_iter, net, loss, optimizer, device, num_epochs):
    net = net.to(device)
    print("training on ", device)
    batch_count = 0
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()
        for X, y in train_iter:
            X = X.to(device)
            y = y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            train_l_sum += l.cpu().item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
            n += y.shape[0]
            batch_count += 1
        test_acc = d2l.evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec'
              % (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))

def resnet_block(in_channels, out_channels, num_residuals, first_block=False):
    if first_block:
        assert in_channels == out_channels # 第一个模块的通道数同输入通道数一致
    blk = []
    for i in range(num_residuals):
        if i == 0 and not first_block:
            blk.append(Residual(in_channels, out_channels, use_1x1conv=True, stride=2))
        else:
            blk.append(Residual(out_channels, out_channels))
    return nn.Sequential(*blk)

def resnet18():
    net = nn.Sequential(
        nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3),
        nn.BatchNorm2d(64),
        nn.ReLU(),
        nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
    net.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True))
    net.add_module("resnet_block2", resnet_block(64, 128, 2))
    net.add_module("resnet_block3", resnet_block(128, 256, 2))
    net.add_module("resnet_block4", resnet_block(256, 512, 2))
    net.add_module("global_avg_pool", d2l.GlobalAvgPool2d())  # GlobalAvgPool2d的输出: (Batch, 512, 1, 1)
    net.add_module("fc", nn.Sequential(nn.Flatten(), nn.Linear(512, 10)))
    return net

def bbox_to_rect(bbox, color):  # 本函数已保存在d2lzh_pytorch中方便以后使用
    # 将边界框(左上x, 左上y, 右下x, 右下y)格式转换成matplotlib格式：
    # ((左上x, 左上y), 宽, 高)
    return d2l.plt.Rectangle(#矩形函数
        xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1],
        fill=False, edgecolor=color, linewidth=2)

def MultiBoxPrior(feature_map, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5]):
    """
    # 按照「9.4.1. 生成多个锚框」所讲的实现, anchor表示成(xmin, ymin, xmax, ymax).
    https://zh.d2l.ai/chapter_computer-vision/anchor.html
    Args:
        feature_map: torch tensor, Shape: [N, C, H, W].
        sizes: List of sizes (0~1) of generated MultiBoxPriores.
        ratios: List of aspect ratios (non-negative) of generated MultiBoxPriores.
    Returns:
        anchors of shape (1, num_anchors, 4). 由于batch里每个都一样, 所以第一维为1
    """
    pairs = [] # pair of (size, sqrt(ration))
    for r in ratios:
        pairs.append([sizes[0], math.sqrt(r)])
    for s in sizes[1:]:
        pairs.append([s, math.sqrt(ratios[0])])

    pairs = np.array(pairs)

    ss1 = pairs[:, 0] * pairs[:, 1] # size * sqrt(ration)
    ss2 = pairs[:, 0] / pairs[:, 1] # size / sqrt(ration)

    base_anchors = np.stack([-ss1, -ss2, ss1, ss2], axis=1) / 2

    h, w = feature_map.shape[-2:]
    shifts_x = np.arange(0, w) / w
    shifts_y = np.arange(0, h) / h
    shift_x, shift_y = np.meshgrid(shifts_x, shifts_y)
    shift_x = shift_x.reshape(-1)
    shift_y = shift_y.reshape(-1)
    shifts = np.stack((shift_x, shift_y, shift_x, shift_y), axis=1)

    anchors = shifts.reshape((-1, 1, 4)) + base_anchors.reshape((1, -1, 4))

    return torch.tensor(anchors, dtype=torch.float32).view(1, -1, 4)

def show_bboxes(axes, bboxes, labels=None, colors=None):
    def _make_list(obj, default_values=None):
        if obj is None:
            obj = default_values
        elif not isinstance(obj, (list, tuple)):
            obj = [obj]
        return obj

    labels = _make_list(labels)
    colors = _make_list(colors, ['b', 'g', 'r', 'm', 'c'])
    for i, bbox in enumerate(bboxes):
        color = colors[i % len(colors)]
        rect = d2l.bbox_to_rect(bbox.detach().cpu().numpy(), color)
        axes.add_patch(rect)
        if labels and len(labels) > i:
            text_color = 'k' if color == 'w' else 'w'
            axes.text(rect.xy[0], rect.xy[1], labels[i],
                      va='center', ha='center', fontsize=6, color=text_color,
                      bbox=dict(facecolor=color, lw=0))

def compute_intersection(set_1, set_2):
    """
    计算anchor之间的交集
    Args:
        set_1: a tensor of dimensions (n1, 4), anchor表示成(xmin, ymin, xmax, ymax)
        set_2: a tensor of dimensions (n2, 4), anchor表示成(xmin, ymin, xmax, ymax)
    Returns:
        intersection of each of the boxes in set 1 with respect to each of the boxes in set 2, shape: (n1, n2)
    """
    # PyTorch auto-broadcasts singleton dimensions
    lower_bounds = torch.max(set_1[:, :2].unsqueeze(1), set_2[:, :2].unsqueeze(0))  # (n1, n2, 2)
    upper_bounds = torch.min(set_1[:, 2:].unsqueeze(1), set_2[:, 2:].unsqueeze(0))  # (n1, n2, 2)
    intersection_dims = torch.clamp(upper_bounds - lower_bounds, min=0)  # (n1, n2, 2)
    return intersection_dims[:, :, 0] * intersection_dims[:, :, 1]  # (n1, n2)

def compute_jaccard(set_1, set_2):
    """
    计算anchor之间的Jaccard系数(IoU)
    Args:
        set_1: a tensor of dimensions (n1, 4), anchor表示成(xmin, ymin, xmax, ymax)
        set_2: a tensor of dimensions (n2, 4), anchor表示成(xmin, ymin, xmax, ymax)
    Returns:
        Jaccard Overlap of each of the boxes in set 1 with respect to each of the boxes in set 2, shape: (n1, n2)
    """
    # Find intersections
    intersection = compute_intersection(set_1, set_2)  # (n1, n2)

    # Find areas of each box in both sets
    areas_set_1 = (set_1[:, 2] - set_1[:, 0]) * (set_1[:, 3] - set_1[:, 1])  # (n1)
    areas_set_2 = (set_2[:, 2] - set_2[:, 0]) * (set_2[:, 3] - set_2[:, 1])  # (n2)

    # Find the union
    # PyTorch auto-broadcasts singleton dimensions
    union = areas_set_1.unsqueeze(1) + areas_set_2.unsqueeze(0) - intersection  # (n1, n2)

    return intersection / union  # (n1, n2)

def assign_anchor(bb, anchor, jaccard_threshold=0.5):
    """
    # 按照「9.4.1. 生成多个锚框」图9.3所讲为每个anchor分配真实的bb, anchor表示成归一化(xmin, ymin, xmax, ymax).
    https://zh.d2l.ai/chapter_computer-vision/anchor.html
    Args:
        bb: 真实边界框(bounding box), shape:（nb, 4）
        anchor: 待分配的anchor, shape:（na, 4）
        jaccard_threshold: 预先设定的阈值
    Returns:
        assigned_idx: shape: (na, ), 每个anchor分配的真实bb对应的索引, 若未分配任何bb则为-1
    """
    na = anchor.shape[0]
    nb = bb.shape[0]
    jaccard = compute_jaccard(anchor, bb).detach().cpu().numpy()  # shape: (na, nb)
    assigned_idx = np.ones(na) * -1  # 初始全为-1

    # 先为每个bb分配一个anchor(不要求满足jaccard_threshold)
    jaccard_cp = jaccard.copy()
    for j in range(nb):
        i = np.argmax(jaccard_cp[:, j])
        assigned_idx[i] = j
        jaccard_cp[i, :] = float("-inf")  # 赋值为负无穷, 相当于去掉这一行

    # 处理还未被分配的anchor, 要求满足jaccard_threshold
    for i in range(na):
        if assigned_idx[i] == -1:
            j = np.argmax(jaccard[i, :])
            if jaccard[i, j] >= jaccard_threshold:
                assigned_idx[i] = j

    return torch.tensor(assigned_idx, dtype=torch.long)

def xy_to_cxcy(xy):
    """
    将(x_min, y_min, x_max, y_max)形式的anchor转换成(center_x, center_y, w, h)形式的.
    https://github.com/sgrvinod/a-PyTorch-Tutorial-to-Object-Detection/blob/master/utils.py
    Args:
        xy: bounding boxes in boundary coordinates, a tensor of size (n_boxes, 4)
    Returns:
        bounding boxes in center-size coordinates, a tensor of size (n_boxes, 4)
    """
    return torch.cat([(xy[:, 2:] + xy[:, :2]) / 2,  # c_x, c_y
                      xy[:, 2:] - xy[:, :2]], 1)  # w, h

def MultiBoxTarget(anchor, label):
    """
    # 按照「9.4.1. 生成多个锚框」所讲的实现, anchor表示成归一化(xmin, ymin, xmax, ymax).
    https://zh.d2l.ai/chapter_computer-vision/anchor.html
    Args:
        anchor: torch tensor, 输入的锚框, 一般是通过MultiBoxPrior生成, shape:（1，锚框总数，4）
        label: 真实标签, shape为(bn, 每张图片最多的真实锚框数, 5)
               第二维中，如果给定图片没有这么多锚框, 可以先用-1填充空白, 最后一维中的元素为[类别标签, 四个坐标值]
    Returns:
        列表, [bbox_offset, bbox_mask, cls_labels]
        bbox_offset: 每个锚框的标注偏移量，形状为(bn，锚框总数*4)
        bbox_mask: 形状同bbox_offset, 每个锚框的掩码, 一一对应上面的偏移量, 负类锚框(背景)对应的掩码均为0, 正类锚框的掩码均为1
        cls_labels: 每个锚框的标注类别, 其中0表示为背景, 形状为(bn，锚框总数)
    """
    assert len(anchor.shape) == 3 and len(label.shape) == 3
    bn = label.shape[0]

    def MultiBoxTarget_one(anc, lab, eps=1e-6):
        """
        MultiBoxTarget函数的辅助函数, 处理batch中的一个
        Args:
            anc: shape of (锚框总数, 4)
            lab: shape of (真实锚框数, 5), 5代表[类别标签, 四个坐标值]
            eps: 一个极小值, 防止log0
        Returns:
            offset: (锚框总数*4, )
            bbox_mask: (锚框总数*4, ), 0代表背景, 1代表非背景
            cls_labels: (锚框总数, 4), 0代表背景
        """
        an = anc.shape[0]
        assigned_idx = assign_anchor(lab[:, 1:], anc)  # (锚框总数, )
        bbox_mask = ((assigned_idx >= 0).float().unsqueeze(-1)).repeat(1, 4)  # (锚框总数, 4)

        cls_labels = torch.zeros(an, dtype=torch.long)  # 0表示背景
        assigned_bb = torch.zeros((an, 4), dtype=torch.float32)  # 所有anchor对应的bb坐标
        for i in range(an):
            bb_idx = assigned_idx[i]
            if bb_idx >= 0:  # 即非背景
                cls_labels[i] = lab[bb_idx, 0].long().item() + 1  # 注意要加一
                assigned_bb[i, :] = lab[bb_idx, 1:]

        center_anc = xy_to_cxcy(anc)  # (center_x, center_y, w, h)
        center_assigned_bb = xy_to_cxcy(assigned_bb)

        offset_xy = 10.0 * (center_assigned_bb[:, :2] - center_anc[:, :2]) / center_anc[:, 2:]
        offset_wh = 5.0 * torch.log(eps + center_assigned_bb[:, 2:] / center_anc[:, 2:])
        offset = torch.cat([offset_xy, offset_wh], dim=1) * bbox_mask  # (锚框总数, 4)

        return offset.view(-1), bbox_mask.view(-1), cls_labels

    batch_offset = []
    batch_mask = []
    batch_cls_labels = []
    for b in range(bn):
        offset, bbox_mask, cls_labels = MultiBoxTarget_one(anchor[0, :, :], label[b, :, :])

        batch_offset.append(offset)
        batch_mask.append(bbox_mask)
        batch_cls_labels.append(cls_labels)

    bbox_offset = torch.stack(batch_offset)
    bbox_mask = torch.stack(batch_mask)
    cls_labels = torch.stack(batch_cls_labels)
    return [bbox_offset, bbox_mask, cls_labels]

# 以下函数已保存在d2lzh_pytorch包中方便以后使用
from collections import namedtuple
Pred_BB_Info = namedtuple("Pred_BB_Info", ["index", "class_id", "confidence", "xyxy"])

def non_max_suppression(bb_info_list, nms_threshold = 0.5):
    """
    非极大抑制处理预测的边界框
    Args:
        bb_info_list: Pred_BB_Info的列表, 包含预测类别、置信度等信息
        nms_threshold: 阈值
    Returns:
        output: Pred_BB_Info的列表, 只保留过滤后的边界框信息
    """
    output = []
    # 先根据置信度从高到低排序
    sorted_bb_info_list = sorted(bb_info_list, key = lambda x: x.confidence, reverse=True)

    while len(sorted_bb_info_list) != 0:
        best = sorted_bb_info_list.pop(0)
        output.append(best)

        if len(sorted_bb_info_list) == 0:
            break

        bb_xyxy = []
        for bb in sorted_bb_info_list:
            bb_xyxy.append(bb.xyxy)

        iou = compute_jaccard(torch.tensor([best.xyxy]),
                              torch.tensor(bb_xyxy))[0] # shape: (len(sorted_bb_info_list), )

        n = len(sorted_bb_info_list)
        sorted_bb_info_list = [sorted_bb_info_list[i] for i in range(n) if iou[i] <= nms_threshold]
    return output

def MultiBoxDetection(cls_prob, loc_pred, anchor, nms_threshold = 0.5):
    """
    # 按照「9.4.1. 生成多个锚框」所讲的实现, anchor表示成归一化(xmin, ymin, xmax, ymax).
    https://zh.d2l.ai/chapter_computer-vision/anchor.html
    Args:
        cls_prob: 经过softmax后得到的各个锚框的预测概率, shape:(bn, 预测总类别数+1, 锚框个数)
        loc_pred: 预测的各个锚框的偏移量, shape:(bn, 锚框个数*4)
        anchor: MultiBoxPrior输出的默认锚框, shape: (1, 锚框个数, 4)
        nms_threshold: 非极大抑制中的阈值
    Returns:
        所有锚框的信息, shape: (bn, 锚框个数, 6)
        每个锚框信息由[class_id, confidence, xmin, ymin, xmax, ymax]表示
        class_id=-1 表示背景或在非极大值抑制中被移除了
    """
    assert len(cls_prob.shape) == 3 and len(loc_pred.shape) == 2 and len(anchor.shape) == 3
    bn = cls_prob.shape[0]

    def MultiBoxDetection_one(c_p, l_p, anc, nms_threshold = 0.5):
        """
        MultiBoxDetection的辅助函数, 处理batch中的一个
        Args:
            c_p: (预测总类别数+1, 锚框个数)
            l_p: (锚框个数*4, )
            anc: (锚框个数, 4)
            nms_threshold: 非极大抑制中的阈值
        Return:
            output: (锚框个数, 6)
        """
        pred_bb_num = c_p.shape[1]
        anc = (anc + l_p.view(pred_bb_num, 4)).detach().cpu().numpy() # 加上偏移量

        confidence, class_id = torch.max(c_p, 0)
        confidence = confidence.detach().cpu().numpy()
        class_id = class_id.detach().cpu().numpy()

        pred_bb_info = [Pred_BB_Info(
                            index = i,
                            class_id = class_id[i] - 1, # 正类label从0开始
                            confidence = confidence[i],
                            xyxy=[*anc[i]]) # xyxy是个列表
                        for i in range(pred_bb_num)]

        # 正类的index
        obj_bb_idx = [bb.index for bb in non_max_suppression(pred_bb_info, nms_threshold)]

        output = []
        for bb in pred_bb_info:
            output.append([
                (bb.class_id if bb.index in obj_bb_idx else -1.0),
                bb.confidence,
                *bb.xyxy
            ])

        return torch.tensor(output) # shape: (锚框个数, 6)

    batch_output = []
    for b in range(bn):
        batch_output.append(MultiBoxDetection_one(cls_prob[b], loc_pred[b], anchor[0], nms_threshold))

    return torch.stack(batch_output)


import math
import numbers
import torch

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

import time
import numpy as np
from scipy.ndimage import gaussian_filter

import pdb

class GaussianSmoothing(nn.Module):
    def __init__(self, num_kps):
        super(GaussianSmoothing, self).__init__()
        num_kps = int(num_kps)
        self.heatmaps = nn.Sequential(
            nn.Conv2d(num_kps, num_kps, kernel_size = 5, padding=2, bias=None)
        )
        self.weights_init()

    def weights_init(self):
        n= np.zeros((5,5))
        n[0, 0], n[0, 1], n[0, 2], n[0, 3], n[0, 4] = 1, 4, 7, 4, 1
        n[1, 0], n[1, 1], n[1, 2], n[1, 3], n[1, 4] = 4, 16, 26, 16, 4
        n[2, 0], n[2, 1], n[2, 2], n[2, 3], n[2, 4] = 7, 26, 41, 26, 7
        n[3, 0], n[3, 1], n[3, 2], n[3, 3], n[3, 4] = 4, 16, 26, 16, 4
        n[4, 0], n[4, 1], n[4, 2], n[4, 3], n[4, 4] = 1, 4, 7, 4, 1

        n /= 41

        for name, f in self.named_parameters():
            f.data.copy_(torch.from_numpy(n))

    def make_sparseHeat(self, j2d, vis_idx, heatmapSize = 64):
        """
        Arguments:
            j2d (Torch.Tensor | batch x num_kps x 2): Input 2D joint normalized coordinate (-1 ~ 1)
            heatmapSize (Torch.Tensor | 1,): Heatmap size, default = 224
        Returns:
            sparseHeatmap (Torch.Tensor | batch x 14 x ):
        """
        batch = j2d.size(0)
        num_kps = j2d.size(1)

        # 1. rescale the normalized keypoints by image resolution (-1 ~ 1) --> (0 ~ 223)
        j2d_resized = torch.round((j2d + 1) / 2 * (heatmapSize - 1)).long()

        # 2. make the heatmap using the indices of rescaled keypoints (batch x 14 x 2) --> heatmap: (batch x 14 x 224 x 224)
        heatmap = torch.zeros((batch, num_kps, heatmapSize, heatmapSize)).cuda()

        # There are some GT kps whose value is equal or exceeds the 224. We don't make any heatmap on that kps.
        batch_over_idx, kps_over_idx, _ = torch.where(j2d_resized >= heatmapSize)
        batch_under_idx, kps_under_idx, _ = torch.where(j2d_resized < 0)

        # make values to be zero.
        j2d_resized[torch.cat((batch_over_idx, batch_under_idx)), torch.cat((kps_over_idx, kps_under_idx)), :] = 0

        batch_idx, kps_idx= torch.where(j2d_resized[:, :, 0])
        heatmap[batch_idx, kps_idx, j2d_resized[batch_idx, kps_idx, 1], j2d_resized[batch_idx, kps_idx, 0]] = 1

        # 3. make the entire values of invisible keypoint channel to be zeros
        heatmap[torch.cat((batch_over_idx, batch_under_idx)), torch.cat((kps_over_idx, kps_under_idx)), :, :] = 0

        if vis_idx:
            vis_batch, vis_kps = vis_idx
            heatmap[vis_batch, vis_kps, :, :] = 0

        return heatmap

    def forward(self, x, vis_idx):
        sparseHeat = self.make_sparseHeat(x, vis_idx)
        heatmap = self.heatmaps(sparseHeat)
        return heatmap

if __name__ == '__main__':
    smoothing = GaussianSmoothing(4).cuda()
    for i in range(5):
        input = torch.rand(3, 4, 2).cuda()
        input[0, 0, 0] += 2
        input[2, 0, 1] += 2
        input[1, 0, 0] += 2
        input[1, 0, 1] += 2
        start = time.time()
        output = smoothing(input, None)
        print(f'time {time.time() - start}')


import datetime
import cgi

import app.model
import app.web

class ThreadAtom (app.web.RequestHandler):

    def get (self, pasty_slug):
        self.set_module(__name__ + ".__init__")
        pasties = app.model.Pasty.all()
        pasties.filter("slug =", pasty_slug)

        self.pasty = pasties.get()
        self.pasty_slug = pasty_slug
        self.forks = []

        if self.pasty == None:
            self.get_404()
        else:
            self.get_200()

    def get_200 (self):
        self.get_forks()

        self.content["paste_title"] = self.pasty.get_title()
        self.content["paste_code"] = self.pasty.get_code()

        self.content["u_thread"] = app.url("threads/%s", self.pasty.slug)
        self.content["u_thread_self"] = app.url("threads/%s.atom", self.pasty.slug)
        self.content["paste_username"] = self.pasty.posted_by_user_name
        self.content["paste_posted_at"] = self.pasty.posted_at.strftime("%Y-%m-%dT%H:%M:%SZ")
        self.content["forks"] = self.forks

        self.set_header("Content-Type", "application/atom+xml")
        self.write_out("page/threads/thread_atom/200.html")

    def get_404 (self):
        self.content["paste_slug"] = self.pasty_slug
        self.content["u_thread"] = app.url("threads/%s", self.pasty_slug)
        self.content["u_thread_self"] = app.url("threads/%s.atom", self.pasty_slug)
        self.content["date"] = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")

        self.set_header("Content-Type", "application/atom+xml")
        self.error(404)
        self.write_out("page/threads/thread_atom/404.html")

    def get_forks (self):
        qry_forks = app.model.Pasty.all()
        qry_forks.filter("thread =", self.pasty.slug)
        qry_forks.order("-posted_at")
        db_forks = qry_forks.fetch (10)

        if db_forks:
            for db_fork in db_forks:
                tpl_fork = {}
                tpl_fork["u"] = db_fork.get_url()
                tpl_fork["slug"] = db_fork.slug
                tpl_fork["title"] = db_fork.get_title()
                tpl_fork["published_at"] = db_fork.posted_at.strftime("%Y-%m-%dT%H:%M:%SZ")
                tpl_fork["updated_at"] = db_fork.edited_at.strftime("%Y-%m-%dT%H:%M:%SZ")
                tpl_fork["author_name"] = db_fork.posted_by_user_name
                tpl_fork["summary"] = db_fork.get_snippet()
                self.forks.append(tpl_fork)


import os
import unittest
from pyunitreport import HTMLTestRunner
from time import sleep
from appium import webdriver
from appium.webdriver.common.touch_action import TouchAction
from __Logging import log
from __Utils import parseCapabilities

class Login(unittest.TestCase):

    def setUp(self):
        ueCap = parseCapabilities()
        appiumserverurl = 'http://'+ueCap[3]+':'+ueCap[4]+'/wd/hub'
        log.info("Using Appium Server Url" + appiumserverurl)

        desired_caps = {'platformName': ueCap[0], 'platformVersion': ueCap[1], 'deviceName': ueCap[2],
                        'appPackage': 'com.google.android.youtube', 'appActivity': '.HomeActivity'}

        log.info(desired_caps)
        self.driver = webdriver.Remote(appiumserverurl, desired_caps)
        sleep(10)

    def test_login(self):
        log.starttestcase("test_login")
        ell = self.driver.find_element_by_xpath("//android.widget.ImageView[@content-desc='Account']")
        actions = TouchAction(self.driver)
        actions.tap(ell)
        actions.perform()

        log.debug("Account page is Visiable")
        sleep(5)
        self.driver.save_screenshot(log.screenshotpath("Accoun page"))
        w = self.driver.find_element_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.support.v7.widget.RecyclerView").is_displayed()
        sleep(5)
        log.debug("Switch Account")
        self.driver.save_screenshot(log.screenshotpath("Switch account"))

        sw = self.driver.find_element_by_xpath("	/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.support.v7.widget.RecyclerView/android.widget.LinearLayout[4]/android.widget.RelativeLayout/android.widget.TextView")
        actions = TouchAction(self.driver)
        actions.tap(sw)
        actions.perform()
        sleep(5)
        log.debug("Added accounts visiable")
        self.driver.save_screenshot(log.screenshotpath("Added accounts"))
        #Select Added Account

        #(1) Atleast 2 Account should be added in You Tube app for account switching.
        #(2) Change the content-desc in xpath below with your Account channel name Ex: "Eshan varma"

        ac = self.driver.find_element_by_xpath("//android.widget.LinearLayout[@content-desc='Eshan varma']")
        actions = TouchAction(self.driver)
        actions.tap(ac)
        actions.perform()
        log.debug("selected the added accout, account changed")
        sleep(5)
        self.driver.save_screenshot(log.screenshotpath("Account changed screenshot"))

        #Sign Out
        ell= self.driver.find_element_by_xpath("//android.widget.ImageView[@content-desc='Account']")
        actions = TouchAction(self.driver)
        actions.tap(ell)
        actions.perform()
        log.debug("Switch Account")
        sleep(5)
        self.driver.save_screenshot(log.screenshotpath("Switch account"))
        w = self.driver.find_element_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.support.v7.widget.RecyclerView").is_displayed()
        log.debug("Account page displayed")
        sw = self.driver.find_element_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.support.v7.widget.RecyclerView/android.widget.LinearLayout[4]")
        actions = TouchAction(self.driver)
        actions.tap(sw)
        actions.perform()
        sleep(5)
        sg = self.driver.find_element_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.TextView")
        actions = TouchAction(self.driver)
        actions.tap(sg)
        actions.perform()
        log.debug("Account is in sign out mode")
        sleep(5)
        self.driver.save_screenshot(log.screenshotpath("sign out mode"))
        log.stoptestcase(self)

    def tearDown(self):
        self.driver.quit()


import pandas as pd

from sklearn.model_selection import train_test_split

from sklearn.preprocessing import StandardScaler

from sklearn.neighbors import KNeighborsClassifier

from sklearn.metrics import accuracy_score

data = pd.read_excel('KNN.xlsx')
print(data)

x_fitur = data.iloc[:, 1:5]
y_label = data.iloc[:, -1]

#print(x_fitur)
#print(y_label)

x_training, x_testing, y_training, y_testing = train_test_split(x_fitur , y_label, train_size = 0.8, test_size = 0.2, random_state = 2)

scaller = StandardScaler()
x_training_sc = scaller.fit_transform(x_training)
x_testing_sc = scaller.transform(x_testing)

classifier = KNeighborsClassifier(n_neighbors = 3)
classifier.fit(x_training_sc, y_training)

y_prediksi = classifier.predict(x_testing_sc)

akurasi = accuracy_score(y_testing, y_prediksi)
print('akurasi model adalah', format(akurasi*100),'%')


import matplotlib.pyplot as plt

supervectors_dir = "/Users/lmatayoshi/Documents/Projects/tesis_notebooks/main_experiments/scatter-plots/data/heldout/supervectors/supervectors_single_phonemes/"
dct_dir = "/Users/lmatayoshi/Documents/Projects/tesis_notebooks/main_experiments/scatter-plots/data/heldout/dct/dct_single_phonemes/"
output_dir = "/Users/lmatayoshi/Documents/Projects/tesis_notebooks/main_experiments/scatter-plots/data/heldout/output_dct/"

all_phonemes = ["Y", "f", "c", "x", "G", "d", "b", "g", "z", "w", "N", "B", "rr", "u", "p", "D", "y", "k", "m", "t", "l", "i", "r", "n", "s", "o", "a", "e"]
positive_values = [9, 172, 89, 137, 37, 176, 115, 226, 29, 148, 205, 84, 107, 436, 332, 222, 559, 326, 677, 623, 744, 1094, 811, 1594, 1683, 1725, 2305, 2314]
negative_values = [1, 4, 38, 37, 149, 16, 91, 27, 220, 106, 112, 299, 391, 125, 261, 473, 143, 417, 168, 376, 362, 277, 597, 118, 115, 555, 479, 826]
positives_dict = dict(zip(all_phonemes, positive_values))
negatives_dict = dict(zip(all_phonemes, negative_values))

for phoneme in all_phonemes:
    original_phoneme = phoneme
    if phoneme in ['g', 'y', 'd', 'b', 'n']:
        phoneme = phoneme + "_lowercase"
    supervectors_filename = supervectors_dir + phoneme
    dct_filename = dct_dir + phoneme
    with open(supervectors_filename, "r") as f_supervectors:
        lines = f_supervectors.readlines()
        logids_supervectors = [line.split(" ")[0] for line in lines]
        scores_supervectors = [float(line.split(" ")[4]) for line in lines]
    with open(dct_filename, "r") as f_dct:
        lines = f_dct.readlines()
        logids_dct = [line.split(" ")[0] for line in lines]
        scores_dct = [float(line.split(" ")[4]) for line in lines]
    if not logids_supervectors == logids_dct:
        raise Exception(phoneme + " instances problem")
    plt.clf()
    plt.plot(scores_supervectors, scores_dct, marker=".", linestyle="", color="blue")
    title = phoneme + " heldout, positives: " + str(positives_dict[original_phoneme]) + ", negatives: " + str(negatives_dict[original_phoneme])
    plt.xlabel("supervectors score")
    plt.ylabel("dct score")
    plt.title(title)
    plt.savefig(output_dir + phoneme)


#!/usr/bin/env python

'''
Print matplotlib colors to standard output.
Some inspiration taken from Matplotlib documentation
https://matplotlib.org/stable/gallery/color/named_colors.html
for getting color names and values.
'''

import argparse
from collections import OrderedDict
from os import get_terminal_size

import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib as mpl
from matplotlib import cm

def FormatRGB( rgb ):
    """
    Take output of mpl.colors.to_rgb and format for use in print.
    input is (r, g, b) in decimal form (e.g., values range from 0.0 to 1.0)
    Output is a string with ANSI escape sequences.
    """

    rgb = [ round(i*255) for i in rgb  ]

    # ANSI escape sequence mess
    return "\x1b[48;2;" + str(rgb[0]) + ";" + str(rgb[1]) + ";" + str(rgb[2]) + "m"

def PrintColor( rgb, name, endline ):
    """
    Print output for a single color (rbg, name).
    endline is a control to send a newline character
    """

    # Set the length of an entry as 25 spaces
    num_spaces = 25 - 5 - len(name)
    print( FormatRGB( mcolors.to_rgb(name) ) + "      "
           + "\x1b[0;0m", name, num_spaces*" ", end=endline )

def PrintColors( colors=mcolors.CSS4_COLORS ):
    """
    Print the standard matplotlib colors to screen.
    """

    by_hsv = sorted((tuple(mcolors.rgb_to_hsv(mcolors.to_rgb(color))),
                         name)
                        for name, color in colors.items())
    names = [name for hsv, name in by_hsv]

    n     = len( names  )

    # NOTE: You may edit the number of printed columns here
    ncols = 3
    nrows = n // ncols + int( n % ncols > 0 )

    for i, name in enumerate(names):
        col = i % ncols

        # print color.
        # If we're at the end of a row, send in a newline.
        if ( col == ncols - 1 ):
            PrintColor( mcolors.to_rgb(name), name, "\n"  )
        else:
            PrintColor( mcolors.to_rgb(name), name, " "  )

def GetColormap( name ):
    """
    Load a colormap into a useable object.
    Easy to do with e.g., cm.Blues but we only have a string

    Create a scalarMap object that can return rgb values with
    scalarMap.to_rgba(0...255)
    """

    norm = mpl.colors.Normalize(vmin=0.0, vmax=256)
    scalarMap = cm.ScalarMappable(norm=norm, cmap=name)

    return scalarMap

def GetStep( cols ):
    """
    Given the size of the terminal window,
    set the striding for printing colors
    """

    step = 5
    if ( cols > 55 ):
        step = 5
    if ( cols > 69 ):
        step = 4
    if ( cols > 91 ):
        step = 3
    if ( cols > 136 ):
        step = 2

    return step

def PrintColorbar( name ):
    """
    Print a single colorbar
    """

    scalarMap = GetColormap( name )

    n = 17 # length for text
    print( (n-len(name))*" " + name, end = " " )

    # get the length of the terminal window.
    # used for adjusting amount of colors printed
    cols   = get_terminal_size().columns

    step = GetStep( cols )

    # Print every 5th color. The colorbar is massive if we print all 256
    for i in range(0, 256, step):
        print( FormatRGB(scalarMap.to_rgba(i)[:-1]) + " " + "\033[0;0m",end=""  )
    print("\n")

def PrintColorbars( cmaps ):
    """
    Print a set of colorbars from dictionary cmap
    """

    for cmap_category, cmap_list in cmaps.items():

        cols = get_terminal_size().columns
        size = int( (256 + 17) / GetStep( cols ) - 1 )

        title = " = " + cmap_category + " = "
        print( str( (len(title)+1)*"=" ).center(size ) )
        print(title.center(size))
        print( str( (len(title)+1)*"=" ).center(size ) )
        print("\n")
        for cmap in cmap_list:
            PrintColorbar( cmap )

def main( args ):

    if ( args.colorbars == False and args.all == False ):
        PrintColors()
    elif ( args.colorbars == False and args.all == True ):
        PrintColors( colors=mcolors.XKCD_COLORS )
    else:
        cmaps = OrderedDict()

        # Help us organize the colormaps

        cmaps['Perceptually Uniform Sequential'] = [
            'viridis', 'plasma', 'inferno', 'magma', 'cividis']

        cmaps['Sequential'] = [
            'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
            'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
            'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']

        cmaps['Sequential2'] = [
            'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink',
            'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia',
            'hot', 'afmhot', 'gist_heat', 'copper']

        cmaps['Diverging'] = [
            'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu',
            'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']

        cmaps['Cyclic'] = ['twilight', 'twilight_shifted', 'hsv']

        cmaps['Qualitative'] = ['Pastel1', 'Pastel2', 'Paired', 'Accent',
                        'Dark2', 'Set1', 'Set2', 'Set3',
                        'tab10', 'tab20', 'tab20b', 'tab20c']

        cmaps['Miscellaneous'] = [
            'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern',
            'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg',
            'gist_rainbow', 'rainbow', 'jet', 'turbo', 'nipy_spectral',
            'gist_ncar']

        PrintColorbars(cmaps)
    return 0
if __name__ == "__main__":

    # parse any args
    parser = argparse.ArgumentParser()
    parser.add_argument("-b", "--colorbars", action="store_true",
                    help="display colorbars")
    parser.add_argument( "-a", "--all", action="store_true", help="Print all xkcd colors" )

    args = parser.parse_args()

    main( args  )


from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.core.paginator import Paginator

from app.models import GoodsInfo, TypeInfo

def index(request):
    if request.method =='GET':
        return render(request,'backadmin/index.html')

# 分页展示
def productlist(request):
    if request.method == 'GET':
        num = request.GET.get('page_num',1)
        good = GoodsInfo.objects.filter(is_delete=0)
        paginator = Paginator(good,1)
        page = paginator.page(num)
        data={'goods':page}
        return render(request,'backadmin/product_list.html',data)

# 删除
def deletegood(request):
    if request.method == 'GET':
        gid = request.GET.get('gid')
        good = GoodsInfo.objects.filter(id=gid).first()
        good.is_delete = 1
        good.save()
        msg = '删除成功'
        return HttpResponseRedirect(reverse('backadmin:productlist'),{'msg':msg})

# 增加和修改
def productdetail(request):
    gtypes = TypeInfo.objects.all()
    gid = request.GET.get('gid')
    good = GoodsInfo.objects.filter(id=gid).first()
    if request.method == 'GET':
        if gid:
            data={'goods':good, 'gtypes':gtypes}
        else:
            data = {'gtypes': gtypes}
        return render(request,'backadmin/product_detail.html', data )

    if request.method == 'POST':
        gname = request.POST.get('gname')
        gprice = request.POST.get('gprice')
        gpic = request.FILES.get('gpic')
        gstock = request.POST.get('gstock')
        tname = request.POST.get('tname')
        gtype_id = TypeInfo.objects.filter(tname=tname).first().id

        gid = request.POST.get('gid')

        if gid:
            if  all([gname, gprice, gpic ,gstock, gtype_id]):
                good.gname  = gname
                good.gprice = gprice
                good.gpic   = gpic
                good.gstock = gstock
                good.gtype_id = gtype_id
                good.save()
                msg = '修改成功'
                return HttpResponseRedirect(reverse('backadmin:productlist'), {'msg': msg})
            elif all([gname, gprice, gstock, gtype_id]):
                good.gname = gname
                good.gprice = gprice
                good.gstock = gstock
                good.gtype_id = gtype_id
                good.save()
                msg = '修改成功'
                return HttpResponseRedirect(reverse('backadmin:productlist'), {'msg': msg})
            else:
                msg = '请将数据填写完整'
                data = {'gtypes': gtypes, 'msg': msg, 'goods': good}
                return render(request, 'backadmin/product_detail.html', data)

        else:
            if not all([gname, gprice, gpic, gstock, gtype_id]):
                msg = '请将数据填写完整'
                data ={'gtypes':gtypes,'msg': msg}
                return render(request, 'backadmin/product_detail.html', data)
            else:
                GoodsInfo.objects.create(gname=gname,gprice=gprice,gpic=gpic,gstock=gstock,gtype_id=gtype_id)
                msg = '添加成功'
                return HttpResponseRedirect(reverse('backadmin:productlist'),{'msg': msg})

def recyclebin(request):
    if request.method =='GET':
        num = request.GET.get('page_num',1)
        good = GoodsInfo.objects.filter(is_delete=1)
        paginator = Paginator(good,1)
        page = paginator.page(num)
        data={'goods':page}
        return render(request,'backadmin/recycle_bin.html',data)

# 恢复
def recovergood(request):
    if  request.method == 'GET':
        gid = request.GET.get('gid')
        good = GoodsInfo.objects.filter(id=gid).first()
        good.is_delete = 0
        good.save()
        msg = '恢复成功'
        return HttpResponseRedirect(reverse('backadmin:recyclebin'),{'msg':msg})

def login(request):
    if request.method =='GET':
        return render(request,'backadmin/login.html')


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" Pygame basics

See https://inventwithpython.com/pygame/chapter2.html

Usage:
    python3 file.py <PARAM>
"""
import pygame
from pygame.locals import *
import sys

pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Hello World')
while True:
    for event in pygame.event.get():
        if event.type == 'QUIT':
            pygame.quit()
            sys.exit()
    pygame.display.update()


from typing import List

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        def BinarySearch(nums: List[list], target) ->bool:
            left, right = -1, len(nums)
            while right - left > 1:
                mid = left + (right - left) //2
                if nums[mid] > target:
                    right = mid
                else:
                    left = mid
            return left

        n = len(matrix)
        for i in range(n):
            index = BinarySearch(nums=matrix[i], target=target)
            pur = matrix[i][index]
            if pur == target:
                return True
        return False