import struct
from ctypes import create_string_buffer
from PyComPortTransf import ByteDataBuff, ComPortTransf
from KX028_ItemMAC import KX028_ItemMAC
from KX028_ItemVLAN import KX028_ItemVLAN
from KX028_KeyEntryMAC import KX028_KeyMAC, KX028_KeyEntryMAC
from KX028_KeyEntryVLAN import KX028_KeyVLAN, KX028_KeyEntryVLAN
from KX028_ItemPort import KX028_ItemPort
from pyVV3_ItemMAC import VV3_ItemMAC
from pyBasisRes import kx028_AddrStruc1, kx028_AddrStruc2, CLASS1_BASE_ADDR
from pyInfoSFP import StatusSPF, InfoSPF

DEBUG_MODE = False

#Commands
cliCMD_NONE           = 0
cliCMD_ERROR          = 1
#  User Defs
cliCMD_ReadMAC        = 2
cliCMD_UpdAddMAC      = 3
cliCMD_DelMAC         = 4
cliCMD_ClearMAC       = 5
cliCMD_ReadVLAN       = 6
cliCMD_UpdAddVLAN     = 7
cliCMD_DelVLAN        = 8
cliCMD_ClearVLAN      = 9
cliCMD_ReadStatPort   = 10
cliCMD_ReadStatClass  = 11
cliCMD_ClearStatPort  = 12
cliCMD_ClearStatClass = 13
cliCMD_WritePortCfg   = 14
cliCMD_ReadAXI        = 15
cliCMD_WriteAXI       = 16

cliCMD_VV3_ReadMAC    = 17
cliCMD_VV3_AddMAC     = 18
cliCMD_VV3_DelMAC     = 19
cliCMD_VV3_ReadRegs   = 20
cliCMD_VV3_WriteRegs  = 21

cliCMD_SFP_ReadStatus = 22
cliCMD_SFP_ReadInfo   = 23

cliCMD_Str = ['cmdNone', 'cmdError', \
              'cmdReadMAC',  'cmdUpdAddMAC',  'cmdDelMAC',  'cmdClearMAC', \
              'cmdReadVLAN', 'cmdUpdAddVLAN', 'cmdDelVLAN', 'cmdClearVLAN', \
              'ReadStatPort', 'ReadStatClass', 'ClearStatPort', 'ClearStatClass', \
              'WritePortCfg', 'ReadAXI', 'WriteAXI',
              'AddMAC_BB3', 'DelMAC_BB3', 'ReadMAC_VV3',
              'ReadStatusSFP', 'ReadInfoSFP']

cliACK_MinLen_1       = 1

# Results
cliAck_LenError       = 0
cliAck_Ok             = 1
cliAck_TBL_AddOk      = 2
cliAck_TBL_AddFault   = 3
cliAck_TBL_UpdOk      = 4
cliAck_TBL_RdFault    = 5
cliAck_TBL_Empty      = 6
cliAck_Unknown        = 7

cliAck_Str = ['MessRx Len Error',
              'OK',
              'Item Added',
              'Item AddFault',
              'Item Updated',
              'Read Fault ',
              'Table Empty']
cliAck_UnknownStr = 'Unknown Error'

#Buffers Size
BUF_TX_LEN = 128
BUF_RX_LEN = 1024

HDR_LEN       = 2
ACK_PARS_OFFS = 2

CFG_MAC_TABLE_ITEMS_COUNT  = 4095 #8192
CFG_MAC_ITEMS_RD_ITER      = 16
CFG_VLAN_TABLE_ITEMS_COUNT = 128
CFG_VLAN_ITEMS_RD_ITER     = 4

CFG_VV3_MAC_TABLE_LEN = 0x800

def IsEvenNum(num):
  return (num & 1) != 1

class KX028_CLI:
  def __init__(self, comPortTransf):
    self.ComPort = comPortTransf
    self.buffTx = create_string_buffer(BUF_TX_LEN)
    self.buffRx = ByteDataBuff(BUF_RX_LEN)

  def packHeader(self, cmd, paramsLen):
    # Create Buff with desired Len
    buffLen = paramsLen + HDR_LEN
    if buffLen & 1 == 0:
      buffLen += 1
    self.buffTx = create_string_buffer(buffLen)
    # Fill Header
    txBytes = paramsLen + HDR_LEN
    b1 = txBytes & 0xFF
    b2 = cmd | ((txBytes >> 2) & 0xC0)
    struct.pack_into("BB", self.buffTx, 0, b1, b2)
    return HDR_LEN

  def transfer(self, cmd, minAckLen):
    rxCnt = self.ComPort.transfer(self.buffTx, self.buffRx)
    if rxCnt < 3:
      print('rxCnt Too Small: {}'.format(rxCnt))
      return False
    else:
      b1, b2 = struct.unpack_from('BB', self.buffRx.data, 0)
      #print('b1={}, b2={}'.format(b1, b2))
      messLen = b1 | ((b2 & 0xC0) << 2)
      rxCmd = b2 & 0x3F
      #print('cmd={} , messLen={}'.format(rxCmd, messLen))
      dataLen = len(self.buffRx.data)
      if IsEvenNum(messLen):
        protocolOk = dataLen == messLen + 1
      else:
        protocolOk = dataLen == messLen
      protocolOk = protocolOk and (rxCmd == cmd)
      #print(str(protocolOk))
      if protocolOk and (dataLen - HDR_LEN >= minAckLen):
        return True
      else:
        print('Wrong DataLength = {}'.format(dataLen))
        return False

  def checkAckStatus(self):
    (R1, ) = struct.unpack_from('B', self.buffRx.data, ACK_PARS_OFFS)
    if R1 < cliAck_Unknown:
      print(cliAck_Str[R1])
    else:
      print(cliAck_UnknownStr)

  # --- Read Items from Tables ---
  def readItemMAC(self, hashAddr, rxItemMAC):
    offs = self.packHeader(cliCMD_ReadMAC, 2)
    struct.pack_into("H", self.buffTx, offs, hashAddr)
    resOK = self.transfer(cliCMD_ReadMAC, KX028_ItemMAC.packLen)
    if resOK:
      rxItemMAC.unpack(self.buffRx.data, offs)
    else:
      print('Fault readItemMAC')
    return resOK

  def readItemVLAN(self, hashAddr, rxItemVLAN):
    offs = self.packHeader(cliCMD_ReadVLAN, 2)
    struct.pack_into("H", self.buffTx, offs, hashAddr)
    resOK = self.transfer(cliCMD_ReadVLAN, KX028_ItemVLAN.packLen)
    if resOK:
      rxItemVLAN.unpack(self.buffRx.data, offs)
    else:
      print('Fault readItemVLAN')
    return resOK

  def readActiveItemsMAC(self, fromHashAddr, rdCount, itemsList):
    #print('RdFrom: {} Count: {}'.format(fromHashAddr, rdCount))
    offs = self.packHeader(cliCMD_ReadMAC, 4)
    struct.pack_into("HH", self.buffTx, offs, fromHashAddr, rdCount)
    resOK = self.transfer(cliCMD_ReadMAC, cliACK_MinLen_1)
    rdItemsCnt = 0
    if resOK:
      messLenRx = len(self.buffRx.data)
      if messLenRx > KX028_ItemMAC.packLen:
        while offs + KX028_ItemMAC.packLen < messLenRx:
          item = KX028_ItemMAC()
          item.unpack(self.buffRx.data, offs)
          itemsList.append(item)
          offs += KX028_ItemMAC.packLen
          rdItemsCnt += 1
      else:
        self.checkAckStatus()
    else:
      print('Fault readActiveItemsMAC')
    return rdItemsCnt

  def readActiveItemsVLAN(self, fromHashAddr, rdCount, itemsList):
    offs = self.packHeader(cliCMD_ReadVLAN, 4)
    struct.pack_into("HH", self.buffTx, offs, fromHashAddr, rdCount)
    resOK = self.transfer(cliCMD_ReadVLAN, cliACK_MinLen_1)
    rdItemsCnt = 0
    if resOK:
      messLenRx = len(self.buffRx.data)
      if messLenRx > KX028_ItemVLAN.packLen:
        while offs  + KX028_ItemVLAN.packLen < messLenRx:
          item = KX028_ItemVLAN()
          item.unpack(self.buffRx.data, offs)
          itemsList.append(item)
          offs += KX028_ItemVLAN.packLen
          rdItemsCnt += 1
      else:
        self.checkAckStatus()
    else:
      print('Fault readActiveItemsVLAN')
    return rdItemsCnt

  def ReadTableMAC(self):
    cntToRead = CFG_MAC_TABLE_ITEMS_COUNT
    itemsList = []
    fromHashAddr = 0
    while cntToRead > 0:
      rdCnt = self.readActiveItemsMAC(fromHashAddr, CFG_MAC_ITEMS_RD_ITER, itemsList)
      if rdCnt < CFG_MAC_ITEMS_RD_ITER:
        break
      else:
        cntToRead -= CFG_MAC_ITEMS_RD_ITER
        fromHashAddr += CFG_MAC_ITEMS_RD_ITER
    return itemsList

  def ReadTableVLAN(self):
    cntToRead = CFG_VLAN_TABLE_ITEMS_COUNT
    itemsList = []
    fromHashAddr = 0
    while cntToRead > 0:
      rdCnt = self.readActiveItemsVLAN(fromHashAddr, CFG_VLAN_ITEMS_RD_ITER, itemsList)
      if rdCnt < CFG_VLAN_ITEMS_RD_ITER:
        break
      else:
        cntToRead -= CFG_VLAN_ITEMS_RD_ITER
        fromHashAddr += CFG_VLAN_ITEMS_RD_ITER
    return itemsList

  # --- Simplest Commands with single ack (no data) ---
  def SendObjectWithAck(self, cmd, obj):
    offs = self.packHeader(cmd, obj.packLen)
    obj.pack(self.buffTx, offs)
    resOK = self.transfer(cmd, cliACK_MinLen_1)
    if resOK:
      self.checkAckStatus()
    else:
      print('Fault ' + cliCMD_Str[cmd])
    return resOK

  def UpdateOrAddMAC(self, keyEntryMAC):
    return self.SendObjectWithAck(cliCMD_UpdAddMAC, keyEntryMAC)

  def UpdateOrAddVLAN(self, keyEntryVLAN):
    return self.SendObjectWithAck(cliCMD_UpdAddVLAN, keyEntryVLAN)

  def DelItemMAC(self, keyMAC):
    return self.SendObjectWithAck(cliCMD_DelMAC, keyMAC)

  def DelItemVLAN(self, keyVLAN):
    return self.SendObjectWithAck(cliCMD_DelVLAN, keyVLAN)

  # --- Commands with single param (int32) ---
  def SendCmdPar32WithAck(self, cmd, par32):
    offs = self.packHeader(cmd, 4)
    struct.pack_into("L", self.buffTx, offs, par32)
    resOK = self.transfer(cmd, cliACK_MinLen_1)
    if resOK:
      self.checkAckStatus()
    else:
      print('Fault ' + cliCMD_Str[cmd])
    return resOK

  def ClearMAC(self, options):
    return self.SendCmdPar32WithAck(cliCMD_ClearMAC, options)

  def ClearVLAN(self, options):
    return self.SendCmdPar32WithAck(cliCMD_ClearVLAN, options)

  # --- AXI Registers ---
  def readAxiRegList(self, regAddrList):
    regCnt = len(regAddrList)
    offs = self.packHeader(cliCMD_ReadAXI, regCnt * 4)
    for Reg in regAddrList:
      struct.pack_into("L", self.buffTx, offs, Reg)
      offs += 4
    resOK = self.transfer(cliCMD_ReadAXI, cliACK_MinLen_1)
    valueList = []
    if resOK:
      if len(self.buffRx.data) == (regCnt * 4 + HDR_LEN + 1):
        offs = ACK_PARS_OFFS
        for i in range(regCnt):
          R, = struct.unpack_from('L', self.buffRx.data, offs)
          valueList.append(R)
          offs += 4
      else:
        self.checkAckStatus()
    else:
      print('Fault readAxiRegList')
    return valueList

  def writeAxiRegList(self, regAddrList, regDataList):
    regCnt = len(regAddrList)
    offs = self.packHeader(cliCMD_WriteAXI, regCnt * 8)
    for i in range(regCnt):
      struct.pack_into("LL", self.buffTx, offs, regAddrList[i], regDataList[i])
      offs += 8
    resOK = self.transfer(cliCMD_WriteAXI, cliACK_MinLen_1)
    if resOK:
      self.checkAckStatus()
    else:
      print('Fault writeAxiRegList')
    return resOK

  # --- Read/Clear Statistics ---
  def readStatsEMAC(self, emac, estimLen):
    offs = self.packHeader(cliCMD_ReadStatPort, 1)
    struct.pack_into("B", self.buffTx, offs, emac)
    resOK = self.transfer(cliCMD_ReadStatPort, cliACK_MinLen_1)
    if resOK:
      if len(self.buffRx.data) > estimLen + offs:
        return (self.buffRx.data, ACK_PARS_OFFS)
      else:
        self.checkAckStatus()
    else:
      print('Fault readStatsEMAC')
    return (None, 1)

  def readStatClassifEMAC(self, emac, estimLen):
    offs = self.packHeader(cliCMD_ReadStatClass, 1)
    struct.pack_into("B", self.buffTx, offs, emac)
    resOK = self.transfer(cliCMD_ReadStatClass, cliACK_MinLen_1)
    if resOK:
      if len(self.buffRx.data) > estimLen + offs:
        return (self.buffRx.data, ACK_PARS_OFFS)
      else:
        self.checkAckStatus()
    else:
      print('Fault readStatClassifEMAC')
    return (None, 1)

  def SendCmdPar8WithAck(self, cmd, par8):
    offs = self.packHeader(cmd, 1)
    struct.pack_into("B", self.buffTx, offs, par8)
    resOK = self.transfer(cmd, cliACK_MinLen_1)
    if resOK:
      self.checkAckStatus()
    else:
      print('Fault ' + cliCMD_Str[cmd])
    return resOK

  def clearStatsEMAC(self, emac):
    return self.SendCmdPar8WithAck(cliCMD_ClearStatPort, emac)

  def clearStatClassifEMAC(self, emac):
    return self.SendCmdPar8WithAck(cliCMD_ClearStatClass, emac)

  # --- EMAC Ports Control ---
  def writePortCfg(self, emac, itemPort):
    offs = self.packHeader(cliCMD_WritePortCfg, itemPort.packLen + 1)
    offs = itemPort.pack(self.buffTx, offs)
    struct.pack_into("B", self.buffTx, offs, emac)
    resOK = self.transfer(cliCMD_WritePortCfg, cliACK_MinLen_1)
    if resOK:
      self.checkAckStatus()
    else:
      print('Fault writePortCfg')
    return resOK

  def readPortCfg(self, emac):
    if not DEBUG_MODE:
      addrs = [CLASS1_BASE_ADDR + kx028_AddrStruc1[emac], CLASS1_BASE_ADDR + kx028_AddrStruc2[emac]]
    else:
      addrs = [kx028_AddrStruc1_dbg[emac], kx028_AddrStruc1_dbg[emac]]
    values = self.readAxiRegList(addrs)
    if len(values) > 1:
      itemPort = KX028_ItemPort()
      itemPort.unpackFromRegs(values[0], values[1])
      return itemPort
    else:
      return None

  #============== VV3 ==============
  def AddMAC_VV3(self, itemMac):
    return self.SendObjectWithAck(cliCMD_VV3_AddMAC, itemMac)

  def readActiveItemsMAC_VV3(self, fromHashAddr, rdCount, itemsList):
    #print('RdFrom: {} Count: {}'.format(fromHashAddr, rdCount))
    offs = self.packHeader(cliCMD_VV3_ReadMAC, 4)
    struct.pack_into("HH", self.buffTx, offs, fromHashAddr, rdCount)
    resOK = self.transfer(cliCMD_VV3_ReadMAC, cliACK_MinLen_1)
    rdItemsCnt = 0
    if resOK:
      messLenRx = len(self.buffRx.data)
      if messLenRx > VV3_ItemMAC.packLen:
        while offs + VV3_ItemMAC.packLen < messLenRx:
          item = VV3_ItemMAC()
          item.unpack(self.buffRx.data, offs)
          itemsList.append(item)
          offs += VV3_ItemMAC.packLen
          rdItemsCnt += 1
      else:
        self.checkAckStatus()
    else:
      print('Fault readActiveItemsMAC_VV3')
    return rdItemsCnt

  def ReadTableMAC_VV3(self):
    cntToRead = CFG_VV3_MAC_TABLE_LEN
    itemsList = []
    fromHashAddr = 0
    while cntToRead > 0:
      rdCnt = self.readActiveItemsMAC_VV3(fromHashAddr, CFG_MAC_ITEMS_RD_ITER, itemsList)
      if rdCnt < CFG_MAC_ITEMS_RD_ITER:
        break
      else:
        cntToRead -= CFG_MAC_ITEMS_RD_ITER
        fromHashAddr += CFG_MAC_ITEMS_RD_ITER
    return itemsList

  def DelItemMAC_VV3(self, hashAddr):
    offs = self.packHeader(cliCMD_VV3_DelMAC, 2)
    struct.pack_into("H", self.buffTx, offs, hashAddr)
    resOK = self.transfer(cliCMD_VV3_DelMAC, cliACK_MinLen_1)
    if resOK:
      self.checkAckStatus()
    else:
      print('Fault ' + cliCMD_Str[cliCMD_VV3_DelMAC])
    return resOK

  def ReadRegList_VV3(self, regAddrList):
    regCnt = len(regAddrList)
    offs = self.packHeader(cliCMD_VV3_ReadRegs, regCnt)
    for Reg in regAddrList:
      struct.pack_into("B", self.buffTx, offs, Reg)
      offs += 1
    resOK = self.transfer(cliCMD_VV3_ReadRegs, cliACK_MinLen_1)
    valueList = []
    if resOK:
      if len(self.buffRx.data) >= (regCnt + HDR_LEN):
        offs = ACK_PARS_OFFS
        for i in range(regCnt):
          R, = struct.unpack_from('B', self.buffRx.data, offs)
          valueList.append(R)
          offs += 1
      else:
        self.checkAckStatus()
    else:
      print('Fault readRegList_VV3')
    return valueList

  def WriteRegList_VV3(self, regAddrList, regDataList):
    regCnt = len(regAddrList)
    offs = self.packHeader(cliCMD_VV3_WriteRegs, regCnt * 2)
    for i in range(regCnt):
      struct.pack_into("BB", self.buffTx, offs, regAddrList[i], regDataList[i])
      offs += 2
    resOK = self.transfer(cliCMD_VV3_WriteRegs, cliACK_MinLen_1)
    if resOK:
      self.checkAckStatus()
    else:
      print('Fault writeRegList_VV3')
    return resOK

  #============== SFP ==============
  def ReadStatusSFP(self, statusSFP):
    offs = self.packHeader(cliCMD_SFP_ReadStatus, 0)
    resOK = self.transfer(cliCMD_SFP_ReadStatus, StatusSPF.packLen)
    if resOK:
      statusSFP.unpack(self.buffRx.data, offs)
    else:
      print('Fault readItemMAC')
    return resOK

  def ReadInfoSFP(self, indexSFP, infoSFP):
    offs = self.packHeader(cliCMD_SFP_ReadInfo, 1)
    struct.pack_into("B", self.buffTx, offs, indexSFP)
    resOK = self.transfer(cliCMD_SFP_ReadInfo, StatusSPF.packLen)
    if resOK:
      infoSFP.unpack(self.buffRx.data, offs)
    else:
      print('Fault readItemMAC')
    return resOK


from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^invoice/([0-9]+)/$', views.invoice, name='invoice'),
    url(r'^search/', views.search, name='search'),
    url(r'^feed/', views.feed, name='feed'),
    url(r'^start/([0-9]+)', views.start, name='start'),
    url(r'^buttontoggle/', views.buttontoggle, name='buttontoggle'),
    url(r'^feedstatus/', views.feedstatus, name='feedstatus'),
    url(r'^remove/', views.remove, name='remove'),
    url(r'^customerinfo/([0-9]+)', views.customerinfo, name='customerinfo'),
    url(r'^newmultiple', views.newmultiple, name='newmultiple'),
    url(r'', views.index, name='index'),
]


import tensorflow as tf
import sklearn
import scipy.sparse
import numpy as np
import os, time, collections, shutil
from collections import defaultdict

# NFEATURES = 28**2
# NCLASSES = 10

# Common methods for all models

class base_model(object):
    def __init__(self):
        self.seg_classes = {'Earphone': [16, 17, 18], 'Motorbike': [30, 31, 32, 33, 34, 35], 'Rocket': [41, 42, 43],
                            'Car': [8, 9, 10, 11], 'Laptop': [28, 29], 'Cap': [6, 7], 'Skateboard': [44, 45, 46],
                            'Mug': [36, 37], 'Guitar': [19, 20, 21], 'Bag': [4, 5], 'Lamp': [24, 25, 26, 27],
                            'Table': [47, 48, 49], 'Airplane': [0, 1, 2, 3], 'Pistol': [38, 39, 40],
                            'Chair': [12, 13, 14, 15], 'Knife': [22, 23]}
        self.label_to_cat = {}
        for key in self.seg_classes.keys():
            for label in self.seg_classes[key]:
                self.label_to_cat[label] = key
        self.regularizers = []

    # High-level interface which runs the constructed computational graph.

    def predict(self, data, cat, labels, sess=None):
        loss = 0
        size = data.shape[0]
        predictions = np.empty((size, data.shape[1]))
        sess = self._get_session(sess)
        for begin in range(0, size, self.batch_size):
            end = begin + self.batch_size
            end = min([end, size])

            batch_data = np.zeros((self.batch_size, data.shape[1], 6))
            batch_cat = np.zeros((self.batch_size))
            # batch_lap = np.zeros((self.batch_size, data.shape[1], data.shape[1]))
            tmp_data = data[begin:end, :]
            tmp_cat = cat[begin:end]
            # tmp_lap = lap[begin:end, :, :]
            if type(tmp_data) is not np.ndarray:
                tmp_data = tmp_data.toarray()  # convert sparse matrices
            batch_data[:end - begin] = tmp_data
            batch_cat[:end - begin] = tmp_cat
            # batch_lap[:end - begin] = tmp_lap
            feed_dict = {self.ph_data: batch_data, self.ph_cat: batch_cat, self.ph_dropout: 1}

            # Compute loss if labels are given.
            if labels is not None:
                batch_labels = np.zeros((self.batch_size, data.shape[1]))
                batch_labels[:end - begin] = labels[begin:end]
                feed_dict[self.ph_labels] = batch_labels
                batch_pred, batch_loss = sess.run([self.op_prediction, self.op_loss], feed_dict)
                loss += batch_loss
            else:
                batch_pred = sess.run(self.op_prediction, feed_dict)

            predictions[begin:end] = batch_pred[:end - begin]

        if labels is not None:
            return predictions, loss * self.batch_size / size
        else:
            return predictions

    def evaluate(self, data, cat, labels, sess=None):
        """
        Runs one evaluation against the full epoch of data.
        Return the precision and the number of correct predictions.
        Batch evaluation saves memory and enables this to run on smaller GPUs.

        sess: the session in which the model has been trained.
        op: the Tensor that returns the number of correct predictions.
        data: size N x M
            N: number of signals (samples)
            M: number of vertices (features)
        labels: size N
            N: number of signals (samples)
        """

        t_process, t_wall = time.process_time(), time.time()
        predictions, loss = self.predict(data, cat, labels, sess)
        # print(predictions)
        ncorrects = np.mean(predictions == labels, axis=1)
        print(ncorrects)
        accuracy = np.mean(ncorrects)
        f1 = 0

        cat_iou = defaultdict(list)

        tot_iou = []
        for i in range(predictions.shape[0]):
            segp = predictions[i, :]
            segl = labels[i, :]
            cat = self.label_to_cat[segl[0]]
            part_ious = [0.0 for _ in range(len(self.seg_classes[cat]))]

            for l in self.seg_classes[cat]:
                if (np.sum(segl == l) == 0) and (np.sum(segp == l) == 0):  # part is not present, no prediction as well
                    part_ious[l - self.seg_classes[cat][0]] = 1.0
                else:
                    part_ious[l - self.seg_classes[cat][0]] = np.sum((segl == l) & (segp == l)) / float(
                        np.sum((segl == l) | (segp == l)))
            cat_iou[cat].append(np.mean(part_ious))
            tot_iou.append(np.mean(part_ious))

        for key, value in cat_iou.items():
            print(key + ': {:.4f}, total: {:d}'.format(np.mean(value), len(value)))
        # print(tot_iou)
        # accuracy = 100 * sklearn.metrics.accuracy_score(labels, predictions)
        # f1 = 100 * sklearn.metrics.f1_score(labels, predictions, average='weighted')
        string = 'accuracy: {:.4f} ({:d} / {:d}), iou (weighted): {:.4f}, loss: {:.2e}'.format(
            accuracy, np.sum(predictions == labels), labels.shape[0] * labels.shape[1], np.mean(tot_iou), loss)
        if sess is None:
            string += '\ntime: {:.0f}s (wall {:.0f}s)'.format(time.process_time() - t_process, time.time() - t_wall)
        return string, accuracy, f1, loss

    def fit(self, train_data, train_cat, train_labels, val_data, val_cat, val_labels, is_continue=True):
        f = open("train.log", "a")
        t_process, t_wall = time.process_time(), time.time()
        if is_continue is True:
            sess = self._get_session(sess=None)
        else:
            sess = tf.Session(graph=self.graph)
            sess.run(self.op_init)
            shutil.rmtree(self._get_path('checkpoints'), ignore_errors=True)
            os.makedirs(self._get_path('checkpoints'))

        writer = tf.summary.FileWriter(self._get_path('summaries'), self.graph)
        path = os.path.join(self._get_path('checkpoints'), 'model')

        # Training.
        accuracies = []
        losses = []
        indices = collections.deque()
        num_steps = int(self.num_epochs * train_data.shape[0] / self.batch_size)
        for step in range(1, num_steps + 1):

            # Be sure to have used all the samples before using one a second time.
            if len(indices) < self.batch_size:
                indices.extend(np.random.permutation(train_data.shape[0]))

            idx = [indices.popleft() for i in range(self.batch_size)]
            batch_data, batch_cat, batch_labels = train_data[idx, :], train_cat[idx], train_labels[idx]
            if type(batch_data) is not np.ndarray:
                batch_data = batch_data.toarray()  # convert sparse matrices
            feed_dict = {self.ph_data: batch_data, self.ph_cat: batch_cat, self.ph_labels: batch_labels,
                         self.ph_dropout: self.dropout}
            learning_rate, loss_average, loss = sess.run([self.op_train, self.op_loss_average, self.op_loss], feed_dict)
            # print(sess.run([self.op_logits],feed_dict))
            print(loss, loss_average)
            f.write(str(loss) + str(loss_average))
            # Periodical evaluation of the model.
            if step % self.eval_frequency == 0 or step == num_steps:
                epoch = step * self.batch_size / train_data.shape[0]
                print('step {} / {} (epoch {:.2f} / {}):'.format(step, num_steps, epoch, self.num_epochs))
                f.write('step {} / {} (epoch {:.2f} / {}):'.format(step, num_steps, epoch, self.num_epochs))
                print('  learning_rate = {:.2e}, loss_average = {:.2e}'.format(learning_rate, loss_average))
                f.write('  learning_rate = {:.2e}, loss_average = {:.2e}'.format(learning_rate, loss_average))
                string, accuracy, f1, loss = self.evaluate(val_data, val_cat, val_labels, sess)
                accuracies.append(accuracy)
                losses.append(loss)
                print('  validation {}'.format(string))
                f.write('  validation {}'.format(string))
                print('  time: {:.0f}s (wall {:.0f}s)'.format(time.process_time() - t_process, time.time() - t_wall))
                f.write('  time: {:.0f}s (wall {:.0f}s)'.format(time.process_time() - t_process, time.time() - t_wall))

                # Summaries for TensorBoard.
                summary = tf.Summary()
                summary.ParseFromString(sess.run(self.op_summary, feed_dict))
                summary.value.add(tag='validation/accuracy', simple_value=accuracy)
                summary.value.add(tag='validation/f1', simple_value=f1)
                summary.value.add(tag='validation/loss', simple_value=loss)
                writer.add_summary(summary, step)

                # Save model parameters (for evaluation).
                self.op_saver.save(sess, path, global_step=step)

        print('validation accuracy: peak = {:.2f}, mean = {:.2f}'.format(max(accuracies), np.mean(accuracies[-10:])))
        writer.close()
        sess.close()

        t_step = (time.time() - t_wall) / num_steps
        return accuracies, losses, t_step

    # Methods to construct the computational graph.

    def build_graph(self, M_0):
        """Build the computational graph of the model."""
        self.graph = tf.Graph()
        with self.graph.as_default():
            # Inputs.
            with tf.name_scope('inputs'):
                # self.pj_graph = tf.placeholder(tf.float32, (self.batch_size, M_0, M_0), 'lapacian')
                self.ph_data = tf.placeholder(tf.float32, (self.batch_size, M_0, 6), 'data')
                self.ph_labels = tf.placeholder(tf.int32, (self.batch_size, M_0), 'labels')
                self.ph_cat = tf.placeholder(tf.int32, (self.batch_size), 'labels')
                self.ph_dropout = tf.placeholder(tf.float32, (), 'dropout')

            # Model.
            op_logits = self.inference(self.ph_data, self.ph_cat, self.ph_dropout)

            run_meta = tf.RunMetadata()
            opts = tf.profiler.ProfileOptionBuilder.float_operation()
            flops = tf.profiler.profile(self.graph, run_meta=run_meta, cmd='op', options=opts)
            print('Total flops' + str(flops.total_float_ops))

            self.op_logits = op_logits
            self.op_loss, self.op_loss_average = self.loss(op_logits, self.ph_labels, self.regularization)
            self.op_train = self.training(self.op_loss, self.learning_rate,
                                          self.decay_steps, self.decay_rate, self.momentum)
            self.op_prediction = self.prediction(op_logits)

            # Initialize variables, i.e. weights and biases.
            self.op_init = tf.global_variables_initializer()

            # Summaries for TensorBoard and Save for model parameters.
            self.op_summary = tf.summary.merge_all()
            self.op_saver = tf.train.Saver(max_to_keep=5)

        self.graph.finalize()

    def inference(self, data, cat, dropout):
        """
        It builds the model, i.e. the computational graph, as far as
        is required for running the network forward to make predictions,
        i.e. return logits given raw data.

        data: size 2 x N x M
        first part:
            N: number of signals (samples)
            M: number of vertices (features)
        second part:
            K: k layers of graph
        training: we may want to discriminate the two, e.g. for dropout.
            True: the model is built for training.
            False: the model is built for evaluation.
        """
        # TODO: optimizations for sparse data
        logits = self._inference(data, cat, dropout)
        return logits

    def probabilities(self, logits):
        """Return the probability of a sample to belong to each class."""
        with tf.name_scope('probabilities'):
            probabilities = tf.nn.softmax(logits)
            return probabilities

    def prediction(self, logits):
        """Return the predicted classes."""
        with tf.name_scope('prediction'):
            prediction = tf.argmax(logits, axis=2)
            return prediction

    def loss(self, logits, labels, regularization):
        """Adds to the inference model the layers required to generate loss."""
        with tf.name_scope('loss'):
            with tf.name_scope('cross_entropy'):
                labels = tf.to_int64(labels)
                cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels)
                cross_entropy = tf.reduce_mean(cross_entropy)
            with tf.name_scope('regularization'):
                regularization *= tf.add_n(self.regularizers)
            loss = cross_entropy + regularization

            # Summaries for TensorBoard.
            tf.summary.scalar('loss/cross_entropy', cross_entropy)
            tf.summary.scalar('loss/regularization', regularization)
            tf.summary.scalar('loss/total', loss)
            with tf.name_scope('averages'):
                averages = tf.train.ExponentialMovingAverage(0.9)
                op_averages = averages.apply([cross_entropy, regularization, loss])
                tf.summary.scalar('loss/avg/cross_entropy', averages.average(cross_entropy))
                tf.summary.scalar('loss/avg/regularization', averages.average(regularization))
                tf.summary.scalar('loss/avg/total', averages.average(loss))
                with tf.control_dependencies([op_averages]):
                    loss_average = tf.identity(averages.average(loss), name='control')
            return loss, regularization

    def training(self, loss, learning_rate, decay_steps, decay_rate=0.95, momentum=0.8):
        """Adds to the loss model the Ops required to generate and apply gradients."""
        with tf.name_scope('training'):
            # Learning rate.
            global_step = tf.Variable(0, name='global_step', trainable=False)
            if decay_rate != 1:
                learning_rate = tf.train.exponential_decay(
                    learning_rate, global_step, decay_steps, decay_rate, staircase=True)
            tf.summary.scalar('learning_rate', learning_rate)
            # Optimizer.
            if momentum == 0:
                # optimizer = tf.train.GradientDescentOptimizer(learning_rate)
                optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
            else:
                optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)
            grads = optimizer.compute_gradients(loss)
            op_gradients = optimizer.apply_gradients(grads, global_step=global_step)
            # Histograms.
            for grad, var in grads:
                if grad is None:
                    print('warning: {} has no gradient'.format(var.op.name))
                else:
                    tf.summary.histogram(var.op.name + '/gradients', grad)
            # The op return the learning rate.
            with tf.control_dependencies([op_gradients]):
                op_train = tf.identity(learning_rate, name='control')
            return op_train

    # Helper methods.

    def _get_path(self, folder):
        path = os.path.dirname(os.path.realpath(__file__))
        return os.path.join(path, '..', folder, self.dir_name)

    def _get_session(self, sess=None):
        """Restore parameters if no session given."""
        if sess is None:
            sess = tf.Session(graph=self.graph)
            filename = tf.train.latest_checkpoint(self._get_path('checkpoints'))
            self.op_saver.restore(sess, filename)
        return sess

    def _weight_variable(self, shape, regularization=True):
        initial = tf.truncated_normal_initializer(0, 0.1)
        var = tf.get_variable('weights', shape, tf.float32, initializer=initial)
        if regularization:
            self.regularizers.append(tf.nn.l2_loss(var))
        tf.summary.histogram(var.op.name, var)
        return var

    def _bias_variable(self, shape, regularization=True):
        initial = tf.constant_initializer(0.1)
        var = tf.get_variable('bias', shape, tf.float32, initializer=initial)
        if regularization:
            self.regularizers.append(tf.nn.l2_loss(var))
        tf.summary.histogram(var.op.name, var)
        return var

    def _conv2d(self, x, W):
        return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

class rgcnn(base_model):
    """
    Graph CNN which uses the Chebyshev approximation.

    The following are hyper-parameters of graph convolutional layers.
    They are lists, which length is equal to the number of gconv layers.
        F: Number of features.
        K: List of polynomial orders, i.e. filter sizes or number of hopes.

    The following are hyper-parameters of fully connected layers.
    They are lists, which length is equal to the number of fc layers.
        M: Number of features per sample, i.e. number of hidden neurons.
           The last layer is the softmax, i.e. M[-1] is the number of classes.

    Training parameters:
        num_epochs:    Number of training epochs.
        learning_rate: Initial learning rate.
        decay_rate:    Base of exponential decay. No decay with 1.
        decay_steps:   Number of steps after which the learning rate decays.
        momentum:      Momentum. 0 indicates no momentum.

    Regularization parameters:
        regularization: L2 regularizations of weights and biases.
        dropout:        Dropout (fc layers): probability to keep hidden neurons. No dropout with 1.
        batch_size:     Batch size. Must divide evenly into the dataset sizes.
        eval_frequency: Number of steps between evaluations.

    Directories:
        dir_name: Name for directories (summaries and model parameters).
    """

    def __init__(self, vertice, F, K, M, filter='chebyshev5', brelu='b1relu', pool='mpool1',
                 num_epochs=20, learning_rate=0.1, decay_rate=0.95, decay_steps=None, momentum=0.9,
                 regularization=0, dropout=0, batch_size=100, eval_frequency=200,
                 dir_name=''):
        super().__init__()

        # Verify the consistency w.r.t. the number of layers.
        assert len(F) == len(K)

        # Keep the useful Laplacians only. May be zero.
        M_0 = vertice
        # Print information about NN architecture.
        Ngconv = len(F)
        Nfc = len(M)
        print('NN architecture')
        print('  input: M_0 = {}'.format(vertice))
        for i in range(Ngconv):
            print('  layer {0}: gconv{0}'.format(i + 1))
            print('    representation: M_{0} * F_{1}= {2} * {3} = {4}'.format(
                i, i + 1, vertice, F[i], vertice * F[i]))
            F_last = F[i - 1] if i > 0 else 1
            print('    weights: F_{0} * F_{1} * K_{1} = {2} * {3} * {4} = {5}'.format(
                i, i + 1, F_last, F[i], K[i], F_last * F[i] * K[i]))
            print('    biases: F_{} = {}'.format(i + 1, F[i]))
        for i in range(Nfc):
            name = 'fc{}'.format(i + 1)
            print('  layer {}: {}'.format(Ngconv + i + 1, name))
            print('    representation: M_{} = {}'.format(Ngconv + i + 1, M[i]))
            M_last = M[i - 1] if i > 0 else vertice if Ngconv == 0 else vertice * F[-1]
            print('    weights: M_{} * M_{} = {} * {} = {}'.format(
                Ngconv + i, Ngconv + i + 1, M_last, M[i], M_last * M[i]))
            print('    biases: M_{} = {}'.format(Ngconv + i + 1, M[i]))

        # Store attributes and bind operations.
        self.F, self.K, self.M = F, K, M
        self.num_epochs, self.learning_rate = num_epochs, learning_rate
        self.decay_rate, self.decay_steps, self.momentum = decay_rate, decay_steps, momentum
        self.regularization, self.dropout = regularization, dropout
        self.batch_size, self.eval_frequency = batch_size, eval_frequency
        self.dir_name = dir_name
        self.filter = getattr(self, filter)
        self.brelu = getattr(self, brelu)

        # Build the computational graph.
        self.build_graph(M_0)

    def chebyshev5(self, x, L, Fout, K):
        # If K == 1 it is equivalent to fc layer
        N, M, Fin = x.get_shape()
        N, M, Fin = int(N), int(M), int(Fin)
        x0 = x  # N x M x Fin
        x = tf.expand_dims(x0, 0)

        def concat(x, x_):
            x_ = tf.expand_dims(x_, 0)  # 1 x M x Fin*N
            return tf.concat([x, x_], axis=0)  # K x M x Fin*N

        if K > 1:
            x1 = tf.matmul(L, x0)
            x = concat(x, x1)
        for k in range(2, K):
            x2 = 2 * tf.matmul(L, x1) - x0
            x = concat(x, x2)
            x0, x1 = x1, x2
        # K x N x M x Fin
        x = tf.transpose(x, perm=[1, 2, 3, 0])  # N x M x Fin x K
        x = tf.reshape(x, [N * M, Fin * K])  # N*M x Fin*K
        # Filter: Fin*Fout filters of order K, i.e. one filterbank per feature pair.
        W = self._weight_variable([Fin * K, Fout], regularization=False)
        x = tf.matmul(x, W)  # N*M x Fout
        return tf.reshape(x, [N, M, Fout])  # N x M x Fout

    def b1relu(self, x):
        """Bias and ReLU. One bias per filter."""
        N, M, F = x.get_shape()
        b = self._bias_variable([1, 1, int(F)], regularization=False)
        return tf.nn.relu(x + b)

    def b2relu(self, x):
        """Bias and ReLU. One bias per vertex per filter."""
        N, M, F = x.get_shape()
        b = self._bias_variable([1, int(M), int(F)], regularization=False)
        return tf.nn.relu(x + b)

    def fc(self, x, Mout, relu=True):
        """Fully connected layer with Mout features."""
        N, Min = x.get_shape()
        W = self._weight_variable([int(Min), Mout], regularization=True)
        b = self._bias_variable([Mout], regularization=True)
        x = tf.matmul(x, W) + b
        return tf.nn.relu(x) if relu else x

    def pairwise_distance(self, point_cloud):
        """Compute pairwise distance of a point cloud.

        Args:
            point_cloud: tensor (batch_size, num_points, num_dims)

        Returns:
            pairwise distance: (batch_size, num_points, num_points)
        """

        og_batch_size = point_cloud.get_shape().as_list()[0]
        point_cloud = tf.squeeze(point_cloud)
        if og_batch_size == 1:
            point_cloud = tf.expand_dims(point_cloud, 0)

        point_cloud_transpose = tf.transpose(point_cloud, perm=[0, 2, 1])
        point_cloud_inner = tf.matmul(point_cloud, point_cloud_transpose)
        point_cloud_inner = -2 * point_cloud_inner
        point_cloud_square = tf.reduce_sum(tf.square(point_cloud), axis=-1, keep_dims=True)
        point_cloud_square_tranpose = tf.transpose(point_cloud_square, perm=[0, 2, 1])
        adj_matrix = point_cloud_square + point_cloud_inner + point_cloud_square_tranpose
        adj_matrix = tf.exp(-adj_matrix)
        return adj_matrix

    def knn(self, adj_matrix, k=30):
        """Get KNN based on the pairwise distance.
        Args:
            pairwise distance: (batch_size, num_points, num_points)
            k: int

        Returns:
            nearest neighbors: (batch_size, num_points, k)
        """
        # neg_adj = -adj_matrix
        _, nn_idx = tf.nn.top_k(adj_matrix, k=k)
        return nn_idx

    def get_laplacian(self, adj_matrix, normalize=True):
        """Compute pairwise distance of a point cloud.

        Args:
            pairwise distance: tensor (batch_size, num_points, num_points)

        Returns:
            pairwise distance: (batch_size, num_points, num_points)
        """
        if normalize:
            D = tf.reduce_sum(adj_matrix, axis=1)  # (batch_size,num_points)
            eye = tf.ones_like(D)
            eye = tf.matrix_diag(eye)
            D = 1 / tf.sqrt(D)
            D = tf.matrix_diag(D)
            L = eye - tf.matmul(tf.matmul(D, adj_matrix), D)
        else:
            D = tf.reduce_sum(adj_matrix, axis=1)  # (batch_size,num_points)
            # eye = tf.ones_like(D)
            # eye = tf.matrix_diag(eye)
            # D = 1 / tf.sqrt(D)
            D = tf.matrix_diag(D)
            L = D - adj_matrix
        return L

    def get_one_matrix_knn(self, matrix, k):
        values, indices = tf.nn.top_k(matrix, k,
                                      sorted=False)  # indices will be [[0, 1], [1, 2]], values will be [[6., 2.], [4., 5.]]

        my_range = tf.expand_dims(tf.range(0, indices.get_shape()[0]), 1)  # will be [[0], [1]]
        my_range_repeated = tf.tile(my_range, [1, k])  # will be [[0, 0], [1, 1]]

        # change shapes to [N, k, 1] and [N, k, 1], to concatenate into [N, k, 2]
        full_indices = tf.concat([tf.expand_dims(my_range_repeated, 2), tf.expand_dims(indices, 2)], axis=2)
        full_indices = tf.reshape(full_indices, [-1, 2])

        to_substract = tf.sparse_to_dense(full_indices, matrix.get_shape(), tf.reshape(values, [-1]), default_value=0.,
                                          validate_indices=False)

        # res = matrix - to_substract  # res should be all 0.
        return to_substract

    def _inference(self, x, cat, dropout):
        L = self.pairwise_distance(x)
        # L_ =self.get_laplacian(L,normalize=False)
        # L = tf.stack([self.get_one_matrix_knn(matrix = L[o],k = 30) for o in range(L.get_shape()[0])])
        L = self.get_laplacian(L)
        cat = tf.expand_dims(cat, axis=1)
        cat = tf.one_hot(cat, 16, axis=-1)
        cat = tf.tile(cat, [1, 2048, 1])
        x = tf.concat([x, cat], axis=2)

        x1 = 0 #cache for layer1
        for i in range(len(self.F)):
            with tf.variable_scope('conv{}'.format(i)):
                with tf.name_scope('filter'):
                    if i == 4:
                        x = tf.concat([x, x1], axis=2)
                    x = self.filter(x, L, self.F[i], self.K[i])
                    if i == 1:
                        x1 = x
                    self.regularizers.append(tf.nn.l2_loss(tf.matmul(tf.matmul(tf.transpose(x, perm=[0, 2, 1]), L), x)))
                with tf.name_scope('bias_relu'):
                    x = self.brelu(x)
        return x


#Testing a commit
#Commit code to github: git push origin master (after first one, can just typw git push)

#To Do's
#Multiple circles - redraw based on BN.parent.length

import wx
import os
import BayesianNetwork as BN
import functools
import datetime

#Frame - can't figure out how to change initial size
class BNGUI(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(BNGUI,self).__init__(*args,**kwargs)
        self.InitUI()
        self.SetBackgroundColour("white")

    def InitUI(self):
        menubar = wx.MenuBar()

        #File Menubar
        fileMenu = wx.Menu()
        fileMenu.Append(wx.ID_NEW, '&New')
        menuOpen = fileMenu.Append(wx.ID_OPEN, '&Open')
        fileMenu.Append(wx.ID_SAVE, '&Save')
        fileMenu.AppendSeparator()

        qmi = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tCtrl+W')
        fileMenu.Append(qmi)

        self.Bind(wx.EVT_MENU, self.OnQuit, qmi)
        self.Bind(wx.EVT_MENU, self.OnOpen, menuOpen)

        menubar.Append(fileMenu, '&File')

        #View Menubar
        viewMenu = wx.Menu()
        self.shnw = viewMenu.Append(wx.ID_ANY, 'Show Network',
            'Show Statusbar', kind=wx.ITEM_CHECK)

        viewMenu.Check(self.shnw.GetId(), True)

        self.Bind(wx.EVT_MENU, self.ToggleStatusBar, self.shnw)

        menubar.Append(viewMenu, '&View')
        self.SetMenuBar(menubar)

        #self.Panel = DrawPanel() #call DrawPanel class
        self.Fit()

        self.toolbar = self.CreateToolBar()
        self.toolbar.Realize()

        self.statusbar = self.CreateStatusBar()
        self.statusbar.SetStatusText('Ready')

        self.SetSize((700, 600))
        self.SetTitle('Bayesian Network')
        self.Centre()
        self.Show(True)

    def ToggleStatusBar(self, e):
        if self.shnw.IsChecked():
            self.statusbar.Show()
        else:
            self.statusbar.Hide()

    def OnQuit(self, e):
        self.Close()

    def OnOpen(self,e):
        '''Load in existing JSON file - This works currently'''
        dlg = wx.FileDialog(self, "Choose a file", style=wx.FD_OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            filename = dlg.GetFilename()
            dirname = dlg.GetDirectory()
            f= dirname + '/' + filename
            BN.load(f) #Load and parse network from properly-formatted JSON file
        dlg.Destroy()

    def OnSave(self,e):
        '''Save JSON file'''
        pass

#Panel within frame - special type of panel for painting objects on
class DrawPanel(wx.Panel):
    P = [200]
    Qc = [200]
    Qb = [200]  #where to draw circle on canvas
    r = 80 #circle radius
    dim = len(P)
    j = 0 #not sure what these are for
    t = 0
    d = 0

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        # 'Add node' button (calls AddNode function)
        AddNodeBtn = wx.Button(self, label="Add Node")
        AddNodeBtn.Bind(wx.EVT_BUTTON, self.AddNode)

        # 'Do Inference' button (will eventually call DoInference Function from BN)
        InfBtn = wx.Button(self, label="Do Inference")
        InfBtn.Bind(wx.EVT_BUTTON, self.OnInfBtn)

        #Align buttons top left
        Sizer = wx.BoxSizer(wx.HORIZONTAL)
        Sizer.Add(AddNodeBtn, 0, wx.ALIGN_LEFT|wx.ALL, 5)
        Sizer.Add(InfBtn, 0, wx.ALIGN_TOP|wx.ALL, 5)

        self.SetSizerAndFit(Sizer)
        self.InitBuffer()
        self.Buffer = None
        #self.SetBackgroundColour("white")
        self.SetSize((700, 600))

    def InitBuffer(self):
        '''Setup canvas for circle drawing'''
        size=self.GetClientSize()
        self.Buffer = wx.EmptyBitmap(size.width, size.height)
        self.dc = wx.MemoryDC()
        self.dc.SelectObject(self.Buffer)

    def OnPaint(self, evt):
        '''Called when AddNode button is clicked - draw circle and line'''
        self.dc = wx.PaintDC(self)
        self.drawCircle()
        self.Refresh()
        self.InitBuffer
        self.drawLine()

    def drawCircle(self):
        self.dc.SetPen(wx.Pen("black", style=wx.SOLID))
        #self.dc.SetBrush(wx.Brush("red", wx.TRANSPARENT))
        for i in range(self.dim):
            self.dc.DrawCircle(self.P[i], self.Qc[i], self.r)

    def drawLine(self):
        '''Connect circles'''
        self.dc.SetPen(wx.Pen('#4c4c4c', 1, wx.SOLID))

        for i in range(1,self.dim): #will need to store circle dimensions in array so lines can be drawn
            self.dc.DrawLine(self.P[i-1], self.Qc[i-1], self.P[i], self.Qc[i])

    def MouseMove(self, e):
        '''Drag circles and lines'''
        x, y = e.GetPosition()

        if self.d == 1:
            if self.t == 0:
                self.P[self.j] = x
                self.Qc[self.j] = y
            elif self.t == 1:
                self.P[self.j] = x
                self.Qb[self.j] = y
            else: pass

            self.drawCircle()
            self.drawLine()
            self.Refresh()
            self.InitBuffer

        else: pass

    def MouseUp(self, e):
        '''Drag circles and lines'''
        self.d = 0

    def MouseDown(self, e):
        '''Drag circles and lines'''
        self.d = 1

        x, y = e.GetPosition()

        for i in range(self.dim):
            P = abs(self.P[i]-abs(x))//self.r
            Qb = abs(self.Qb[i]-abs(y))//self.r
            Qc = abs(self.Qc[i]-abs(y))//self.r

            if P == 0:
                if Qb == 0:
                    self.j = i
                    self.t = 1
                elif Qc == 0:
                    self.j = i
                    self.t = 0
                else: pass
            else: pass

    def AddNode(self, e):
        '''Create a node from scratch - Only draws circle right now (won't draw multiple circles if you click button multiple times)'''
        getInputs() #get info needed - eventually display within circles (maybe), or try and allow for right clicking in circle and open 'properties'
        self.Bind(wx.EVT_PAINT, self.OnPaint) #draw circles
        self.Bind(wx.EVT_LEFT_DOWN, self.MouseDown) #allow for moving of cirlces/lines
        self.Bind(wx.EVT_LEFT_UP, self.MouseUp)
        self.Bind(wx.EVT_MOTION, self.MouseMove)

    def OnInfBtn(self, event=None):
        """Show results (eventually)."""
        #this will call BN.doInference eventually and display results - need to think about display
        dlg = wx.MessageDialog(self,
                               message='Here is your answer',
                               caption='Inference',
                               style=wx.OK|wx.ICON_INFORMATION
                               )
        dlg.ShowModal()
        dlg.Destroy()

def getInputs(parent = None):
    '''Get inputs needed to create node and add to inputs to global lists in BN'''
    #Input name
    dlg = wx.TextEntryDialog(parent, 'Please enter name of node: ')
    dlg.ShowModal()
    BN.nodesSave.append(dlg.GetValue)

    #Input parents - CHANGE -> ask for number of parents and have new window pop up for each
    dlg.Destroy()
    dlg2 = wx.TextEntryDialog(parent, 'Please enter names of parents: ')
    dlg2.ShowModal()
    BN.parentsSave.append(dlg2.GetValue)
    dlg2.Destroy()

    #Input States - same as parents (ask for number first)
    #Input cpts (ideally grab parent states and create table to allow user to enter)

def main():
    bn = wx.App()
    g = BNGUI(None)
    DrawPanel(g)
    bn.MainLoop()

if __name__ == '__main__':
     main()


import sqlalchemy as sa
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime

# / imports

# Database should locate in project folder, change DB_PATH if not
DB_PATH = "sqlite:///sochi_athletes.sqlite3"
Base = declarative_base()

class User(Base):
    __tablename__ = "user"
    id = sa.Column(sa.INTEGER, primary_key=True, autoincrement=True)
    first_name = sa.Column(sa.Text)
    last_name = sa.Column(sa.Text)
    gender = sa.Column(sa.Text)
    birthdate = sa.Column(sa.DATE)
    height = sa.Column(sa.INTEGER)

def connect_db():
    engine = sa.create_engine(DB_PATH)
    Base.metadata.create_all(engine)
    session = sessionmaker(engine)
    return session()

def check_dob():
    """CHECK FOR RIGHT DOB FORMAT"""
    while True:
        try:
            dob_correct = datetime.strptime(input("Date of birth DD.MM.YYYY: "), '%d.%m.%Y')
        except ValueError:
            print('use DD.MM.YYYY format!')
        else:
            return dob_correct

def request_data():
    print("Fill data")
    first_name = input("First name: ").capitalize()
    last_name = input("Last name: ").capitalize()
    gender = input("Gender: ").capitalize()
    dob = check_dob()
    height = input("height: ")
    try:
        int(height)
    except ValueError:
        height = float(height)
    else:
        height = float('{:.2f}'.format(int(height)/100))

    user = User(
        first_name=first_name,
        last_name=last_name,
        gender=gender,
        birthdate=dob,
        height=height
    )
    return user

def write_data():
    session = connect_db()
    new_user = request_data()
    session.add(new_user)
    session.commit()
    print("Data saved")

if __name__ == '__main__':
    write_data()


from django.shortcuts import render
from django.core.cache import cache
# Create your views here.

def home(request):
    mv = cache.get('movie','has_expired')
    if mv == 'has_expired':
        cache.set('movie','The Manjhi', 30)
        mv = cache.get('movie')

    return render(request, 'enroll/course.html', {'fm':mv})


#!/usr/bin/env python
import argparse
import os
import warnings
from astropy._erfa import ErfaWarning

parser = argparse.ArgumentParser(description='Instance catalog generator')
parser.add_argument('--db', type=str,
                    help='path to the OpSim database to query')
parser.add_argument('--agn_db_name', type=str,
                    help='File of AGN parameters generated by create_agn_db.py')
parser.add_argument('--host_image_dir', type=str,
                    help='Location of FITS stamps of lensed host images produced by generate_lensed_hosts_***.py',
                    default=os.path.join(os.environ['SIMS_GCRCATSIMINTERFACE_DIR'], 'data',
                                         'outputs'))
parser.add_argument('--host_data_dir', type=str,
                    help='Name of csv file of lensed host data created by the sprinkler.', default=os.path.join(os.environ['TWINKLES_DIR'], 'data'))
parser.add_argument('--descqa_catalog', type=str, default='protoDC2',
                    help='the desired DESCQA catalog')
parser.add_argument('--out_dir', type=str, default='.',
                    help='directory where output will be written')
parser.add_argument('--ids', type=int, nargs='+',
                    default=None,
                    help='obsHistID to generate InstanceCatalog for (a list)')
parser.add_argument('--disable_dithering', default=False,
                    action='store_true',
                    help='flag to disable dithering')
parser.add_argument('--min_mag', type=float, default=10.0,
                    help='the minimum magintude for stars')
parser.add_argument('--fov', type=float, default=2.0,
                    help='field of view radius in degrees')
parser.add_argument('--enable_proper_motion', default=False,
                    action='store_true',
                    help='flag to enable proper motion')
parser.add_argument('--minsource', type=int, default=100,
                    help='mininum #objects in a trimmed instance catalog')
parser.add_argument('--protoDC2_ra', type=float, default=0,
                    help='RA (J2000 degrees) of the new protoDC2 center')
parser.add_argument('--protoDC2_dec', type=float, default=0,
                    help='Dec (J2000 degrees) of the new protoDC2 center')
parser.add_argument('--enable_sprinkler', default=False, action='store_true',
                    help='flag to enable the sprinkler')
parser.add_argument('--suppress_warnings', default=False, action='store_true',
                    help='flag to suppress warnings')
args = parser.parse_args()

with warnings.catch_warnings():
    if args.suppress_warnings:
        warnings.filterwarnings('ignore', '\nThis call', UserWarning)
        warnings.filterwarnings('ignore', 'Duplicate object type', UserWarning)
        warnings.filterwarnings('ignore', 'No md5 sum', UserWarning)
        warnings.filterwarnings('ignore', 'ERFA function', ErfaWarning)
        warnings.filterwarnings('ignore', 'Numpy has detected', FutureWarning)
        warnings.filterwarnings('ignore', 'divide by zero', RuntimeWarning)
        warnings.filterwarnings('ignore', 'invalid value', RuntimeWarning)

    from desc.sims.GCRCatSimInterface import InstanceCatalogWriter
    instcat_writer = InstanceCatalogWriter(args.db, args.descqa_catalog,
                                           dither=not args.disable_dithering,
                                           min_mag=args.min_mag,
                                           minsource=args.minsource,
                                           proper_motion=args.enable_proper_motion,
                                           protoDC2_ra=args.protoDC2_ra,
                                           protoDC2_dec=args.protoDC2_dec,
                                           agn_db_name=args.agn_db_name,
                                           host_image_dir=args.host_image_dir,
                                           host_data_dir=args.host_data_dir,
                                           sprinkler=args.enable_sprinkler)

    for obsHistID in args.ids:
        instcat_writer.write_catalog(obsHistID, out_dir=args.out_dir, fov=args.fov)


import csv

from django.core.management.base import BaseCommand
from django.utils import timezone
from rest_framework import serializers

from dora.core.models import ModerationStatus
from dora.core.notify import send_moderation_notification
from dora.sirene.models import Establishment
from dora.structures.emails import send_invitation_email
from dora.structures.models import (
    Structure,
    StructureMember,
    StructurePutativeMember,
    StructureSource,
)
from dora.users.models import User

#################
# Format du CSV attendu:
#
# | name                 | siret          | parent         | email                    | is_admin |
# |----------------------|----------------|----------------|--------------------------|----------|
# | Ma structure         | 12345678901234 |                | admin1@str1.com          | TRUE     |
# | Nom de la structure1 | 12345678901234 |                | admin2@str1.com          | TRUE     |
# | Nom de la structure1 | 12345678901234 |                | user1@str1.com           | FALSE    |
# | Nom de l'antenne1    |                | 12345678901234 | admin1@ant1-str1.com     | TRUE     |
# | Mon autre structure  | 45678911234567 |                | admin1@str2.com          | TRUE     |

def structure_from_siret(siret, name, user, for_parent=False):
    try:
        structure = Structure.objects.get(siret=siret)
    except Structure.DoesNotExist:
        try:
            establishment = Establishment.objects.get(siret=siret)
        except Establishment.DoesNotExist:
            if for_parent:
                raise serializers.ValidationError(
                    f"Invalid siret {siret} for parent of {name}"
                )
            else:
                raise serializers.ValidationError(f"Invalid siret {siret} for {name}")
        structure = Structure.objects.create_from_establishment(establishment)
        structure.creator = user
        structure.last_editor = user
        structure.source = StructureSource.objects.get(value="invitations-masse")
        structure.save()
        send_moderation_notification(
            structure,
            user,
            "Structure créée à partir d'une invitation en masse",
            ModerationStatus.VALIDATED,
        )
    if not for_parent and structure.name != name:
        structure.name = name
        structure.last_editor = user
        structure.modification_date = timezone.now()
        structure.save()
    return structure

def clean_siret(value):
    return "".join([c for c in value if c.isdigit()])

class InviteSerializer(serializers.Serializer):
    name = serializers.CharField()
    siret = serializers.CharField(allow_blank=True)
    parent = serializers.CharField(allow_blank=True)
    email = serializers.EmailField()
    is_admin = serializers.BooleanField()

    def validate(self, data):
        user = User.objects.get_dora_bot()
        name = data["name"]
        siret = clean_siret(data["siret"])
        parent = clean_siret(data["parent"])
        if siret and parent:
            raise serializers.ValidationError(
                f"Expecting only one of siret, parent in {name}"
            )
        if not siret and not parent:
            raise serializers.ValidationError(
                f"Expecting one of siret or parent in {name}"
            )
        if siret:
            data["structure"] = structure_from_siret(siret, name, user)
        if parent:
            data["parent_structure"] = structure_from_siret(
                parent, name, user, for_parent=True
            )
        return data

class Command(BaseCommand):
    help = "Mass-send user invitations"

    def add_arguments(self, parser):
        parser.add_argument("filename")

    def handle(self, *args, **options):
        filename = options["filename"]
        bot_user = User.objects.get_dora_bot()
        with open(filename) as invites_file:
            invites = csv.reader(invites_file, delimiter=",")
            next(invites)  # skip headers
            for i, row in enumerate(invites):
                f = InviteSerializer(
                    data={
                        "name": row[0][:255],
                        "siret": row[1],
                        "parent": row[2],
                        "email": row[3],
                        "is_admin": row[4],
                    },
                )
                try:
                    f.is_valid(raise_exception=True)
                    data = f.validated_data
                    structure = data.get("structure")
                    if structure:
                        target_structure = structure

                    parent = data.get("parent_structure")
                    if parent:
                        branch, created = Structure.objects.get_or_create(
                            name=data["name"], parent=parent
                        )
                        if created:
                            branch.creator = bot_user
                            branch.last_editor = bot_user
                            branch.source = StructureSource.objects.get(
                                value="invitations-masse"
                            )
                            branch.modification_date = timezone.now()
                            branch.save()
                        target_structure = branch

                    self.invite_user(target_structure, data["email"], data["is_admin"])

                except serializers.ValidationError as err:
                    self.stderr.write(f"{i}: {row}")
                    self.stderr.write(str(err))

    def invite_user(self, structure, email, is_admin):
        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            user = User.objects.create_user(
                email,
            )
        try:
            member = StructurePutativeMember.objects.get(user=user, structure=structure)
            self.stdout.write(f"Member {member.user.email} already invited")
            if is_admin is True and not member.is_admin:
                member.is_admin = True
                member.save()
        except StructurePutativeMember.DoesNotExist:
            try:
                member = StructureMember.objects.get(user=user, structure=structure)
                self.stdout.write(f"Member {member.user.email} already exists")
                if is_admin is True and not member.is_admin:
                    member.is_admin = True
                    member.save()
            except StructureMember.DoesNotExist:
                member = StructurePutativeMember.objects.create(
                    user=user,
                    structure=structure,
                    invited_by_admin=True,
                    is_admin=is_admin,
                )
                self.stdout.write(f"Inviting {member.user.email}")
                send_invitation_email(
                    member,
                    "L’équipe DORA",
                )


import logging

from django_eth.constants import NULL_ADDRESS
from django_eth.tests.factories import get_eth_address_with_key
from gnosis.safe.contracts import get_safe_contract
from gnosis.safe.safe_service import (GasPriceTooLow, InvalidMasterCopyAddress,
                                      InvalidRefundReceiver,
                                      NotEnoughFundsForMultisigTx)
from gnosis.safe.tests.factories import deploy_safe, generate_safe
from gnosis.safe.tests.test_safe_service import GAS_PRICE, TestSafeService
from hexbytes import HexBytes

from safe_relay_service.gas_station.gas_station import GasStationMock

from ..relay_service import (RefundMustBeEnabled, RelayService,
                             RelayServiceProvider)

logger = logging.getLogger(__name__)

class TestRelayService(TestSafeService):

    def test_relay_provider_singleton(self):
        relay_service1 = RelayServiceProvider()
        relay_service2 = RelayServiceProvider()
        self.assertEqual(relay_service1, relay_service2)

    def test_relay_send_multisig_tx(self):
        gas_station = GasStationMock()
        relay_service = RelayService(self.safe_service, gas_station)
        # Create Safe
        w3 = self.w3
        funder = w3.eth.accounts[0]
        owners_with_keys = [get_eth_address_with_key(), get_eth_address_with_key()]
        # Signatures must be sorted!
        owners_with_keys.sort(key=lambda x: x[0].lower())
        owners = [x[0] for x in owners_with_keys]
        keys = [x[1] for x in owners_with_keys]
        threshold = len(owners_with_keys)

        safe_creation = generate_safe(relay_service, owners=owners, threshold=threshold)
        my_safe_address = deploy_safe(w3, safe_creation, funder)

        # The balance we will send to the safe
        safe_balance = w3.toWei(0.01, 'ether')

        # Send something to the owner[0], who will be sending the tx
        owner0_balance = safe_balance
        w3.eth.waitForTransactionReceipt(w3.eth.sendTransaction({
            'from': funder,
            'to': owners[0],
            'value': owner0_balance
        }))

        my_safe_contract = get_safe_contract(w3, my_safe_address)

        to = funder
        value = safe_balance // 2
        data = HexBytes(0x00)
        operation = 0
        safe_tx_gas = 100000
        data_gas = 300000
        gas_price = gas_station.get_gas_prices().standard
        gas_token = NULL_ADDRESS
        refund_receiver = NULL_ADDRESS
        nonce = relay_service.retrieve_nonce(my_safe_address)
        safe_multisig_tx_hash = relay_service.get_hash_for_safe_tx(safe_address=my_safe_address,
                                                                   to=to,
                                                                   value=value,
                                                                   data=data,
                                                                   operation=operation,
                                                                   safe_tx_gas=safe_tx_gas,
                                                                   data_gas=data_gas,
                                                                   gas_price=gas_price,
                                                                   gas_token=gas_token,
                                                                   refund_receiver=refund_receiver,
                                                                   nonce=nonce)

        # Just to make sure we are not miscalculating tx_hash
        contract_multisig_tx_hash = my_safe_contract.functions.getTransactionHash(
            to,
            value,
            data,
            operation,
            safe_tx_gas,
            data_gas,
            gas_price,
            gas_token,
            refund_receiver,
            nonce).call()

        self.assertEqual(safe_multisig_tx_hash, contract_multisig_tx_hash)

        signatures = [w3.eth.account.signHash(safe_multisig_tx_hash, private_key) for private_key in keys]
        signature_pairs = [(s['v'], s['r'], s['s']) for s in signatures]
        signatures_packed = relay_service.signatures_to_bytes(signature_pairs)

        # {bytes32 r}{bytes32 s}{uint8 v} = 65 bytes
        self.assertEqual(len(signatures_packed), 65 * len(owners))

        # Recover key is now a private function
        # Make sure the contract retrieves the same owners
        # for i, owner in enumerate(owners):
        #    recovered_owner = my_safe_contract.functions.recoverKey(safe_multisig_tx_hash, signatures_packed, i).call()
        #    self.assertEqual(owner, recovered_owner)

        self.assertTrue(relay_service.check_hash(safe_multisig_tx_hash, signatures_packed, owners))

        # Check owners are the same
        contract_owners = my_safe_contract.functions.getOwners().call()
        self.assertEqual(set(contract_owners), set(owners))
        self.assertEqual(w3.eth.getBalance(owners[0]), owner0_balance)

        with self.assertRaises(NotEnoughFundsForMultisigTx):
            relay_service.send_multisig_tx(
                my_safe_address,
                to,
                value,
                data,
                operation,
                safe_tx_gas,
                data_gas,
                gas_price,
                gas_token,
                refund_receiver,
                signatures_packed,
                tx_sender_private_key=keys[0]
            )

        # Send something to the safe
        w3.eth.waitForTransactionReceipt(w3.eth.sendTransaction({
            'from': funder,
            'to': my_safe_address,
            'value': safe_balance
        }))

        bad_refund_receiver = get_eth_address_with_key()[0]
        with self.assertRaises(InvalidRefundReceiver):
            relay_service.send_multisig_tx(
                my_safe_address,
                to,
                value,
                data,
                operation,
                safe_tx_gas,
                data_gas,
                gas_price,
                gas_token,
                bad_refund_receiver,
                signatures_packed,
                tx_sender_private_key=keys[0]
            )

        invalid_gas_price = 0
        with self.assertRaises(RefundMustBeEnabled):
            relay_service.send_multisig_tx(
                my_safe_address,
                to,
                value,
                data,
                operation,
                safe_tx_gas,
                data_gas,
                invalid_gas_price,
                gas_token,
                refund_receiver,
                signatures_packed,
                tx_sender_private_key=keys[0]
            )

        with self.assertRaises(GasPriceTooLow):
            relay_service.send_multisig_tx(
                my_safe_address,
                to,
                value,
                data,
                operation,
                safe_tx_gas,
                data_gas,
                gas_station.get_gas_prices().standard - 1,
                gas_token,
                refund_receiver,
                signatures_packed,
                tx_sender_private_key=keys[0]
            )

        sent_tx_hash, tx = relay_service.send_multisig_tx(
            my_safe_address,
            to,
            value,
            data,
            operation,
            safe_tx_gas,
            data_gas,
            gas_price,
            gas_token,
            refund_receiver,
            signatures_packed,
            tx_sender_private_key=keys[0]
        )

        tx_receipt = w3.eth.waitForTransactionReceipt(sent_tx_hash)
        self.assertTrue(tx_receipt['status'])
        owner0_new_balance = w3.eth.getBalance(owners[0])
        gas_used = tx_receipt['gasUsed']
        gas_cost = gas_used * GAS_PRICE
        estimated_payment = (data_gas + gas_used) * gas_price
        real_payment = owner0_new_balance - (owner0_balance - gas_cost)
        # Estimated payment will be bigger, because it uses all the tx gas. Real payment only uses gas left
        # in the point of calculation of the payment, so it will be slightly lower
        self.assertTrue(estimated_payment > real_payment > 0)
        self.assertTrue(owner0_new_balance > owner0_balance - tx['gas'] * GAS_PRICE)
        self.assertEqual(relay_service.retrieve_nonce(my_safe_address), 1)


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

def read_file(filename):
  file = open(filename, 'r')
  u = []

  for line in file:
    data = line.split()

    u.append(float(data[0]))

  file.close()

  u = np.array(u)
  return u

filename_gauss = 'Histogram_gaussStepLength.dat'
filename_const = 'Histogram_constStepLength.dat'
u_gauss = read_file(filename_gauss)
u_const = read_file(filename_const)

plt.hist(u_const, bins=50, color='green')
plt.title('Particle concentration (constant step length)')
plt.xlabel(r'Position $x$')
plt.ylabel(r'Consentration $u(x)$')
plt.savefig(filename_const.split('.')[0]+'.png')
plt.show()

plt.hist(u_gauss, bins=100, color='blue', histtype='step')
plt.title('Particle concentration (gaussian random step length)')
plt.xlabel(r'Position $x$')
plt.ylabel(r'Consentration $u(x)$')
plt.savefig(filename_gauss.split('.')[0]+'.png')
plt.show()


# -*- coding: utf-8 -*-
from selenium import webdriver
import smtplib
import datetime
import time

# Starting the Chrome Webdriver and sending us straight to floor plans
driver = webdriver.Chrome('C:/chromedriver.exe')
driver.get("http://www.vitatysons.com/floor-plans/")

# Checks to see if any studio apartments are found and if so, then will
# call the checkPrice() method to see if they are affordable or not
def checkStudios():
    print("Checking for studios...")
    driver.find_element_by_class_name("block-0").click()
    found = driver.find_element_by_class_name("results-found").text
    if "Apartments found: " in found:
        print(found)
        checkPrice()
        driver.back()
    else:
        print("None available")
        driver.back()

# Checks to see if any one bedrooms are found and if so, will
# call the checkPrice() method to see if they are affordable or not
def checkOneBeds():
    print("Checking for one bedrooms...")
    driver.find_element_by_class_name("block-1").click()
    found = driver.find_element_by_class_name("results-found").text
    if "Apartments found: " in found:
        print(found)
        checkPrice()
        driver.back()
    else:
        print("None available")
        driver.back()

# This finds and sends the price to be split into something more searchable
def checkPrice():
    print("Checking the prices...")
    try:
        price = driver.find_element_by_css_selector("body > div.container-fluid > div.floorplans.list > div > section.inner-section > div.section-2 > div > div:nth-child(3) > a > figure > figcaption > div > ul > li:nth-child(4)").text
        splitForPice(price)
    except:
        print("Couldn't find the price")

# This method is splitting the format from "STARTING FROM $3,000/MONTH"
# to just $3,000, which is then parsed to see if it is affordable or not
# If it is affordable, then it will call the sendEmail() method to send
# me a notification
def splitForPice(text):
    words = text.split("/")
    amount = words[0].split()
    price = amount[1]

    if "1," in price:
        print(amount[1])
        print("BUY NOW")
        sendEmail()
    if "2,0" in price:
        print(amount[1])
        sendEmail()
    else:
        print("Everything is too expensive right now!")

# Method establishes a server and will send me an email, letting me know
# that something affordable is available
def sendEmail():
    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.ehlo()
    server.starttls()
    fromadd = 'khan.taimore@gmail.com'
    toadd = 'khan.taimore@gmail.com'
    subject = "AFFORDABLE APARTMENT ALERT"
    text = "GO ONLINE NOW AND CHECK IT OUT! http://www.vitatysons.com/floor-plans/"
    server.login("khan.taimore@gmail.com", "PASSWORDGOESHERE")
    msg = """From: %s\nTo: %s\nSubject: %s\n\n%s""" % (fromadd, toadd, subject, text)

    server.sendmail(fromadd, toadd, msg)
    server.quit()

# This method just prints out the date and time so I can see when I last
# ran it
def printDate():
	print(time.strftime("%m-%d-%Y %H:%M:%S"))

printDate()
checkStudios()
checkOneBeds()

driver.close()


# -*- coding: utf-8 -*-
"""
Created on Thu Nov 16 10:00:32 2017

@author: Raluca Sandu
"""

# -*- coding: utf-8 -*-
"""
Created on Thu Nov  9 13:43:49 2017

@author: Raluca Sandu
"""
# libraries import

import numpy as np
import pandas as pd
from enum import Enum
from medpy import metric
import SimpleITK as sitk
import matplotlib.pyplot as plt
#%%
class DistanceMetrics(object):

    def __init__(self, maskfile , referencefile, flag_symmetric, flag_mask2reference, flag_reference2mask, flag_plot):

        ''' Read the images from the filepaths'''
        reference_segmentation = sitk.ReadImage(referencefile, sitk.sitkUInt8)
        segmentation = sitk.ReadImage(maskfile,sitk.sitkUInt8)

        ''' init the enum fields for surface dist measures computer with simpleitk'''
        class SurfaceDistanceMeasuresITK(Enum):
            hausdorff_distance, max_distance, min_surface_distance, mean_surface_distance, median_surface_distance, std_surface_distance = range(6)

        class MedpyMetricDists(Enum):
            hausdorff_distanceMedPy, avg_surface_distanceMedPy, avg_symmetric_surface_distanceMedPy = range(3)

        surface_distance_results = np.zeros((1,len(SurfaceDistanceMeasuresITK.__members__.items())))
        surface_dists_Medpy = np.zeros((1,len(MedpyMetricDists.__members__.items())))

        #%%
        ''''
        Mauerer Distance Map for the Reference Object (deemed as "tumor in this particular case")
        Algorithm Pipeline :
            1. compute the contour surface of the object (6-pixel-connectivity neighborhood)
            2. convert from SimpleITK format to Numpy Array Img
            3. remove the zeros from the contour of the object, NOT from the distance map
            4. compute the number of 1's pixels in the contour
            5. instantiate the Signed Mauerer Distance map for the object (negative numbers also)
        '''
        reference_surface = sitk.LabelContour(reference_segmentation)
        reference_surface_array = sitk.GetArrayFromImage(reference_surface)
        reference_surface_array_NonZero = reference_surface_array.nonzero()
        self.num_reference_surface_pixels = len(list(zip(reference_surface_array_NonZero[0], reference_surface_array_NonZero[1], reference_surface_array_NonZero[2])))

        # init signed mauerer distance as reference metrics from SimpleITK
        self.reference_distance_map = sitk.SignedMaurerDistanceMap(reference_segmentation, squaredDistance=False, useImageSpacing=True)

        ''''compute Hausdorff distance also known as maximum symmetric distance
        - doesn't matter whether is computed from mask to reference (ablation, tumor) as it selects the maximum amongs the two
        '''
        hausdorff_distance_filter = sitk.HausdorffDistanceImageFilter()
        hausdorff_distance_filter.Execute(reference_segmentation, segmentation)
        surface_distance_results[0,SurfaceDistanceMeasuresITK.hausdorff_distance.value] = hausdorff_distance_filter.GetHausdorffDistance()

        #%%
        ''' Mauerer Distance Map for the Mask Object (deemed as "ablation" in this particular case)'''
        segmented_surface_mask = sitk.LabelContour(segmentation) #aka the ablation file
        segmented_surface_mask_array = sitk.GetArrayFromImage(segmented_surface_mask)
        surface_mask_array_NonZero = segmented_surface_mask_array.nonzero()

        # Get the number of pixels in the mask surface by counting all pixels that are non-zero
        self.num_segmented_surface_pixels = len(list(zip(surface_mask_array_NonZero[0],surface_mask_array_NonZero[1], surface_mask_array_NonZero[2])))

        # init Mauerer Distance
        self.mask_distance_map = sitk.SignedMaurerDistanceMap(segmentation, squaredDistance=False, useImageSpacing=True)
        #%%
        # Multiply the binary surface segmentations with the distance maps. The resulting distance
        # maps contain non-zero values only on the surface (they can also contain zero on the surface)
        mask_distance_map_array = sitk.GetArrayFromImage(self.mask_distance_map)
        reference_distance_map_array = sitk.GetArrayFromImage(self.reference_distance_map)

        '''compute the contours multiplied with the euclidean distances'''
        self.seg2ref_distance_map = mask_distance_map_array*reference_surface_array
        self.ref2seg_distance_map = reference_distance_map_array*segmented_surface_mask_array
 #%%
        '''
        - plot the distances as heatmap one slice for verification
        '''
        if flag_plot is True:
            fig1, ax1= plt.subplots()
            z = int(np.floor(np.shape(reference_distance_map_array)[0]/2))
            heatmap = ax1.imshow(reference_distance_map_array[z,:,:])
            plt.title('Distance Map for Tumor. 1 Slice Visualization')
            plt.colorbar(heatmap)

            fig2, ax2= plt.subplots()
            z = int(np.floor(np.shape(self.ref2seg_distance_map)[0]/2))
            heatmap = ax2.imshow(self.ref2seg_distance_map[z,:,:]/255)
            plt.title('Tumor to Ablation Surface Distances. 1 Slice Visualization')
            plt.colorbar(heatmap)

            fig3, ax3= plt.subplots()
            z = int(np.floor(np.shape(mask_distance_map_array)[0]/2))
            heatmap = ax3.imshow(mask_distance_map_array[z,:,:])
            plt.title('Distance Map for Ablation. 1 Slice Visualization')
            plt.colorbar(heatmap)

            fig4, ax4= plt.subplots()
            z = int(np.floor(np.shape(self.seg2ref_distance_map)[0]/2))
            heatmap = ax4.imshow(self.seg2ref_distance_map[z,:,:]/-255)
            plt.title('Ablation To Tumor Surface Distances. 1 Slice Visualization')
            plt.colorbar(heatmap)

#%%
        '''remove the zeros from the surface contour(indexes) from the distance maps '''
        self.seg2ref_distances = list(self.seg2ref_distance_map[reference_surface_array_NonZero]/-255)
        self.ref2seg_distances = list(self.ref2seg_distance_map[surface_mask_array_NonZero]/255)

        if flag_symmetric is True:
            self.surface_distances = self.seg2ref_distances + self.ref2seg_distances
        if flag_mask2reference is True:
            self.surface_distances = self.seg2ref_distances
        if flag_reference2mask is True:
            self.surface_distances = self.ref2seg_distances
        #%%
        ''' Compute the surface distances max, min, mean, median, std '''
        surface_distance_results[0,SurfaceDistanceMeasuresITK.max_distance.value] = np.max(self.surface_distances)
        surface_distance_results[0,SurfaceDistanceMeasuresITK.min_surface_distance.value] = np.min(self.surface_distances)
        surface_distance_results[0,SurfaceDistanceMeasuresITK.mean_surface_distance.value] = np.mean(self.surface_distances)
        surface_distance_results[0,SurfaceDistanceMeasuresITK.median_surface_distance.value] = np.median(self.surface_distances)
        surface_distance_results[0,SurfaceDistanceMeasuresITK.std_surface_distance.value] = np.std(self.surface_distances)

        # Save to DataFrame
        self.surface_distance_results_df = pd.DataFrame(data=surface_distance_results, index = list(range(1)),
                                      columns=[name for name, _ in SurfaceDistanceMeasuresITK.__members__.items()])

        # change the name of the columns
        if flag_symmetric is True:
            self.surface_distance_results_df.columns = ['Hausdorff', 'Maximum Symmetric', 'Minimum Symmetric', 'Mean Symmetric', 'Median Symmetric', 'Std']

        if flag_reference2mask is True:
            self.surface_distance_results_df.columns = ['Hausdorff_TA', 'Maximum_TA', 'Minimum_TA', 'Mean_TA', 'Median_TA', 'Std_TA']

        if flag_mask2reference is True:
            self.surface_distance_results_df.columns = ['Hausdorff_AT', 'Maximum_AT', 'Minimum_AT', 'Mean_AT', 'Median_AT', 'Std_AT']

        #%%
        ''' use MedPy library for comparision with SimpleITK that values are in the same range'''
        img_array = sitk.GetArrayFromImage(reference_segmentation)
        seg_array = sitk.GetArrayFromImage(segmentation)
        # reverse array in the order x, y, z
        img_array_rev = np.flip(img_array,2)
        seg_array_rev = np.flip(seg_array,2)
        vxlspacing = segmentation.GetSpacing()

        # use the MedPy metric library
        surface_dists_Medpy[0,MedpyMetricDists.hausdorff_distanceMedPy.value] = metric.binary.hd(seg_array_rev,img_array_rev, voxelspacing=vxlspacing)
        surface_dists_Medpy[0,MedpyMetricDists.avg_surface_distanceMedPy.value] = metric.binary.asd(seg_array_rev,img_array_rev, voxelspacing=vxlspacing)
        surface_dists_Medpy[0,MedpyMetricDists.avg_symmetric_surface_distanceMedPy.value] = metric.binary.assd(seg_array_rev,img_array_rev, voxelspacing=vxlspacing)
        self.surface_dists_Medpy_df = pd.DataFrame(data=surface_dists_Medpy, index = list(range(1)),
                                      columns=[name for name, _ in MedpyMetricDists.__members__.items()])

    #%%

    def get_Distances(self):
        # convert to DataFrame
        metrics_all = pd.concat([self.surface_dists_Medpy_df, self.surface_distance_results_df], axis=1)
        return(metrics_all)

    def get_SitkDistances(self):
        return self.surface_distance_results_df

    def get_MedPyDistances(self):
        return self.surface_dists_Medpy_df

    def get_avg_symmetric_dist(self):
        return (self.n1*self.avg_ref + self.n2*self.avg_seg)/(self.n1+self.n2)

    def get_std_symmetric_dist(self):
        return self.symmetric_std

    def get_mask_dist_map(self):
        return self.seg2ref_distance_map

    def get_reference_dist_map(self):
        return self.ref2seg_distance_map

    def get_ref2seg_distances(self):
        return self.ref2seg_distances

    def get_seg2ref_distances(self):
        return self.seg2ref_distances

    def get_all_distances(self):
        return self.all_surface_distances


"""Test HomematicIP Cloud accesspoint."""
from unittest.mock import Mock, patch

from homeassistant.components.homematicip_cloud import hap as hmipc
from homeassistant.components.homematicip_cloud import const, errors
from tests.common import mock_coro

async def test_auth_setup(hass):
    """Test auth setup for client registration."""
    config = {
        const.HMIPC_HAPID: 'ABC123',
        const.HMIPC_PIN: '123',
        const.HMIPC_NAME: 'hmip',
    }
    hap = hmipc.HomematicipAuth(hass, config)
    with patch.object(hap, 'get_auth', return_value=mock_coro()):
        assert await hap.async_setup() is True

async def test_auth_setup_connection_error(hass):
    """Test auth setup connection error behaviour."""
    config = {
        const.HMIPC_HAPID: 'ABC123',
        const.HMIPC_PIN: '123',
        const.HMIPC_NAME: 'hmip',
    }
    hap = hmipc.HomematicipAuth(hass, config)
    with patch.object(hap, 'get_auth',
                      side_effect=errors.HmipcConnectionError):
        assert await hap.async_setup() is False

async def test_auth_auth_check_and_register(hass):
    """Test auth client registration."""
    config = {
        const.HMIPC_HAPID: 'ABC123',
        const.HMIPC_PIN: '123',
        const.HMIPC_NAME: 'hmip',
    }
    hap = hmipc.HomematicipAuth(hass, config)
    hap.auth = Mock()
    with patch.object(hap.auth, 'isRequestAcknowledged',
                      return_value=mock_coro()), \
            patch.object(hap.auth, 'requestAuthToken',
                         return_value=mock_coro('ABC')), \
            patch.object(hap.auth, 'confirmAuthToken',
                         return_value=mock_coro()):
        assert await hap.async_checkbutton() is True
        assert await hap.async_register() == 'ABC'

async def test_hap_setup_works(aioclient_mock):
    """Test a successful setup of a accesspoint."""
    hass = Mock()
    entry = Mock()
    home = Mock()
    entry.data = {
        hmipc.HMIPC_HAPID: 'ABC123',
        hmipc.HMIPC_AUTHTOKEN: '123',
        hmipc.HMIPC_NAME: 'hmip',
    }
    hap = hmipc.HomematicipHAP(hass, entry)
    with patch.object(hap, 'get_hap', return_value=mock_coro(home)):
        assert await hap.async_setup() is True

    assert hap.home is home
    assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 6
    assert hass.config_entries.async_forward_entry_setup.mock_calls[0][1] == \
        (entry, 'alarm_control_panel')
    assert hass.config_entries.async_forward_entry_setup.mock_calls[1][1] == \
        (entry, 'binary_sensor')

async def test_hap_setup_connection_error():
    """Test a failed accesspoint setup."""
    hass = Mock()
    entry = Mock()
    entry.data = {
        hmipc.HMIPC_HAPID: 'ABC123',
        hmipc.HMIPC_AUTHTOKEN: '123',
        hmipc.HMIPC_NAME: 'hmip',
    }
    hap = hmipc.HomematicipHAP(hass, entry)
    with patch.object(hap, 'get_hap',
                      side_effect=errors.HmipcConnectionError):
        assert await hap.async_setup() is False

    assert len(hass.async_add_job.mock_calls) == 0
    assert len(hass.config_entries.flow.async_init.mock_calls) == 0

async def test_hap_reset_unloads_entry_if_setup():
    """Test calling reset while the entry has been setup."""
    hass = Mock()
    entry = Mock()
    home = Mock()
    entry.data = {
        hmipc.HMIPC_HAPID: 'ABC123',
        hmipc.HMIPC_AUTHTOKEN: '123',
        hmipc.HMIPC_NAME: 'hmip',
    }
    hap = hmipc.HomematicipHAP(hass, entry)
    with patch.object(hap, 'get_hap', return_value=mock_coro(home)):
        assert await hap.async_setup() is True

    assert hap.home is home
    assert len(hass.services.async_register.mock_calls) == 0
    assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 6

    hass.config_entries.async_forward_entry_unload.return_value = \
        mock_coro(True)
    await hap.async_reset()

    assert len(hass.config_entries.async_forward_entry_unload.mock_calls) == 6


#!/usr/bin/env python

# This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>.

"""
Module use to parse all the route and scenario configuration parameters .
"""

from __future__ import print_function
import json
import math
import xml.etree.ElementTree as ET

import carla
from agents.navigation.local_planner import RoadOption

# TODO  check this threshold, it could be a bit larger but not so large that we cluster scenarios.
TRIGGER_THRESHOLD = 2.0  # Threshold to say if a trigger position is new or repeated, works for matching positions
TRIGGER_ANGLE_THRESHOLD = 10  # Threshold to say if two angles can be considering matching when matching transforms.

def parse_annotations_file(annotation_filename):
    """
    Return the annotations of which positions where the scenarios are going to happen.
    :param annotation_filename: the filename for the anotations file
    :return:
    """

    with open(annotation_filename, 'r') as f:
        annotation_dict = json.loads(f.read())

    final_dict = {}

    for town_dict in annotation_dict['available_scenarios']:
        final_dict.update(town_dict)

    return final_dict  # the file has a current maps name that is an one element vec

def parse_routes_file(route_filename):
    """
    Returns a list of route elements that is where the challenge is going to happen.
    :param route_filename: the path to a set of routes.
    :return:  List of dicts containing the waypoints, id and town of the routes
    """

    list_route_descriptions = []
    tree = ET.parse(route_filename)
    for route in tree.iter("route"):
        route_town = route.attrib['map']
        route_id = route.attrib['id']
        waypoint_list = []  # the list of waypoints that can be found on this route
        for waypoint in route.iter('waypoint'):
            waypoint_list.append(carla.Location(x=float(waypoint.attrib['x']),
                                                y=float(waypoint.attrib['y']),
                                                z=float(waypoint.attrib['z'])))

            # Waypoints is basically a list of XML nodes

        list_route_descriptions.append({
            'id': route_id,
                                    'town_name': route_town,
                                    'trajectory': waypoint_list
        })

    return list_route_descriptions

def check_trigger_position(new_trigger, existing_triggers):
    """
    Check if this trigger position already exists or if it is a new one.
    :param new_trigger:
    :param existing_triggers:
    :return:
    """

    for trigger_id in existing_triggers.keys():
        trigger = existing_triggers[trigger_id]
        dx = trigger['x'] - new_trigger['x']
        dy = trigger['y'] - new_trigger['y']
        distance = math.sqrt(dx * dx + dy * dy)
        dyaw = trigger['yaw'] - trigger['yaw']
        dist_angle = math.sqrt(dyaw * dyaw)
        if distance < (TRIGGER_THRESHOLD * 2) and dist_angle < TRIGGER_ANGLE_THRESHOLD:
            return trigger_id

    return None

def convert_waypoint_float(waypoint):
    """
    Convert waypoint values to float
    """
    waypoint['x'] = float(waypoint['x'])
    waypoint['y'] = float(waypoint['y'])
    waypoint['z'] = float(waypoint['z'])
    waypoint['yaw'] = float(waypoint['yaw'])

def match_world_location_to_route(world_location, route_description):
    """
    We match this location to a given route.
        world_location:
        route_description:
    """
    def match_waypoints(waypoint1, wtransform):
        """
        Check if waypoint1 and wtransform are similar
        """
        dx = float(waypoint1['x']) - wtransform.location.x
        dy = float(waypoint1['y']) - wtransform.location.y
        dz = float(waypoint1['z']) - wtransform.location.z
        dist_position = math.sqrt(dx * dx + dy * dy + dz * dz)

        dyaw = float(waypoint1['yaw']) - wtransform.rotation.yaw

        dist_angle = math.sqrt(dyaw * dyaw)

        return dist_position < TRIGGER_THRESHOLD and dist_angle < TRIGGER_ANGLE_THRESHOLD

    match_position = 0
    # TODO this function can be optimized to run on Log(N) time
    for route_waypoint in route_description:
        if match_waypoints(world_location, route_waypoint[0]):
            return match_position
        match_position += 1

    return None

def get_scenario_type(scenario, match_position, trajectory):
    """
    Some scenarios have different types depending on the route.
    :param scenario: the scenario name
    :param match_position: the matching position for the scenarion
    :param trajectory: the route trajectory the ego is following
    :return: 0 for option, 0 ,1 for option
    """

    if scenario == 'Scenario4':
        for tuple_wp_turn in trajectory[match_position:]:
            if RoadOption.LANEFOLLOW != tuple_wp_turn[1]:
                if RoadOption.LEFT == tuple_wp_turn[1]:
                    return 1
                elif RoadOption.RIGHT == tuple_wp_turn[1]:
                    return 0
                return None
        return None

    return 0

def scan_route_for_scenarios(route_description, world_annotations):
    """
    Just returns a plain list of possible scenarios that can happen in this route by matching
    the locations from the scenario into the route description

    :return:  A list of scenario definitions with their correspondent parameters
    """

    # the triggers dictionaries:
    existent_triggers = {}
    # We have a table of IDs and trigger positions associated
    possible_scenarios = {}

    # Keep track of the trigger ids being added
    latest_trigger_id = 0

    for town_name in world_annotations.keys():
        if town_name != route_description['town_name']:
            continue

        scenarios = world_annotations[town_name]
        for scenario in scenarios:  # For each existent scenario
            scenario_name = scenario["scenario_type"]
            for event in scenario["available_event_configurations"]:
                waypoint = event['transform']  # trigger point of this scenario
                convert_waypoint_float(waypoint)
                # We match trigger point to the  route, now we need to check if the route affects
                match_position = match_world_location_to_route(waypoint, route_description['trajectory'])
                if match_position is not None:
                    # We match a location for this scenario, create a scenario object so this scenario
                    # can be instantiated later

                    if 'other_actors' in event:
                        other_vehicles = event['other_actors']
                    else:
                        other_vehicles = None
                    scenario_subtype = get_scenario_type(scenario_name, match_position,
                                                         route_description['trajectory'])
                    if scenario_subtype is None:
                        continue
                    scenario_description = {
                        'name': scenario_name,
                                           'other_actors': other_vehicles,
                                           'trigger_position': waypoint,
                                           'type': scenario_subtype,  # some scenarios have different configurations
                    }

                    trigger_id = check_trigger_position(waypoint, existent_triggers)
                    if trigger_id is None:
                        # This trigger does not exist create a new reference on existent triggers
                        existent_triggers.update({latest_trigger_id: waypoint})
                        # Update a reference for this trigger on the possible scenarios
                        possible_scenarios.update({latest_trigger_id: []})
                        trigger_id = latest_trigger_id
                        # Increment the latest trigger
                        latest_trigger_id += 1

                    possible_scenarios[trigger_id].append(scenario_description)

    return possible_scenarios, existent_triggers