"""
A module for making and displaying drifting gratings to the
screen of a raspberry pi (with the default NOOBS installation
of raspbian) while bypassing the windowing system.

It is best to run this module from the fullscreen terminal,
accessed with Ctrl+Alt+F1 (Ctrl+Alt+F7 returns to the desktop)
because otherwise the X windowing system and this module will
both be attempting to configure the framebuffer.

This module is an object-oriented wrapper for the
_rpigratings module, which is implemented in C for
performance.

"""
import time as t
import os
import sys
import hashlib
import random
from collections import namedtuple

GratPerfRec = namedtuple("GratingPerformanceRecord",["mean_interframe","stddev_interframe","start_time"])

GRAY   = 127
BLACK  = 0
WHITE  = 255
SINE   = 0b0001
SQUARE = 0b0000

#These are tags passed to the C architexture to specify the bits per pixel,
#probably don't change them unless you're confident you know what you're doing!
RGB888MODE =  0b0010
RGB565MODE =  0b0000

import _rpigratings as rpigratings

def build_grating(filename, options):

    """
    Create a raw animation file of a drifting grating. Saves file to hard disc.
    This file is then loaded with Screen.load_grating, and displayed with one
    of the Screen methods.

    Args:
      filename: The filename and path to file. ~/dir/filename will
        generate a file called filename in a directory dir in the users
        home directory.
      options: A dictionary with the required keys of, duration, angle,
        spac_freq, and temp_freq, which can be created as follows:
        options = {
          "duration": 2,     #2 second long grating
          "angle": 90,       #90 degree gratings,
          "spac_freq": 0.1,  #spatial frequency in cycles per degree
          "temp_freq": 0.5,  #temporal frequency in cycles per second
        }
        optional options are:
          "contrast": 1      #maximum contrast

          "background": 127,   #
          "resolution": (1280, 720)   #resolution of gratings. Must match Screen()
          "waveform": rpg.SINE #rpg.SQUARE (square wave) or rpg.SINE (sine wave)

    For smooth propogation of the grating, the pixels-per-frame speed
    is truncated to the nearest interger; low resolutions combined with
    a low temporal frequency:spacial frequency ratio may result in incorrect
    speeds of propogation or even static, unmoving gratings. This also means
    that the temp_freq is approximate only.

    Returns:
      Nothing
    """

    options = _parse_options(options)

    filename = os.path.expanduser(filename)
    rpigratings.build_grating(filename, options["duration"], options["angle"],
                              options["spac_freq"], options["temp_freq"],
                              options["contrast"], options["background"],
                              options["resolution"][0], options["resolution"][1],
                              options["waveform"], 0, 0, 0, 0, 0, options["colormode"])

def build_masked_grating(filename, options):
    """
    Create a raw animation file of a drifting grating with a circular mask.
    Saves file to hard disc. This file is then loaded with Screen.load_grating,
    and displayed with one    of the Screen methods.

    Args:
      filename: The filename and path to file. ~/dir/filename will
        generate a file called filename in a directory dir in the users
        home directory.
      options: A dictionary with the required keys of: duration, angle,
        spac_freq and temp_freq. The user will also want to set the keys
        of percent_diameter, percent_center_top, percent_center_left
        and percent_padding which can be created as follows:
        options = {
          "duration": 2,     #2 second long grating
          "angle": 90,       #90 degree gratings,
          "spac_freq": 0.1,  #spatial frequency in cycles per degree
          "temp_freq": 0.5,  #temporal frequency in cycles per second
          "percent_diameter": 50,    #diameter of mask as percentage of screen width
          "percent_center_left": 50, #horizontal center of mask as percentage of screen width
          "percent_ceter_top": 50,   #vertical center of mask as percentage of screen height
        }
        optional options are:
          "percent_padding", 10      #specify whether the edge of mask blurs to background value over what distance
          "contrast": 1,     #maximum contrast
          "background": 127,   #
          "resolution": (1280, 720)   #resolution of gratings. Must match Screen()
          "waveform": rpg.SINE #rpg.SQUARE (square wave) or rpg.SINE (sine wave)

    Returns:
      Nothing.
    """

    options = _parse_options(options)

    filename = os.path.expanduser(filename)
    rpigratings.build_grating(filename, options["duration"], options["angle"],
                              options["spac_freq"], options["temp_freq"],
                              options["contrast"], options["background"],
                              options["resolution"][0], options["resolution"][1],
                              options["waveform"], 0, options["percent_diameter"],
                              options["percent_center_left"], options["percent_center_top"],
                              options["percent_padding"], options["colormode"])

def build_gabor(filename, options):
    """
    Create a raw animation file of a drifting gabor patch. Saves file to hard disc.
    This file is then loaded with Screen.load_grating, and displayed with one
    of the Screen methods.

    Args:
      filename: The filename and path to file. ~/dir/filename will
        generate a file called filename in a directory dir in the users
        home directory.
      options: A dictionary with the required keys of: duration, angle,
        spac_freq, and temp_freq. The user will also want to set the keys of
        sigma, percent_center_top, percent_center_left and percent_padding
        which can be created as follows:
        options = {
          "duration": 2,     #2 second long grating
          "angle": 90,       #90 degree gratings,
          "spac_freq": 0.1,  #spatial frequency in cycles per degree
          "temp_freq": 0.5,  #temporal frequency in cycles per second
          "percent_sigma": 10,       #'standard deviation' of the gaussian envelope of the gabor function
                                     #as a percentage of screen width
          "percent_center_left": 50, #horizontal center of mask as percentage of screen width
          "percent_ceter_top": 50,   #vertical center of mask as percentage of screen height
        }
        optional options are:
          "contrast": 1,     #maximum contrast
          "background": 127,   #
          "resolution": (1280, 720)   #resolution of gratings. Must match Screen()
          "waveform": rpg.SINE #rpg.SQUARE (square wave) or rpg.SINE (sine wave)

    Returns:
      Nothing.
    """
    options = _parse_options(options)

    filename = os.path.expanduser(filename)
    rpigratings.build_grating(filename, options["duration"], options["angle"],
                              options["spac_freq"], options["temp_freq"],
                              options["contrast"], options["background"],
                              options["resolution"][0], options["resolution"][1],
                              options["waveform"], options["percent_sigma"], 0,
                              options["percent_center_left"], options["percent_center_top"],
                              0, options["colormode"])

def build_list_of_gratings(func_string, directory_path, options):

    """
    Builds a range of gratings varying over one property. One of the options
    supplied can be a list, and the function will iterate over that list building
    gratings matching each element of this list

    Args:
      func_string: String matching either "grating", "mask" or "gabor", to produce
        full screen gratings, gratings with a circular mask, or gabors, respectively
      directory_path: An absolute or relative path to the directory where
        where the above files will be saved. Most likely, each set of gratings
        generated with this function will be saved in their own directory so
        can be displayed with the Screen.display_rand_grating_on_pulse()
      options: A dictionary containing options, see build_grating(),
        build_masked_grating() or build_gabor() for appropriate options, but note
        one of the options must be in the form of a list.

    Files will be saved with names matching the element of the list they are generated
    from. e.g. if generated with options["angle"] = [0 45 90], then there will be three
    files generated with names "0", "45" and "90" in the directory specificied in
    directory path.

    Returns:
      Nothing
    """

    if func_string == "grating":
        func = build_grating
    elif func_string == "mask":
        func = build_masked_grating
    elif func_string == "gabor":
        func = build_gabor
    else:
        raise ValueError("func_string must be either 'grating', 'mask' or 'gabor', not %s" %func_string)

    path_to_directory = os.path.expanduser(directory_path)
    cwd = os.getcwd()
    os.makedirs(path_to_directory)
    os.chdir(path_to_directory)

    iterable = [];
    for key, value in options.items():
        if isinstance(value, list):
            iterable.append(key)

    if len(iterable) > 1:
        raise ValueError("Supply only a single list as an option, rather than %s" %iterable)

    if len(iterable) == 0:
        raise ValueError("Supply one option as a list")

    for val in options[iterable[0]]:
        options_copy = options.copy()
        options_copy[iterable[0]] = val
        func(str(val), options_copy)

    os.chdir(cwd)

def convert_raw(filename, new_filename, n_frames, width, height, refreshes_per_frame, colormode = 16):
    """
    Converts a raw video/image file saves as uint8: RGBRGBRGB... starting
      in the top left pixel and proceeding rowwise, into a form readily
      displayed by RPG.

    Args:
      filename: the exact path to the raw file, either as relative or absolute
        e.g. "~/videos/raw1.raw".
      new_filename: the exact path of the converted file to be produced e.g.
        "~/raws/raw1.raw".
      n_frames: the number of frames in the raw video/image
      width: the width of the original file in pixels
      height: the height of the original file in pixels
      refreshes_per_frame: the number of monitor refreshes to display each frame for.
        For a movie to display at 30 frames per second, on a 60 Hz monitor, this would
        be 2. On a 75 Hz monitor, 25 frames per second would be acheived by setting this
        to 3. If a still image is displayed, if you require it displayed for X seconds,
        and your monitor refresh rate is R Hz, then this value should be set to X * R.
      colormode: number of bits per pixel; must be 16 or 24.
    Returns:
      None
    """
    if colormode in {0, 16, "rgb565", "RGB565", "565", "16"}:
        colormode = RGB565MODE
    elif colormode in {1, 24, "24", "rgb888", "RGB888", "888"}:
        colormode = RGB888MODE
    else:
        raise ValueError("colormode must be 16 or 24, not %s"%op["colormode"].__repr__)
    filename = os.path.expanduser(filename)
    new_filename = os.path.expanduser(new_filename)
    rpigratings.convertraw(filename, new_filename, n_frames, width, height, refreshes_per_frame,colormode)

class Screen:
    def __init__(self, resolution=(1280,720), background = 127, colormode = 16):
        """
        A class encapsulating the raspberry pi's framebuffer,
          with methods to display drifting gratings and solid colors to
          the screen.

        ONLY ONE INSTANCE OF THIS OBJECT SHOULD EXIST AT ANY ONE TIME.
          Otherwise both objects will be attempting to manipulate the memory
          assosiated with the linux framebuffer. If a resolution change is desired
          first clean up the old instance of this class with the close() method
          and then create the new instance, or del the first instance.

        The resolution of this screen object does NOT need to match the actual
          resolution of the physical display; the linux framebuffer device is
          automatically scaled up to fit the physical display.

        The resolution of this object MUST match the resolution of any
          grating animation files created by draw_grating; if the resolution
          of the animation is smaller pixels will simply be misaligned, but
          if the animation is larger then attempting to play it will cause a
          segmentation fault.

        Args:
          resolution: a tuple of the desired width of the display
            resolution as (width, height). Defaults to (1280,720).
          background: value between 0 and 255 for the background
         """
        if (background < 0 or background > 255):
                raise ValueError("Background must be between 0 and 255")

        if colormode in {0, 16, "565", "rgb565", "RGB565", "16"}:
                colormode = 0
        elif colormode in {1, 24, "888", "rgb888", "RGB888", "24"}:
                colormode = 2
        else:
                raise ValueError("Colormode must be 16 or 24, not %s"%colormode.__repr__)

        self.background = background
        self.capsule = rpigratings.init(resolution[0],resolution[1], colormode)
        self.colormode = colormode
        self.isopen = True

    def load_grating(self,filename):
        """
        Load a grating file called filename into local memory. Once loaded
        in this way, display_grating() can be called to display the loaded file
        to the screen.

        Args:
          filename: string containing the exact filename, either as an absolute
            or relative, e.g. "~/gratings/grat1.dat" or "home/pi/grating/grat1.dat"
        Returns:
          Grating object
        """
        filename = os.path.expanduser(filename)
        return Grating(self,filename)

    def load_raw(self, filename):
        """
        Load a raw file into local memory. Once loaded in this way, the returned
        object can be displayed with display_raw()

        Args:
          filename: string containint the exact filename, either as an absolute
            or relative path.
        Returns:
          Raw object
        """

        filename = os.path.expanduser(filename)
        return Raw(self, filename)

    def display_grating(self, grating, trigger_pin = 0):
        """
        Display the passed grating object (grating files are created with
        the draw_grating function and loaded with the Screen.load_grating
        method).

        Returns a namedtuple (from the collections module) with the fields
        mean_interframe, stddev_interframe and start_time; these refer
        respectively to the average interframe time in microseconds, the standard
        deviation of the interframe time and grating began to play in Unix Time,
        respectively.

        Args:
          grating: a grating objected loaded with Screen.load_grating()
          end_with: greyscale color value to display when grating ends. Value
          should be between 0 and 255.
          trigger_pin: set to 0 to display gratting as soon as possible or set to
            the GPIO pin (as defined by wiringPi) to wait for a trigger signal.
            Note: digital signal is 3.3 volts max, not 5 volt TTL. 5 volt signals
            risk permanently damaging the raspberry pi.

        Returns:
          performance record as a named tuple.
        """
        if trigger_pin == 1:
                raise ValueError("trigger_pin cannot be set to 1. This pin is reserved for feedback")

        rawtuple = rpigratings.display_grating(self.capsule, grating.capsule, trigger_pin)
        if rawtuple is None:
                return None
        else:
                return GratPerfRec(*rawtuple)

    def display_raw(self, raw, trigger_pin = 0):
        """
        Displays the passed raw object (raw objects are loaded with the
        Screen.load_raw method) either as soon as possible, or in response
        to 3.3V trigger.

        Args:
          raw: a raw object loaded with Screen.load_raw()
          trigger_pin: set to 0 to display raw as soon as possible or set to
            the GPIO pin (as defined by wiringPi) to wait for a trigger signal.
            Note: digital signal is 3.3 volts max, not 5 volt TTL. 5 volt signals
            risk permanently damaging the raspberry pi.

        Returns:
          Performance record as named tuple.
        """

        if trigger_pin == 1:
                raise ValueError("trigger_pin cannot be set to 1. This pin is reserved for feedback")

        rawtuple = rpigratings.display_raw(self.capsule, raw.capsule, trigger_pin)
        if rawtuple is None:
                return None
        else:
                return GratPerfRec(*rawtuple)

    def display_greyscale(self,color,blocking=True):
        """
        Fill the screen with a solid color until something else is
                displayed to the screen. Calling display_color(GRAY) will
                display mid-gray.

        Args:
          color: Value between 0 and 255.
          blocking: True or False. When blocking is true, function will not return
            unless the frame has been displayed for one frame.  With blocking set
            to False, function returns after data written to frame buffer, meaning that
            subsequent functions which write to frame buffer could overwrite the color
            information BEFORE it is desplayed.

        Returns:
          None
        """
        if (color<0 or color>255):
                self.close()
                raise ValueError("Color must be between each between 0 and 255.")
        rpigratings.display_color(self.capsule,color,color,color,self.colormode,blocking)

    def display_gratings_randomly(self, dir_containing_gratings, intertrial_time, algorithm = "md5", logfile_name="rpglog.txt"):
        """
        For each file in directory dir_containing_gratings, attempt to display
        that file as a grating. The order of display is a fixed but psudorandomised.
        The location of the log file is ~/rpg/logs/ . Gratings are seperated
        by intertrial_time seconds of the background color set when Screen
        object created.

        Method is blocking, and will not return until all files in directory
        displayed

        Args:
          dir_containing_gratings: A relative or absolute directory path
            to a directory containing gratings. Must not contain any other
            non grating files, or sub directories.
          intertrial_time: Time between gratings in seconds. Accuracy limited by
            monitor refresh rate. If intertrial time is set to X seconds, the true
            intertrial time may be anywhere from X to X + 1/[refresh rate] seconds
          algorithm: Set to either "md5" or "shuffle". "md5" produces a randomization
            that stays fixed over experiments, while "shuffle" randomizes the order of
            playback every cycle of the directory content.
          logfile_name: Name of log file to write performance record to.
            written into directory ~/rpg/logs/

        Returns:
          None
        """

        dir_containing_gratings = os.path.expanduser(dir_containing_gratings)
        gratings = []
        print("Loading gratings...")
        for file in os.listdir(dir_containing_gratings):
            gratings.append((self.load_grating(dir_containing_gratings + "/" + file),dir_containing_gratings + "/" + file))
        randomized_gratings = self._randomize_grating_list(gratings, algorithm = algorithm)

        print("Displaying in order of: " + str([x[1].split("/")[-1] for x in randomized_gratings ] ))

        for grating in randomized_gratings:
            perf = self.display_grating(grating[0])
            self.display_greyscale(self.background)
            self._print_log(logfile_name, "Grating", grating[1], perf)
            t.sleep(intertrial_time)

    def display_raw_randomly(self, dir_containing_raws, intertrial_time, algorithm = "md5", logfile_name="rpglog.txt"):
        """
        For each file in directory dir_containing_raws, attempt to display
        that file as a raw. The order of display is a fixed but psudorandomised.
        The location of the log file is ~/rpg/logs/ . Raws are seperated
        by intertrial_time seconds of the background color set when Screen
        object created.

        Method is blocking, and will not return until all files in directory
        displayed

        Args:
          dir_containing_raws: A relative or absolute directory path
            to a directory containing raws. Must not contain any other
            non raw files, or sub directories.
          intertrial_time: Time between raws in seconds. Will have
            ~of 1-2 monitor refresh cycles
          algorithm: Set to either "md5" or "shuffle". "md5" produces a randomization
            that stays fixed over experiments, while "shuffle" randomizes the order of
            playback every cycle of the directory content.
          logfile_name: Name of log file to write performance record to.
            written into directory ~/rpg/logs/

        Returns:
          None
        """

        dir_containing_raws = os.path.expanduser(dir_containing_raws)
        raws = []
        print("Loading raws...")
        for file in os.listdir(dir_containing_raws):
            raws.append((self.load_raw(dir_containing_raws + "/" + file),dir_containing_raws + "/" + file))
        randomized_raws = self._randomize_grating_list(raws, algorithm=algorithm)

        print("Displaying in order of: " + str([x[1].split("/")[-1] for x in randomized_rawss ] ))

        for raw in randomized_raws:
            perf = self.display_raw(raw[0])
            self.display_greyscale(self.background)
            self._print_log(logfile_name, "Raw", raw[1], perf)
            t.sleep(intertrial_time)

    def display_rand_grating_on_pulse(self, dir_containing_gratings, trigger_pin, algorithm="md5", logfile_name="rpglog.txt"):
        """
        Displays a psudorandom grating from the passed directory in response
        to a 3.3V signal to a GPIO pin. Gauranteed to display each grating
        in directory before playing gratings again. Will display gratings
        in a fixed order across sessions. Between gratings, displays
        Screen.background() shade. Function is blocking, but will return
        in response to a keystroke

        Args:
          dir_containing_gratings: A relative or absolute directory path
            to a directory containing gratings. Must not contain any other
            non grating files, or sub directories.
          trigger_pin: Which trigger pin the raspberry pi listens on for the
            3.3V pulse.
          algorithm: Set to either "md5" or "shuffle". "md5" produces a randomization
            that stays fixed over experiments, while "shuffle" randomizes the order of
            playback every time function is called.
          logfile_name: Name of log file to write performance record to.
            written into directory ~/rpg/logs/

        Returns:
          None
        """

        self.display_greyscale(self.background)

        dir_containing_gratings = os.path.expanduser(dir_containing_gratings)
        gratings = []
        print("Loading gratings...")
        for file in os.listdir(dir_containing_gratings):
            gratings.append((self.load_grating(dir_containing_gratings + "/" + file),dir_containing_gratings + "/" + file))
        randomized_gratings = self._randomize_grating_list(gratings, algorthm=algorithm)
        print("Displaying in order of: " + str([x[1].split("/")[-1] for x in randomized_gratings ] ))
        print("Waiting for pulse on pin " + str(trigger_pin) + ".")
        print("Press any key to stop waiting...")
        n = 0
        while True:
            perf = self.display_grating(randomized_gratings[n][0], trigger_pin)
            self.display_greyscale(self.background)
            if perf is None:
                break
            self._print_log(logfile_name, "Grating", randomized_gratings[n][1], perf)
            n += 1
            if n >= len(randomized_gratings):
                n = 0

        print("Waiting for pulses ended")

    def display_rand_raw_on_pulse(self, dir_containing_raws, trigger_pin, algorithm="md5", logfile_name="rpglog.txt"):
        """
        Displays a psudorandom raw from the passed directory in response
        to a 3.3V signal to a GPIO pin. Gauranteed to display each raw
        in directory before playing raws again. Will display raw
        in a fixed order across sessions. Between raws, displays
        Screen.background() shade. Function is blocking, but will return
        in response to a keystroke.

        Args:
          dir_containing_raws: A relative or absolute directory path
            to a directory containing raws. Must not contain any other
            non raw files, or sub directories.
          trigger_pin: Which trigger pin the raspberry pi listens on for the
            3.3V pulse.
          algorithm: Set to either "md5" or "shuffle". "md5" produces a randomization
            that stays fixed over experiments, while "shuffle" randomizes the order of
            playback every time function is called.
          logfile_name: Name of log file to write performance record to.
            written into directory ~/rpg/logs/

        Returns:
          None
        """

        self.display_greyscale(self.background)

        dir_containing_raws = os.path.expanduser(dir_containing_raws)
        raws = []
        print("Loading raws...")
        for file in os.listdir(dir_containing_raws):
            raws.append((self.load_grating(dir_containing_raws + "/" + file),dir_containing_raws + "/" + file))
        randomized_raws = self._randomize_grating_list(raws, algorithm=algorithm)
        print("Displaying in order of: " + str([x[1].split("/")[-1] for x in randomized_raws ] ))
        print("Waiting for pulse on pin " + str(trigger_pin) + ".")
        print("Press any key to stop waiting...")
        n = 0
        while True:
            perf = self.display_raw(randomized_raws[n][0], trigger_pin)
            self.display_greyscale(self.background)
            if perf is None:
                break
            self._print_log(logfile_name, "Raw", randomized_raws[n][1], perf)
            n += 1
            if n >= len(randomized_raws):
                n = 0

        print("Waiting for pulses ended")

    def _print_log(self, filename, file_type, file_displayed, perf):
        """
        Internal function for print log file

        Args:
          filename: string of file displayed
          file_type: string of whether the file was a raw or a grating
          perf: Named tuple of performance record

        Returns:
          None
        """
        path_of_logfile = os.path.expanduser("~/rpg/logs/") + filename
        with open(path_of_logfile, "a") as file:
            file.write("%s: \t %s \t Displayed starting at (unix time): %d \t Average frame duration (micros): %.2f \t  Std Dev of frame duration(FPS): %.2f \n"
                %(file_type, file_displayed, perf.start_time, perf.mean_interframe, perf.stddev_interframe))

    def _randomize_grating_list(self, gratings, algorithm):
        """
        Internal function for psudorandomizing gratings. Files paths are hashed
        with MD5 to generate a string, which is then sorted to give order. This
        achieves a psuedo random order that is fixed across sessions.

        Args:
          gratings: list containing grating path names
          algorith: Must be "md5" or "shuffle", decides whether order is fixed between
            runs, or truely random.

        Returns:
          list of grating path names psuedorandomzied
        """
        if algorithm == "md5":
            hashed_gratings = [ hashlib.md5(grating[1].encode()).hexdigest() for grating in gratings]
            labelled_hashes = enumerate(hashed_gratings)
            labelled_hashes = [ (x[1], x[0]) for x in labelled_hashes ]
            sorted_hashes = sorted(labelled_hashes)
            randomized_gratings = []

            for el in sorted_hashes:
                randomized_gratings.append( gratings[el[1]] )
            return randomized_gratings
        elif algorithm == "shuffle":
            return random.shuffle(gratings)
        else:
            raise ValueError("Algorithm parameter must be either set to 'md5' or 'shuffle'")

    def close(self):
        """
        Destroy this object, cleaning up its memory and restoring previous
        screen settings.

        Args:
          None

        Returns:
          None
        """
        if self.isopen:
            rpigratings.close_display(self.capsule)
            self.isopen = False

    def __del__(self):
        self.close()
    def __enter__(self):
        return self
    def __exit__(self,exception_type, exception_value, traceback):
        self.close()

class Grating:
    def __init__(self, master, filename):
        if type(master).__name__ != "Screen":
            raise ValueError("master must be a Screen instance")
        self.master = master
        self.capsule = rpigratings.load_grating(master.capsule, filename)
    def __del__(self):
        if "capsule" in self.__dict__:
            rpigratings.unload_grating(self.capsule)

class Raw:
    def __init__(self, master, filename):
        if type(master).__name__ != "Screen":
            raise ValueError("master must be a Screen instance")
        self.master = master
        self.capsule = rpigratings.load_raw(master.capsule, filename)
    def __del__(self):
        if "capsule" in self.__dict__:
            rpigratings.unload_raw(self.capsule)

def _parse_options(options):
    """
    An internal function for testing if options have been
    set correctly, and setting default options if none
    have been provided.

    Args:
      options: A dictionary containing options.

    Returns:
      Parsed option dictionary suitable for passing to other functions
    """
    op = options.copy()
    if "duration" in op:
        if op["duration"] <= 0:
            raise ValueError("options['duration'] option set to invalid value of %d, must be >0" %op["duration"])
    else:
        raise ValueError("Provide options['duration'] in option dictionary")

    if "angle" not in op:
        raise ValueError("Provide options['angle'] in options dictionary")

    if "spac_freq" in op:
        if op["spac_freq"] <= 0:
            raise ValueError("options['spac_freq'] set to invalid value of %d, must be > 0" %op["spac_freq"])
    else:
        raise ValueError("Provide options['space_freq'] in options dictionary")

    if "temp_freq" in op:
        if op["temp_freq"] < 0:
            raise ValueError("options['temp_freq'] set to invalid value of %d, must be >= 0" %op["temp_freq"])
    else:
        raise ValueError("Provide options['temp_freq'] in options dictionary")

    if "contrast" in op:
        if op["contrast"] < 0 or op["contrast"] > 1:
            raise ValueError("options['contrast'] set to invalid value of %d, must be set between 0 and 1 or not set" %op["contrast"])
    else:
        op["contrast"] = 1

    if "background" in op:
        if op["background"] < 0 or op["background"] > 255:
            raise ValueError("options['background'] set to invalid value of %d, must be set between 0 and 255 or not set" %op["contrast"])
    else:
        op["background"] = 127

    if "resolution" not in op:
        op["resolution"] = (1280, 720)

    if "waveform" not in op:
        op["waveform"] = SINE

    if "percent_sigma" in op:
        if op["percent_sigma"] <= 0:
            raise ValueError("options['percent_sigma'] set to invalid value of %d, must be set > 0 or not set" %op["percent_sigma"])
    else:
        op["percent_sigma"] = 0

    if "percent_diameter" in op:
        if op["percent_diameter"] <= 0:
            raise ValueError("options['percent_diameter'] set to invalid value of %d, must be set > 0 or not set" %op["percent_diameter"])
    else:
        op["percent_diameter"] = 0

    if "percent_center_left" not in op:
        op["percent_center_left"] = 50

    if "percent_center_top" not in op:
        op["percent_center_top"] = 50

    if "percent_padding" in op:
        if op["percent_padding"] <= 0:
            raise ValueError("options['percent_padding'] set to invalid value of %d, must be set > 0 or not set" %op["percent_padding"])
    else:
        op["percent_padding"] = 0

    if "colormode" in op:
        if op["colormode"] in {0, 16, "rgb565", "RGB565", "565", "16"}:
            op["colormode"] = RGB565MODE
        elif op["colormode"] in {1, 24, "24", "rgb888", "RGB888", "888"}:
            op["colormode"] = RGB888MODE
        else:
            raise ValueError("options['colormode'] must be 16 or 24, not %s"%op["colormode"].__repr__)
    else:
        op["colormode"] = RGB565MODE
    return op


import os
import sys

import inkex

import pyembroidery

from ..i18n import _
from ..svg import PIXELS_PER_MM, render_stitch_plan
from ..svg.tags import INKSCAPE_LABEL
from .stitch import Stitch
from .stitch_plan import StitchPlan

def generate_stitch_plan(embroidery_file, import_commands="symbols"):  # noqa: C901
    validate_file_path(embroidery_file)
    pattern = pyembroidery.read(embroidery_file)
    stitch_plan = StitchPlan()
    color_block = None

    for raw_stitches, thread in pattern.get_as_colorblocks():
        color_block = stitch_plan.new_color_block(thread)
        for x, y, command in raw_stitches:
            if command == pyembroidery.STITCH:
                color_block.add_stitch(Stitch(x * PIXELS_PER_MM / 10.0, y * PIXELS_PER_MM / 10.0))
            if len(color_block) > 0:
                if import_commands == "none" and command in [pyembroidery.TRIM, pyembroidery.STOP]:
                    # Importing commands is not wanted:
                    # start a new color block without inserting the command
                    color_block = stitch_plan.new_color_block(thread)
                elif command == pyembroidery.TRIM:
                    color_block.add_stitch(trim=True)
                elif command == pyembroidery.STOP:
                    color_block.add_stitch(stop=True)
                    color_block = stitch_plan.new_color_block(thread)

    stitch_plan.delete_empty_color_blocks()

    if stitch_plan.last_color_block:
        if stitch_plan.last_color_block.last_stitch:
            if stitch_plan.last_color_block.last_stitch.stop:
                # ending with a STOP command is redundant, so remove it
                del stitch_plan.last_color_block[-1]

    extents = stitch_plan.extents
    svg = inkex.SvgDocumentElement("svg", nsmap=inkex.NSS, attrib={
        "width": str(extents[0] * 2),
        "height": str(extents[1] * 2),
        "viewBox": "0 0 %s %s" % (extents[0] * 2, extents[1] * 2),
    })

    visual_commands = True if import_commands == "symbols" else False
    render_stitch_plan(svg, stitch_plan, visual_commands=visual_commands)

    # rename the Stitch Plan layer so that it doesn't get overwritten by Embroider
    layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']")
    layer.set(INKSCAPE_LABEL, os.path.basename(embroidery_file))
    layer.attrib.pop('id')

    # Shift the design so that its origin is at the center of the canvas
    # Note: this is NOT the same as centering the design in the canvas!
    layer.set('transform', 'translate(%s,%s)' % (extents[0], extents[1]))

    return svg

def validate_file_path(path):
    # Check if the file exists
    if not os.path.isfile(path):
        inkex.errormsg(_('File does not exist and cannot be opened. Please correct the file path and try again.\r%s') % path)
        sys.exit(1)


import os
import numpy as np
import random
import math
import h5py
from nn_activation_functions_utilities import relu, tanh, softmax, relu_backward, tanh_backward, softmax_backward, cross_entropy_softmax
from nn_cost_functions import crossEntropy_cost, mse_cost
from nn_parameters_initalizations import he_init, random_init, parameters_init
from nn_optimizers_initalizations import momentum_init, adam_init
from nn_utilities import random_mini_batches
from nn_parameters_update import update_parameters_gd, update_parameters_momentum, update_parameters_adam

class neural_network_model(object):
    def __init__(self):
        self.parameters = {}             # parameters dictionary: weight + bias
        self.costs = []                  # list to track the cost
        self.activations_functions = []  # list of activation functions
        self.number_of_layers = 0        # nn size

    def __forward_propagation(self,x,parameters,activations):
        '''
            This function is for the forward propagation
            Arguments:
                x -- input dataset(in shape(inputSize,numOfSamples))
                parameters -- weight and bias
                activations -- a list of activation methods
            Returns:
                cache_dicts -- contains all outputes of wx+b, outputes of activation, weightes, and biases
                keys(z,a,W,b)
        '''
        cache_dicts = {}
        cache_dicts['a0'] = x
        a = x
        for i in range (len(parameters)//2):
            z = np.dot(parameters["W"+str(i+1)],a) + parameters["b"+str(i+1)]   # linear
            cache_dicts["z"+str(i+1)] = z          # append output of wx+b
            # z to activation function
            if(activations[i] == 'relu'):
                a = relu(z)
                cache_dicts["a"+str(i+1)] = a
            if(activations[i] == 'tanh'):
                a = tanh(z)
                cache_dicts["a"+str(i+1)] = a
            if(activations[i] == 'softmax'):
                a = softmax(z)
                cache_dicts["a"+str(i+1)] = a
            cache_dicts["W"+str(i+1)] = parameters["W"+str(i+1)]
            cache_dicts["b"+str(i+1)] = parameters["b"+str(i+1)]

        return cache_dicts["a"+str(len(parameters)//2)],cache_dicts

    def __backward_propagation(self,x,y,cache_dicts,activations):
        '''
            This function is for the backward propagation
            Arguments:
                x -- input dataset(in shape(inputSize,numOfSamples))
                y -- ground truth
                cache_dicts -- cache_dicts output from forward propagation
                activations -- a list of activation methods
            Returns:
                gradients -- a gradient dictionary
        '''
        gradients = {}
        for i in range(len(activations)-1,-1,-1):
            if(activations[i] == 'softmax'):
                dz = cross_entropy_softmax(cache_dicts["a"+str(i+1)],y)
                dW = np.dot(dz, cache_dicts["a"+str(i)].T)
                db = np.sum(dz,axis=1, keepdims=True)
                gradients["dz"+str(i+1)] = dz
                gradients["dW"+str(i+1)] = dW
                gradients["db"+str(i+1)] = db
            if(activations[i] == 'relu'):
                da = np.dot(cache_dicts["W"+str(i+2)].T,gradients["dz"+str(i+2)])
                dz = np.multiply(da,relu_backward(cache_dicts["a"+str(i+1)]))
                dW = np.dot(dz, cache_dicts["a"+str(i)].T)
                db = np.sum(dz,axis=1, keepdims=True)
                gradients["da"+str(i+1)] = da
                gradients["dz"+str(i+1)] = dz
                gradients["dW"+str(i+1)] = dW
                gradients["db"+str(i+1)] = db
            if(activations[i] == 'tanh'):
                da = np.dot(cache_dicts["W"+str(i+2)].T,gradients["dz"+str(i+2)])
                dz = np.multiply(da,tanh_backward(cache_dicts["a"+str(i+1)]))
                dW = np.dot(dz, cache_dicts["a"+str(i)].T)
                db = np.sum(dz,axis=1, keepdims=True)
                gradients["da"+str(i+1)] = da
                gradients["dz"+str(i+1)] = dz
                gradients["dW"+str(i+1)] = dW
                gradients["db"+str(i+1)] = db

        return gradients

    '''
        prediction method
    '''
    def predict(self,x,parameters,activations):
        '''
            Predict the label of a single test example (image).
            Arguments:
                x : numpy.array
            Returns: int
                Predicted label of example (image).
        '''
        a3, _ = self.__forward_propagation(x,parameters,activations)
        return a3

    '''
        Validation method
    '''
    def validation(self,x,y,parameters,activations):
        predict_result = self.predict(x,parameters,activations)
        numCorrect = 0
        y = y.T
        predict_result = predict_result.T
        for i in range(y.shape[0]):
            if(np.argmax(y[i]) == np.argmax(predict_result[i])):
                numCorrect = numCorrect + 1
        return numCorrect/y.shape[0]

    '''
        Training accuracy
    '''
    def train_accuracy(self,train_result,groundTruth):
        numCorrect = 0
        groundTruth = groundTruth.T
        train_result = train_result.T
        for i in range(groundTruth.shape[0]):
            if(np.argmax(groundTruth[i]) == np.argmax(train_result[i])):
                numCorrect = numCorrect + 1

        return numCorrect/groundTruth.shape[0]

    '''
        Training process
    '''
    def model_train(self, X, Y, x_val, y_val, layersDims, activations, initialization, optimizer, learning_rate = 0.0007, learning_rate_decay = True, mini_batch_size = 100, beta = 0.9,
            beta1 = 0.9, beta2 = 0.999, epsilon = 1e-8, num_epochs = 50, verbose = True):
        '''
        '''
        # L = len(layersDims)       # number of layers in nn
        train_accuracies = []       # list to track the train accuracies
        val_accuracies = []         # list to track the val accuracies
        t = 0                       # adam t parameter
        seed = 10
        self.activations_functions = activations
        self.number_of_layers = layersDims
        self.__optimizer = optimizer
        self.__learning_rate = learning_rate
        self.__learning_rate_decay = learning_rate_decay
        self.__beta1 = beta1
        self.__beta2 = beta2
        self.__epsilon = epsilon

        # Initialize parameters
        self.parameters = parameters_init(layersDims,initialization)

        # Initialize the optimizer
        if optimizer == "gd":
            pass # no initialization required for gradient descent
        elif optimizer == "momentum":
            v = momentum_init(self.parameters)
        elif optimizer == "adam":
            v, s = adam_init(self.parameters)

        for epoch in range(num_epochs):
            # Define the random minibatches.
            seed = seed + 1
            minibatches = random_mini_batches(X, Y, mini_batch_size, seed)

            if(learning_rate_decay and epoch == num_epochs//2):
                learning_rate = learning_rate/2

            for minibatch in minibatches:

                # Select a minibatch
                (minibatch_X, minibatch_Y) = minibatch

                # Forward propagation
                train_result, caches = self.__forward_propagation(minibatch_X, self.parameters,activations)

                # Compute cost
                cost = crossEntropy_cost(train_result, minibatch_Y)

                # Backward propagation
                grads = self.__backward_propagation(minibatch_X, minibatch_Y, caches, activations)

                # Update parameters
                if optimizer == "gd":
                    self.parameters = update_parameters_gd(self.parameters, grads, learning_rate)
                elif optimizer == "momentum":
                    self.parameters, v = update_parameters_momentum(self.parameters, grads, v, beta, learning_rate)
                elif optimizer == "adam":
                    t = t + 1 # Adam counter
                    self.parameters, v, s = update_parameters_adam(self.parameters, grads, v, s,
                                                                t, learning_rate, beta1, beta2, epsilon)

            # Calculate train_accuracy and val_accuracy after each epoch
            train_accuracy = self.validation(X,Y,self.parameters,activations)
            train_accuracies.append(train_accuracy)
            val_accuracy = self.validation(x_val,y_val,self.parameters,activations)
            val_accuracies.append(val_accuracy)
            self.costs.append(cost)

            if(verbose):
                # Print the cost
                print("Epoch %i/%i"%(epoch,num_epochs))
                print("-loss: %f - training_acc: %f - validation_acc: %f"%(cost,train_accuracy,val_accuracy))

        history = {}
        history['train_accuracies'] = train_accuracies
        history['val_accuracies'] = val_accuracies

        return history

    def save_model(self, filename='model.npy'):
        """
        """
        model_dictionary = {'parameters':self.parameters, 'activations':self.activations_functions, 'num_layers':self.number_of_layers}
        np.save(filename,model_dictionary)

    def load_model(self, filename='model.npy'):
        """
        """
        nnModel = np.load(filename).item()
        self.parameters = nnModel['parameters']
        self.activations_functions = nnModel['activations']
        self.number_of_layers = nnModel['num_layers']


# -*- coding: utf-8 -*-

import collections
import contextlib
import copy
import gevent
import gevent.subprocess
import itertools
import json
import logging
import re

from .exceptions import UnkownImageError, DockerCommandError, DockerNotFoundError
from .utils import bytes_to_human

class _CatchDockerError(object):
    def __enter__(self):
        pass
    def __exit__(self, exc_type, exc_value, exc_traceback):
        if exc_type is gevent.subprocess.CalledProcessError:
            if exc_value.returncode == 127:
                raise DockerNotFoundError()
            raise DockerCommandError(exc_value.output)
        return False

class Container(object):
    """Containers are transitions between two images.

    :param revpsec: the :class:`ImageRevSpec` of the image to use.
    :param commit_as: When :method:`run` is called, commit the resulting image
                      as this given :class:`ImageRevSpec`.
    :attr image: the image that will be used to start the container.
    :attr result: the revspec of the image commited when run finishes.
    :attr logs: the logs from the container when run finishes.

    .. note::

    :attr:`logs` and attr:`result` are only available once the :method:`run`
    has successfully finished.
    """

    PIPE = gevent.subprocess.PIPE
    STDOUT = gevent.subprocess.STDOUT

    def __init__(self, image, commit_as=None):
        self.image = image
        self.result = None
        self.logs = None
        self.commit_as = commit_as
        self._id = None

    def install_system_packages(self, packages):
        # XXX: pay attention to the tricks done in snapshots/worker.py
        pass

    # XXX: Maybe this should be named to something else to better reflect the
    # fact that it's really an authoring tool, and reduce the confusion with
    # run_stream_logs.
    @contextlib.contextmanager
    def run(self, cmd, as_user=None, stdin=None, stdout=None, stderr=None):
        """Run the specified command in a new container.

        This is a context manager that returns a :class:`subprocess.Popen`
        instance. When the context manager exit a new image is commited from
        the container and the container is automatically destroyed.

        The new image can be accessed via the :attr:`result` attribute.

        :param cmd: the program to run as a list of arguments.
        :param as_user: run the command under this username or uid.
        :param stdout, stderr: as in :class:`subprocess.Popen` except that you
                               should use Container.PIPE and Container.STDOUT
                               instead of subprocess.PIPE and subprocess.STDOUT.
        :param stdin: either None (close stdin) or Container.PIPE.
        :return: Nothing (this is a context manager) but sets :attr:`result`
                 with the class:`ImageRevSpec` of the resulting image.

        .. warning::

        stdout and stderr currently don't work due to limitations on Docker:
        you can't get the id of container, in a race-condition free way, with
        stdout and stderr enabled on docker run. A workaround would be to start
        a shell in detached mode and then execute the command from docker
        attach, but docker attach doesn't exit correctly when the process exits
        (it hangs on stdin until you try to write something which trigger an
        EBADF in docker).
        """

        logging.debug("Starting {0} in a {1} container as user {2}".format(
            cmd, self.image, as_user or "root"
        ))

        as_user = ["-u", as_user] if as_user else []

        try:
            # If stdin is None, start the container in detached mode, this will
            # print the id on stdout, then we simply wait for the container to
            # stop. If stdin is not None, start the container in attached mode
            # without stdout and stderr. This will print the container id on
            # stdout so we can read it.
            if stdin is None:
                with _CatchDockerError():
                    self._id = gevent.subprocess.check_output(
                        ["docker", "run", "-d"] + as_user
                        + [self.image.revision] + cmd
                    ).strip()
                # docker wait prints the number of second we waited, ignore it:
                with open("/dev/null", "w") as ignore:
                    docker = gevent.subprocess.Popen(
                        ["docker", "wait", self._id], stdout=ignore
                    )
            else:
                docker = gevent.subprocess.Popen(
                    ["docker", "run", "-i", "-a", "stdin"]
                    + as_user + [self.image.revision] + cmd,
                    stdin=stdin,
                    stdout=self.PIPE
                )
                # readline instead of read is important here, the object behind
                # docker.stdout is actually a socket._fileobject (yes, the real
                # socket module from Python) and its read method returns when
                # its buffer (8192 bytes by default on Python 2.7) is full or
                # when EOF is reached, not when the underlying read system call
                # returns.
                self._id = docker.stdout.readline().strip()
            logging.debug("Started container {0} from {1}".format(
                self._id, self.image
            ))

            yield docker

            # Wait for the process to terminate (if the calling code didn't
            # already do it):
            logging.debug("Waiting for container {0} to terminate".format(
                self._id
            ))
            docker.wait()
            logging.debug("Container {0} stopped".format(self._id))

            # Since we can't get stdout/stderr in realtime for now (see the
            # docstring), let's get the logs instead.
            logs = gevent.subprocess.Popen(
                ["docker", "logs", self._id],
                stdout=self.PIPE,
                stderr=self.STDOUT
            )
            logs = gevent.spawn(logs.communicate)

            # Commit a new image from the container
            username = repository = tag = None
            commit = ["docker", "commit", self._id]
            if self.commit_as:
                username = self.commit_as.username
                repository = self.commit_as.repository
                tag = self.commit_as.tag
                if self.commit_as.fqrn:
                    commit.append(self.commit_as.fqrn)
                if tag:
                    commit.append(tag)
            elif self.image.fqrn:
                username = self.image.username
                repository = self.image.repository
                tag = self.image.tag
                commit.append(self.image.fqrn)
                if repository and tag == "latest":
                    commit.append("latest")
            with _CatchDockerError():
                revision = gevent.subprocess.check_output(
                    commit, stderr=self.STDOUT
                ).strip()
            self.result = Image(ImageRevSpec(username, repository, revision, tag))
            logging.debug("Container {0} started from {1} commited as image {2}".format(
                self._id, self.image, self.result
            ))

            logging.debug("Fetching logs from container {0}".format(self._id))
            logs.join()
            with _CatchDockerError():
                # if we raise here, self.logs will stay at None which is wanted
                self.logs = logs.get()[0]
            logging.debug("{0} of logs fetched from container {1}".format(
                bytes_to_human(len(self.logs)), self._id
            ))
        finally:
            if self._id:
                # Destroy the container
                logging.debug("Destroying container {0}".format(self._id))
                with _CatchDockerError():
                    gevent.subprocess.check_call(["docker", "rm", self._id])
                logging.debug("Container {0} destroyed".format(self._id))

    @contextlib.contextmanager
    def run_stream_logs(self, cmd, as_user=None, ports=[], env={}, output=None):
        """Run the specified command and wait for it, logs are streamed.

        This is a context manager that yields a :class:`subprocess.Popen`
        instance. When the context manager exits, it automatically waits for
        the docker command to terminate (if you didn't do it already). The
        object yielded will expose a `ports` attribute which is a dict with the
        ports you defined as keys and the ports they got mapped to, on the host
        public address, as values.

        .. note:: stdout and stderr will be mixed in the logs output, this is
                  currently a limitation of Docker.

        :param cmd: the program to run as a list of arguments.
        :param as_user: run the command under this username or uid.
        :param ports: list of ports in the container to expose on the host.
        :param env: define additional environment variables.
        :param output: stream the logs to this file object or fd (by default
                       they are streamed to stdout), it can also be
                       Container.PIPE.

        .. warning:: due to limitations in Docker (see :method:`run`), the
                     first lines of output might be lost.
        """

        logging.debug("Starting {0} in a {1} container as user {2}".format(
            cmd, self.image, as_user or "root"
        ))

        as_user = ["-u", as_user] if as_user else []
        ports = [str(p) for p in ports]
        ports = list(
            itertools.chain.from_iterable(itertools.product(["-p"], ports))
        )
        env = ["{0}={1}".format(k, v) for k, v in env.iteritems()]
        env = list(
            itertools.chain.from_iterable(itertools.product(["-e"], env))
        )
        with _CatchDockerError():
            self._id = gevent.subprocess.check_output(
                ["docker", "run", "-d"] + as_user + env
                + ports + [self.image.revision] + cmd
            ).strip()
            docker = gevent.subprocess.Popen(
                ["docker", "attach", self._id],
                stdout=output, stderr=self.STDOUT
            )
            container_infos = json.loads(gevent.subprocess.check_output(
                ["docker", "inspect", self._id]
            ).strip())
            port_mapping = container_infos['NetworkSettings']['PortMapping']
            docker.ports = {int(k): int(v) for k, v in port_mapping.iteritems()}

        yield docker

        logging.debug("Waiting for container {0} to terminate".format(
            self._id
        ))
        docker.wait()
        logging.debug("Container {0} stopped".format(self._id))

_ImageRevSpec = collections.namedtuple(
    "_ImageRevSpec", ["username", "repository", "revision", "tag"]
)
class ImageRevSpec(_ImageRevSpec):
    """Human representation of an image revision in Docker."""

    def __str__(self):
        s = ""
        if self.username:
            s += "{0}/".format(self.username)
        if self.repository:
            s += self.repository
        if self.tag:
            s += ":{0}".format(self.tag)
            if self.revision:
                s += " ({0})".format(self.revision)
        else:
            if s:
                s += ":"
            s += self.revision
        return s

    # Compare tags or revisions as needed (This allows us to seamlessly resolve
    # a tag into a revision in Image.__init__):
    def __eq__(self, other):
        if self.username == other.username and \
            self.repository == other.repository:
                if not self.revision or not other.revision:
                    return self.tag == other.tag
                return self.revision == other.revision
        return False

    def __ne__(self, other):
        return not self.__eq__(other)

    @staticmethod
    def _is_revision(rev):
        return re.match("^({0}{{12}}|{0}{{64}})$".format("[0-9a-fA-F]"), rev)

    @staticmethod
    def _parse_user_and_repo(username_and_repo):
        username = repository = None
        user_separator = username_and_repo.find("/")
        if user_separator != 0:
            if username_and_repo and user_separator != -1:
                username = username_and_repo[:user_separator]
                repository = username_and_repo[user_separator + 1:]
            elif username_and_repo == "<none>":
                repository = None
            else:
                repository = username_and_repo

        return username, repository

    @property
    def fqrn(self):
        """"Fully Qualified Repository Name"

        :return: the string username/repository if both are set, repository if
                 the username is missing, or None if everything is missing.
        """

        if self.username and self.repository:
            return "{0}/{1}".format(self.username, self.repository)
        return self.repository

    @classmethod
    def parse(cls, revspec):
        username = repository = revision = tag = username_and_repo = None
        revspec_len = len(revspec)
        rev_separator = revspec.find(":")
        user_separator = revspec.find("/")

        if user_separator == revspec_len - 1:
            raise ValueError("Invalid image: {0} (missing repository)".format(
                revspec
            ))
        if rev_separator == revspec_len - 1:
            raise ValueError("Invalid image: {0} (missing revision)".format(
                revspec
            ))

        if rev_separator != -1:
            rev_or_tag = revspec[rev_separator + 1:]
            if cls._is_revision(rev_or_tag):
                revision = rev_or_tag
            else:
                tag = rev_or_tag
            if rev_separator > 0:
                username_and_repo = revspec[:rev_separator]
        elif not cls._is_revision(revspec):
            username_and_repo = revspec
            tag = "latest"
        else:
            revision = revspec
        if username_and_repo:
            username, repository = cls._parse_user_and_repo(username_and_repo)
            if username is None and repository is None:
                raise ValueError("Invalid image: {0} (missing username)".format(
                    revspec
                ))

        if tag and not username_and_repo:
            raise ValueError(
                "Invalid image: {0} (tag without repository)".format(revspec)
            )

        return cls(username, repository, revision, tag)

    @classmethod
    def parse_from_docker(cls, revspec):
        username = repository = revision = tag = None

        if revspec:
            parts = re.split("\s+", revspec, maxsplit=3)
            parts_len = len(parts)
            if not revspec[0].isspace(): # we have an username and/or repo
                username, repository = cls._parse_user_and_repo(parts[0])
                if parts_len >= 2:
                    if cls._is_revision(parts[1]):
                        revision = parts[1]
                    elif parts_len >= 2:
                        tag = None if parts[1] == "<none>" else parts[1]
                        revision = parts[2]
            elif parts_len > 1:
                revision = parts[1]

        if revision and cls._is_revision(revision):
            return cls(username, repository, revision, tag)

        raise ValueError(
            "Invalid image: {0} (can't find the revision)".format(revspec)
        )

class Image(object):
    """Represent an image in Docker. Can be used to start a :class:`Container`.

    :param revspec: :class:`ImageRevSpec` that identify a specific image version.
    :raise UnkownImageError: if the image is not know from the local Docker.
    """

    def __init__(self, revspec):
        logging.debug("Looking for {0} in docker images".format(revspec))
        with _CatchDockerError():
            images = gevent.subprocess.check_output(
                ["docker", "images"], stderr=gevent.subprocess.STDOUT
            ).splitlines()[1:]
        docker_revspecs = []
        for line in images:
            try:
                docker_revspecs.append(ImageRevSpec.parse_from_docker(line))
            except ValueError as ex:
                logging.warning(str(ex))
        # check that the image exists in Docker, and if so save it (it will
        # have the revision, which might not be the case of the revspec
        # received in argument).
        for docker_revspec in docker_revspecs:
            if revspec == docker_revspec:
                self.revspec = docker_revspec
                return
        raise UnkownImageError(
            "The image {0} doesn't exist "
            "(maybe you need to pull it in Docker?)".format(revspec)
        )

    def __str__(self):
        return self.revspec.__str__()

    def __repr__(self):
        return "<{0}(revspec={1}) at {2:#x}>".format(
            self.__class__.__name__, repr(self.revspec), id(self)
        )

    def __getattr__(self, name):
        if name in ["username", "repository", "revision", "tag", "fqrn"]:
            return getattr(self.revspec, name)
        raise AttributeError("'{0}' object has no attribute '{1}'".format(
            self.__class__.__name__, name
        ))

    # NOTE: This is not perfect: if you instantiate several Image object for
    # the same revision and destroy one of them, the others become invalid, but
    # it will not be catched by this.
    def _check_exists(method):
        def wrapped(self, *args, **kwargs):
            if not self.revspec:
                raise UnkownImageError(
                    "You tried to {0} a destroyed image".format(method.__name__)
                )
            return method(self, *args, **kwargs)
        return wrapped

    @_check_exists
    def instantiate(self, *args, **kwargs):
        return Container(self, *args, **kwargs)

    @_check_exists
    def destroy(self):
        """Remove the image from Docker.

        .. warning:: Once you have called this method the current object is
                     invalidated and you can't call further method on it. If
                     you have multiple :class:`Image` objects pointing to the
                     same revision (but with different tags for example), and
                     destroy one of them, then the others will become invalid
                     too.
        """

        logging.debug("Destroying image {0} from Docker".format(self.revspec))
        with _CatchDockerError():
            gevent.subprocess.check_call([
                "docker", "rmi", self.revspec.revision
            ])
        self.revspec = None

    @_check_exists
    def add_tag(self, tag):
        logging.debug("Tagging {0} as {1}".format(self.revspec, tag))
        with _CatchDockerError():
            gevent.subprocess.check_call([
                "docker", "tag", self.revspec.revision, self.revspec.fqrn, tag
            ])
        # We can't reinstantiate an Image object, because it might resolve to
        # the wrong revspec when it parses the output of docker images (and
        # it's slower anyway):
        new_image = copy.copy(self)
        new_image.revspec = ImageRevSpec(*(self.revspec[:-1] + (tag,)))
        return new_image


#!/usr/bin/env python
# CloudFlare service mode. This enables/disables CF's traffic acceleration.
# Enabled (orange cloud) is 1. Disabled (grey cloud) is 0.
# cf_service_mode: 1
#
# CloudFlare DDNS script.
#
# usage:
#   ddns.py domain subdomain cf_service_mode
#   ddns.py test.local sub.test.local 1
# See README for details
#

import requests
import json
import time
import os
import sys
from subprocess import Popen, PIPE

# CloudFlare api url.
CLOUDFLARE_URL = 'https://api.cloudflare.com/client/v4/'

# Time-to-live for your A record. This should be as small as possible to ensure
# changes aren't cached for too long and are propogated quickly.  CloudFlare's
# api docs set a minimum of 120 seconds.
TTL = '120'

# DNS record type for your DDNS host. Probably an A record.
RECORD_TYPE = 'A'

def main():
  cf_key = os.environ['CLOUDFLARE_KEY']
  cf_email = os.environ['CLOUDFLARE_EMAIL']
  cf_domain = sys.argv[1]
  cf_subdomain = sys.argv[2]
  cf_service_mode = sys.argv[3]
  quiet = False
  use_dig = False

  auth_headers = {
    'X-Auth-Key': cf_key,
    'X-Auth-Email': cf_email,
    'Content-Type': 'application/json'
  }

  log('info', "DDNS for domain: {} in subdomain {}".format(cf_domain, cf_subdomain))
  public_ip = requests.get("https://ipv4.icanhazip.com/").text.strip()
  log('info', "Current ip is: {}".format(public_ip))
  ### Get zone id for the dns record we want to update
  results = get_paginated_results(
    'GET',
    CLOUDFLARE_URL + '/zones',
    auth_headers,
  )
  cf_zone_id = None
  for zone in results:
    zone_name = zone['name']
    zone_id = zone['id']
    if zone_name == cf_domain:
      cf_zone_id = zone_id
      break
  if cf_zone_id is None:
    raise Exception("Snap, can't find zone '{}'".format(cf_domain))

  ### Get record id for the record we want to update
  if cf_subdomain == '':
    target_name = cf_domain
  else:
    target_name = cf_subdomain + '.' + cf_domain
  results = get_paginated_results(
    'GET',
    CLOUDFLARE_URL + '/zones/' + cf_zone_id + '/dns_records',
    auth_headers,
  )
  cf_record_obj = None
  for record in results:
    record_id = record['id']
    record_name = record['name']
    if record_name == target_name:
      cf_record_obj = record
      break
  if cf_record_obj is None:
    raise Exception("Snap, can't find record '{}'".format(target_name))

  if not quiet:
    print(json.dumps(cf_record_obj, indent=4))

  ### Update the record
  current_record_ip = cf_record_obj['content']
  if current_record_ip == public_ip:
    # If this record already has the correct IP, we return early
    # and don't do anything.
    if not quiet:
      log('unchanged', '{}, {}'.format(target_name, public_ip))
    return

  cf_record_obj['content'] = public_ip
  r = requests.put(
    CLOUDFLARE_URL
      + '/zones/'
      + cf_zone_id
      + '/dns_records/'
      + cf_record_obj['id'],
    headers=auth_headers,
    json=cf_record_obj
  )
  status_was_error = False
  if r.status_code < 200 or r.status_code > 299:
    log(
      'error',
      "CloudFlare returned an unexpected status code: {}, for "
      "dns_records update request."
      .format(r.status_code)
    )
    status_was_error = True
  response = r.json()
  if response["errors"] or status_was_error:
    die("Updating record failed with the response: '{}'".format(
      json.dumps(response)
    ))
  else:
    log('updated', "{}, {}".format(target_name, public_ip))

  return

def die(msg):
  log('error', msg)
  raise Exception(msg)

def get_paginated_results(method, url, auth_headers):
  """
  Executes the cloudflare api call, fetches all pages and returns the
  concatenated result array.
  """

  results = []
  page = 0
  total_pages = None
  while page != total_pages:
    page += 1
    r = requests.request(
      method,
      url,
      params={ 'page': page },
      headers=auth_headers
    )

    if r.status_code < 200 or r.status_code > 299:
      die(
        "CloudFlare returned an unexpected status code: {}, for "
        "request: {} {}"
        .format(
          r.status_code,
          url,
          r.text
        )
      )

    response = r.json()
    results.extend(response['result'])
    total_pages = response['result_info']['total_pages']
  return results

# TODO use a real logging framework.
def log(status, message=''):
  print(
    "{date}, {status:>10}, '{message}'".format(
      date=time.ctime(),
      status=status,
      message=message
    )
  )
  return

if __name__ == '__main__':
  main()


from src.model.database import Database

def get_users():
    db = Database('data/database.db')
    users = db.select_all_users()
    table = []
    for user in users:
        table.append([
            str(user.user_id), str(user.email), str(user.first_name),
            str(user.last_name), str(user.orders)
        ])
    db.close()
    return table if len(table) > 1 else table + []

def get_items():
    db = Database('data/database.db')
    items = db.select_all_sales_items()
    table = []
    for item in items:
        table.append([
            str(item.item_id), str(item.name), str(item.stock), '$'+str(item.price),
            str(item.department_id)
        ])
    db.close()
    return table if len(table) > 1 else table + []

def get_orders():
    db = Database('data/database.db')
    orders = db.select_all_orders()
    table = []
    for order in orders:
        table.append([
            str(order.order_id), str(order.date), str(order.customer_id), '$'+str(order.total),
            str(order.is_complete), str(order.sales_items)
        ])
    db.close()
    return table

def get_discounts():
    db = Database('data/database.db')
    discounts = db.select_all_discounts()
    table = []
    for discount in discounts:
        table.append([
            str(discount.phrase), '$'+str(discount.amount)
        ])
    db.close()
    return table

# def update_sales_item(sales_item_id, **kwargs):
#     try:
#         sales_item = SalesItem.query.get(sales_item_id)
#         if not sales_item:
#             print(f"Sales item with id {sales_item_id} not found")
#             return None

#         for key, value in kwargs.items():
#             if hasattr(sales_item, key):
#                 setattr(sales_item, key, value)
#             else:
#                 print(f"Sales item does not have attribute {key}")
#         db.session.commit()
#     except Exception as e:
#         print(f"An error occurred while updating sales item: {str(e)}")
#         return None

# def update_user(user_id, **kwargs):
#     try:
#         user = User.query.get(user_id)
#         if not user:
#             print(f"User with id {user_id} not found")
#             return None

#         for key, value in kwargs.items():
#             if hasattr(user, key):
#                 setattr(user, key, value)
#             else:
#                 print(f"User does not have attribute {key}")
#         db.session.commit()
#     except Exception as e:
#         print(f"An error occurred while updating user: {str(e)}")
#         return None

# def create_discount_code(code, discount_amount):
#     try:
#         discount_code = DiscountCode(code=code, discount_amount=discount_amount)
#         db.session.add(discount_code)
#         db.session.commit()
#         return discount_code.id
#     except Exception as e:
#         print(f"An error occurred while creating discount code: {str(e)}")
#         return None

# def create_sales_item(name, price, image, quantity):
#     try:
#         sales_item = SalesItem(name=name, price=price, image=image, quantity=quantity)
#         db.session.add(sales_item)
#         db.session.commit()
#         return sales_item.id
#     except Exception as e:
#         print(f"An error occurred while creating sales item: {str(e)}")
#         return None


from itertools import groupby

class Solution:
    def countAndSay(self, n: int) -> str:
        memo = ["1"]
        for i in range(1, n):
            num = self._calc(memo[i-1])
            memo.append(num)

        return memo[n-1]

    def _calc(self, seq: str) -> str:
        result = ""
        for num, group in groupby(seq):
            count = len(list(group))
            result += f"{count}{num}"

        return result


codigoCidade = int(input("Digite o código da cidade: "))
quantVeiculos = int(input("Digite o número de veículos na cidade: "))
quantAcidentes = int(input("Digite o número de acidentes com vitimas da cidade: "))
print("")

indiceAcidente = float(quantAcidentes) / quantVeiculos
maiorIndice = indiceAcidente
maiorIndiceCodigo = codigoCidade
menorIndice = indiceAcidente
menorIndiceCodigo = codigoCidade
soma = quantVeiculos
somaVeiculos99 = 0
divisorMedia99 = 1

if (quantVeiculos < 2000):
	somaVeiculos99 = somaVeiculos99 + quantAcidentes
	divisorMedia99 = divisorMedia99 + 1

a = 1
while (a < 5):
	codigoCidade = int(input("Digite o código da cidade: "))
	quantVeiculos = int(input("Digite o número de veículos na cidade: "))
	quantAcidentes = int(input("Digite o número de acidentes com vítimas da cidade: "))
	print("")
	indiceAcidente = float(quantAcidentes) / quantVeiculos
	soma = soma + quantVeiculos

	if (indiceAcidente > maiorIndice):
		maiorIndice = indiceAcidente
		maiorIndiceCodigo = codigoCidade

	if (indiceAcidente < menorIndice):
		menorIndice = indiceAcidente
		menorIndiceCodigo = codigoCidade

	if (quantVeiculos < 2000):
		somaVeiculos2000 = somaVeiculos99 + quantAcidentes
		divisorMedia2000 = divisorMedia99 + 1

	a = a + 1

print("\nMenor índice: %.2f \nCódigo da cidade: %d" % (menorIndice, menorIndiceCodigo))
print("\nMaior índice: %.2f \nCódigo da cidade: %d" % (maiorIndice, maiorIndiceCodigo))
print("Média de veículos nas %d cidades = %d veículos" % (a,(float(soma)/a)))
print("Média de acidentes em cidades com menos de 2000 veículos: %.1f" % (float(somaVeiculos99)/divisorMedia99))


from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.views.generic import TemplateView

import home.urls

urlpatterns = patterns('',
    # Examples:
    # url(r'^$', 'hisquare.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),
    url(r'^hisquare/', include(home.urls, namespace='home')),

    # Angular GET urls
    url(r'^get/categories/$', home.views.get_all_categories),
    url(r'^get/events/$', home.views.get_all_events),
    url(r'^get/about/$', home.views.get_about_page),
    url(r'^get/merchants/(?P<catcode>[-_\w]+)/$', home.views.get_merchants_by_category),
    url(r'^get/merchant/(?P<m_id>\d+)/$', home.views.get_merchant_by_id),

    # Angular templates
    url(r'^pages/events/$', TemplateView.as_view(template_name="angularTemplates/events.html")),
    url(r'^pages/about/$', TemplateView.as_view(template_name="angularTemplates/about.html")),
    url(r'^pages/merchants/$', TemplateView.as_view(template_name="angularTemplates/merchants.html")),
    url(r'^pages/details/$', TemplateView.as_view(template_name="angularTemplates/details.html")),
    url(r'^pages/sfform/$', TemplateView.as_view(template_name="angularTemplates/sfform.html")),

    # Redirect
    url(r'^$', home.views.index, name='index'),

    # Admin Urls
    url(r'^hsma-admin/', include(admin.site.urls)),
)

# Below only works in dev server
from django.conf.urls.static import static
from hisquare import settings
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)


import numpy as np
import requests
import cv2
import time
from threading import Thread

def set_velocity(vel0, vel1):
    r = requests.get("http://localhost:8080/robot/set/velocity?value="+str(vel0)+","+str(vel1))

class VideoStreamWidget(object):
    def __init__(self, src=0):
        self.capture = cv2.VideoCapture(src)
        print('Opened capture, start thread')
        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()

    def update(self):
        # Read the next frame from the stream in a different thread
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()
            time.sleep(.01)

    def show_frame(self):
        # Display frames in main program
        cv2.imshow('frame', self.frame)
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            cv2.destroyAllWindows()
            exit(1)


import numpy as np
import torch
from torch.utils.data.sampler import BatchSampler, SubsetRandomSampler

class RolloutStorage(object):
    def __init__(self, num_steps, num_processes, obs_shape, action_space, state_size, gamma, use_rp=False):
        self.observations = torch.zeros(num_steps + 1, num_processes, *obs_shape)
        self.states = torch.zeros(num_steps + 1, num_processes, state_size)
        self.use_rp = use_rp
        if not use_rp:
            self.rewards = torch.zeros(num_steps, num_processes, 1)
        else:
            self.rewards = torch.zeros(num_steps, num_processes, 2)
        self.raw_rewards = torch.zeros(num_steps, num_processes, 1)
        self.value_preds = torch.zeros(num_steps + 1, num_processes, len(gamma))
        self.returns = torch.zeros(num_steps + 1, num_processes, len(gamma))
        self.action_log_probs = torch.zeros(num_steps, num_processes, 1)
        if action_space.__class__.__name__ == 'Discrete':
            action_shape = 1
        else:
            action_shape = action_space.shape[0]
        self.actions = torch.zeros(num_steps, num_processes, action_shape)
        if action_space.__class__.__name__ == 'Discrete':
            self.actions = self.actions.long()
        self.masks = torch.ones(num_steps + 1, num_processes, 1)
        self.gamma = torch.from_numpy(np.array(gamma)).type(self.returns.type())
        self.gamma = self.gamma.view(1, -1).expand((num_processes, len(gamma)))

    def cuda(self):
        self.observations = self.observations.cuda()
        self.states = self.states.cuda()
        self.rewards = self.rewards.cuda()
        self.raw_rewards = self.raw_rewards.cuda()
        self.value_preds = self.value_preds.cuda()
        self.returns = self.returns.cuda()
        self.action_log_probs = self.action_log_probs.cuda()
        self.actions = self.actions.cuda()
        self.masks = self.masks.cuda()
        self.gamma = self.gamma.cuda()

    def insert(self, step, current_obs, state, action, action_log_prob, value_pred, reward, mask, raw_reward):
        self.observations[step + 1].copy_(current_obs)
        self.states[step + 1].copy_(state)
        self.actions[step].copy_(action)
        self.action_log_probs[step].copy_(action_log_prob)
        self.value_preds[step].copy_(value_pred)
        self.rewards[step].copy_(reward)
        self.masks[step + 1].copy_(mask)
        self.raw_rewards[step].copy_(raw_reward)

    def after_update(self):
        self.observations[0].copy_(self.observations[-1])
        self.states[0].copy_(self.states[-1])
        self.masks[0].copy_(self.masks[-1])

    def compute_returns(self, next_value, use_gae, tau):
        if use_gae:
            self.value_preds[-1] = next_value
            gae = 0
            for step in reversed(range(self.rewards.size(0))):
                delta = self.rewards[step] + self.gamma * self.value_preds[step + 1] * self.masks[step + 1] - \
                        self.value_preds[step]
                gae = delta + self.gamma * tau * self.masks[step + 1] * gae
                self.returns[step] = gae + self.value_preds[step]
        else:
            self.returns[-1] = next_value
            for step in reversed(range(self.rewards.size(0))):
                self.returns[step] = self.returns[step + 1] * \
                                     self.gamma * self.masks[step + 1].expand_as(self.gamma) + \
                                     self.rewards[step].expand_as(self.gamma)

    def feed_forward_generator(self, advantages, num_mini_batch):
        num_steps, num_processes = self.rewards.size()[0:2]
        batch_size = num_processes * num_steps
        mini_batch_size = batch_size // num_mini_batch
        sampler = BatchSampler(SubsetRandomSampler(range(batch_size)), mini_batch_size, drop_last=False)
        for indices in sampler:
            indices = torch.LongTensor(indices)

            if advantages.is_cuda:
                indices = indices.cuda()

            observations_batch = self.observations[:-1].view(-1,
                                        *self.observations.size()[2:])[indices]
            observations_batch_prime = self.observations[1:].view(-1,
                                        *self.observations.size()[2:])[indices]
            states_batch = self.states[:-1].view(-1, self.states.size(-1))[indices]
            actions_batch = self.actions.view(-1, self.actions.size(-1))[indices]
            return_batch = self.returns[:-1].view(-1, self.returns.shape[-1])[indices]
            masks_batch = self.masks[:-1].view(-1, 1)[indices]
            old_action_log_probs_batch = self.action_log_probs.view(-1, 1)[indices]
            adv_targ = advantages.view(-1, 1)[indices]
            true_rewards_batch = self.raw_rewards.view(-1, 1)[indices]
            yield observations_batch, states_batch, actions_batch, \
                return_batch, masks_batch, old_action_log_probs_batch, adv_targ, \
                observations_batch_prime, true_rewards_batch


from cProfile import label
import tensorflow as tf
from tensorflow import keras
from keras.losses import MeanAbsoluteError, MeanSquaredError
import csv
import pandas as pd
import matplotlib.pyplot as plt
from keras.preprocessing.text import Tokenizer
from keras.utils import pad_sequences
import numpy as np

labels_str = []
message = []
labels_int = []

#loads dataset
with open('spam.csv', 'r') as dataset:
    csv_reader = csv.reader(dataset)

    #splits dataset into two lists
    for line in csv_reader:
        labels_str.append(line[0])
        message.append(line[1])

    #creates a new list and adds 1s and 0s to it based on "ham"s and "spam"s in labels_str respectively
    for line in labels_str:
        if "ham" in line:
                labels_int.append(1)
        elif "spam" in line:
                labels_int.append(0)

labels_int = tf.convert_to_tensor(np.array(labels_int), dtype=tf.int32)

#prepares data to be used in the neural network
X_train = message[1:4460]
X_test = message[4460:5573]
y_train = labels_int[1:4460]
y_test = labels_int[4460:5573]
X_pred = ["reply to this message to recieve a free $200 iTunes gift card!"]
y_pred = 0

#generates tokenizerand configures it
tokenizer = Tokenizer(num_words= 20, oov_token="<OOV>")

#creates internal knowledge about vocabulary
tokenizer.fit_on_texts(X_train)
tokenizer.fit_on_texts(X_test)
tokenizer.fit_on_texts(X_pred)

#tokenises the test (assigns numerical values to every word)
word_index = tokenizer.word_index

#creates sequence of numbers which correlate to the words in the line
training_sequences = tokenizer.texts_to_sequences(X_train)
prediction_sequence = tokenizer.texts_to_sequences(X_pred)
testing_sequence = tokenizer.texts_to_sequences(X_test)

#adds '0s' to the end of the sequences to ensure that they are all equal length
training_padded = pad_sequences(training_sequences, padding='post')
testing_padded = pad_sequences(testing_sequence, padding='post')

#creates arrays for every value that will be used
training_padded = np.array(training_padded)
training_labels = np.array(y_train)
testing_padded = np.array(testing_padded)
testing_labels = np.array(y_test)
prediction_array = np.array(prediction_sequence)

#creating the model(s)
model1 = tf.keras.Sequential([
    tf.keras.layers.Embedding(999, 20),
    tf.keras.layers.GlobalAveragePooling1D(),
    tf.keras.layers.Dense(24, activation='relu'),
    tf.keras.layers.Dense(10, activation='sigmoid'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

model2 = tf.keras.Sequential([
    tf.keras.layers.Embedding(999, 20),
    tf.keras.layers.GlobalAveragePooling1D(),
    tf.keras.layers.Dense(50, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

model3 = tf.keras.Sequential([
    tf.keras.layers.Embedding(999, 20),
    tf.keras.layers.GlobalAveragePooling1D(),
    tf.keras.layers.Dense(24, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

#view the summary of the model
model1.summary()

#compiles the model (tells the model how wrong it is)
model1.compile(loss=tf.keras.losses.mae,
               optimizer=tf.keras.optimizers.SGD(),
               metrics=["mae"])

model2.compile(loss=tf.keras.losses.mae,
               optimizer=tf.keras.optimizers.SGD(),
               metrics=["mae"])

model3.compile(loss=tf.keras.losses.mae,
               optimizer=tf.keras.optimizers.SGD(),
               metrics=["mae"])

#trains the model on the data I have fed it
model1.fit(training_padded, training_labels, epochs=100)
model2.fit(training_padded, training_labels, epochs=100)
model3.fit(training_padded, training_labels, epochs=500)

#predict model performance
predicted_sequence1 = model1.predict(prediction_array)
predicted_sequence2 = model2.predict(prediction_array)
predicted_sequence3 = model3.predict(prediction_array)

print(predicted_sequence1, predicted_sequence2, predicted_sequence3)

#calculate mean absolute error and square errors of predictions
mae_1 = keras.losses.MAE(y_test, predicted_sequence1)
mse_1 = keras.losses.MSE(y_test, predicted_sequence1)
mae_2 = keras.losses.MAE(y_test, predicted_sequence2)
mse_2 = keras.losses.MSE(y_test, predicted_sequence2)
mae_3 = keras.losses.MAE(y_test, predicted_sequence3)
mse_3 = keras.losses.MSE(y_test, predicted_sequence3)

#prepare results into a nice list
model_results = [["model 1", mae_1.numpy(), mse_1.numpy()],
                 ["model 2", mae_2.numpy(), mse_2.numpy()],
                 ["model 3", mae_3.numpy(), mse_3.numpy()]]

#put the list into a table
all_results = pd.DataFrame(model_results, columns = ["model", "mae", "mse"])

# print table
print(all_results)

#define the graphing plot function
def plot_predictions(prediction, true_label):
    plt.figure(figsize=(6,2.5))
    plt.scatter(prediction, y_pred, c="g", label= "prediction")
    plt.xticks(np.arange(0, 1, step=0.2))
    for i, txt in enumerate(prediction):
        plt.annotate(txt, ((prediction-0.2), (y_pred+0.005)))
    plt.legend();
    plt.show()

#run the grapher
plot_predictions(predicted_sequence, y_pred)

#predicting a user input of text
user_text = input("input your text")

user_sequence = tokenizer.texts_to_sequences(user_text)
user_array = np.array(user_sequence)
predict_user_sequence = model.predict(user_array)

plot_predictions(predict_user_sequence, y_pred)


from django.contrib import admin
from .models import Expense,Category

class ExpensesAdmin(admin.ModelAdmin):
    list_display=('amount','description','owner','category','date',)
    search_fields=('description','category','date','amount',)
    list_per_page=7

## note : We can create a super user to our website  - python manage.py createsuperuser
## or Login python shell and perfomr the below command
 ### User.objects.create_superuser(u'username', u'email', u'password')

 # Register your models here.
admin.site.register(Expense,ExpensesAdmin)
admin.site.register(Category)


# Author : Sravani Garikapati
#program reads weights (lbs.) of N students into a list and convert these weights to kilograms
# creating an empty list
lbs = []
kgs = []

# Ask user to enter no of students
n = int(input("Enter number of students : "))

# iterating till the range
# Using for loop
for i in range(n):
    ele = int(input("Enter the weight in lbs: "))
    lbs.append(ele)  # adding the element
    kgs.append(lbs[i]*0.453592)
print("Weight in kgs using for loop: ",kgs)

# Using list comprehension
li = [ i*0.453592 for i in lbs ]
print("Weight in kgs using list comprehension: ",li)