import sys
import getopt
import warnings
import rpyc
import json
import yaml
from PIL import Image, ImageTk
import PySimpleGUI as sg

from PC import PC
from II import II
from MAR import MAR
from REG import REG
from RAM_STACK import STACK
from MEMS import MEMS

from parse_vars import *
from print_call_graph import *

# TODO: dead code analysis
# TODO: call graph to include call/ret/jumps. add subgraph for subroutines
# TODO: extended memory

def main():

  FILE_NAME = 'bin/01_test_a_reg.bin' # default file
  EXIT_ON_HALT = False
  SIM = True
  GUI = True

  # process flags

  OPTIONS = "f:" #TODO: no abbreviated options.
  OPTIONS_LONG = ['file =', 'exit-on-halt', 'no-gui', 'no-sim']

  try:
    # grab flags
    args, vals = getopt.getopt(sys.argv[1:], OPTIONS, OPTIONS_LONG)
    for arg_, val_ in args:
      arg_ = arg_.strip()

      if arg_ in ("--file"):
        FILE_NAME = val_
      elif arg_ in ("--exit-on-halt"):
        EXIT_ON_HALT = True
      elif arg_ in ("--no-gui"):
        GUI = False
        EXIT_ON_HALT = True
      elif arg_ in ("--no-sim"):
        SIM = False

  except getopt.error as err:
    # output error, and return with an error code
    print (str(err))

  if SIM:
    try:
      conn = rpyc.connect("localhost", 18812, config={'allow_all_attrs': True})
    except:
      conn = None
      warnings.warn("Warning. Sim not connected!")
  else:
    conn = None

  print(f"running ${FILE_NAME}")

  pc = PC()
  mar = MAR()
  ii = II()
  mems = MEMS(sim=conn)
  A = REG(8)
  B = REG(8)
  X = REG(8)
  Y = REG(8)
  HL = REG(16)
  J = REG(16)
  K = REG(16)
  D = REG(16)
  stack = STACK(mems)

  clk_counter = 0

  UPDATE_RATE = 1000#10000

  IMG_HEI = 80
  IMG_WID = 101

  addr = 0
  data = 0
  UCC = 0

  flags = {
    'ZF' : 0,
    'CF' : 0,
    'NF' : 0
  }

  ctrl = {
    # outs
    'PO' : 0, # Program counter out
    'AO' : 0, # A out
    'BO' : 0, # B out
    'MO' : 0, # Memory out
    'NO' : 0, # Stack pointer out
    'SO' : 0, # Stack out
    'HO' : 0, # HL register out
    'JO' : 0, # J Scratch register out
    'KO' : 0, # K Scratch register out
    'DO' : 0, # D Scratch register out
    'HT' : 0, # Halt
    # ins
    'PI' : 0, # Program counter in
    'II' : 0, # Instruction register in
    'XI' : 0, # Alu X Register in
    'YI' : 0, # Alu X Register in
    'AI' : 0, # A in
    'BI' : 0, # Bi in
    'HI' : 0, # HL register in
    'MA' : 0, # MAR in
    'MI' : 0, # Memory in
    'NI' : 0, # Stack Pointer in
    'SI' : 0, # Stack in
    'JI' : 0, # J Scratch register in
    'KI' : 0, # K Scratch register in
    'DI' : 0, # D Scratch register in
    # alu
    'ADD' : 0, # Add
    'SUB' : 0, # Subtract
    'AND' : 0, # Logical AND
    'OR'  : 0, # Logical OR
    'XOR' : 0, # Logical XOR
    'SHL' : 0, # Logical Shift Left
    'SHR' : 0, # Logical Shift Right
    'ONES': 0, # Outputs 0xFF
    'ZERO': 0, # Outputs 0x00
    'ONE' : 0, # Outputs 0x01
    #misc
    'CE' : 0, # Program counter increment
    'FI' : 0, # CPU Flag refresh
    'MC' : 0, # MAR increment
    'IS' : 0, # increment stack pointer
    'DS' : 0, # decrement stack pointer
    'LM' : 0, # bus byte indictor
    'RU' : 0  # reset ucode counter
  }

  breakpt = False
  finish_inst = False

  DEFAULT_REG_LAYOUT = {
    'size':(6,1),
    'justification':'left',
    'text_color':'black',
    'background_color':'white'
  }

  layout_regs = [
    [sg.T('Regs')],
    [sg.T('PC  '), sg.T('0x0000', **DEFAULT_REG_LAYOUT, key='_PC_')],
    [sg.T('MAR '), sg.T('0x0000', **DEFAULT_REG_LAYOUT, key='_MAR_')],
    [sg.T('INST'), sg.T('0x00  ', **DEFAULT_REG_LAYOUT, key='_INST_')],
    [sg.T('UCC '), sg.T('0x00  ', **DEFAULT_REG_LAYOUT, key='_UCC_')],
    [sg.T('A   '), sg.T('0x00  ', **DEFAULT_REG_LAYOUT, key='_A_')],
    [sg.T('B   '), sg.T('0x00  ', **DEFAULT_REG_LAYOUT, key='_B_')],
    [sg.T('HL  '), sg.T('0x0000', **DEFAULT_REG_LAYOUT, key='_HL_')],
    [sg.T('X   '), sg.T('0x00  ', **DEFAULT_REG_LAYOUT, key='_X_')],
    [sg.T('Y   '), sg.T('0x00  ', **DEFAULT_REG_LAYOUT, key='_Y_')],
    [sg.T('J   '), sg.T('0x0000', **DEFAULT_REG_LAYOUT, key='_J_')],
    [sg.T('K   '), sg.T('0x0000', **DEFAULT_REG_LAYOUT, key='_K_')],
    [sg.T('D   '), sg.T('0x0000', **DEFAULT_REG_LAYOUT, key='_D_')],
    [sg.T('DATA'), sg.T('0x00  ', **DEFAULT_REG_LAYOUT, key='_DATA_')],
    [sg.T('ADDR'), sg.T('0x0000', **DEFAULT_REG_LAYOUT, key='_ADDR_')],
    [sg.T('STPR'), sg.T('0x0000', **DEFAULT_REG_LAYOUT, key='_STPR_')],
  ]

  DEFAULT_INDICATOR_LAYOUT = {
    'text':'\u2B24',
    'size':(2,1),
    'justification':'left',
    'text_color':'gray',
  }

  layout_flags = [
    [sg.T('Flags')],
    [sg.T('ZF  '), sg.T(**DEFAULT_INDICATOR_LAYOUT, key='_ZF_')],
    [sg.T('CF  '), sg.T(**DEFAULT_INDICATOR_LAYOUT, key='_CF_')],
    [sg.T('NF  '), sg.T(**DEFAULT_INDICATOR_LAYOUT, key='_NF_')],
    [],
    [sg.Button('uSTEP'), sg.Button('STEP Inst.')],
    [sg.Button('Resume')],
    [sg.T('CLK  '), sg.T(
      text='',
      size=(12,1),
      justification='left',
      text_color='black',
      background_color='white',
      key='_CNT_'
    )],
    [sg.T('INST '), sg.T(
      text='inst??',
      size=(21,1),
      justification='left',
      text_color='black',
      background_color='white',
      font=('courier new',7),
      key='_INST_NAME_')
    ],
    [sg.Image(key="-IMAGE-", size=(IMG_WID,IMG_HEI))],
    [sg.Image(key="-MAP-", size=(128,128))],
    [sg.T('UART')],
    [
      sg.Multiline(
        size=(30,24),
        autoscroll=True,
        font=('courier new',7),
        justification='left',
        text_color='black',
        background_color='white',
        key='_UART_'
      )
    ]
  ]

  layout_ctrl = [
    [sg.T('Ctrl')]
  ]

  for ctrl_sig,val in ctrl.items():
    layout_ctrl.append([
      sg.T(text=f"{ctrl_sig:<5}"),
      sg.T(**DEFAULT_INDICATOR_LAYOUT, key=f'_{ctrl_sig}_')
    ])

  layout_sram = [[
    sg.T(
      text='',
      size=(128*3+16,128),
      font=('courier new',6),
      justification='left',
      text_color='black',
      background_color='white',
      key='_SRAM_'
    )
  ]]
  layout_stack = [[
    sg.Column([
      [
        sg.Multiline(
          size=(12,70),
          autoscroll=True,
          font=('courier new',8),
          justification='left',
          text_color='black',
          background_color='white',
          write_only = True,
          disabled = True,
          key='_STACK_'
        )
      ]
    ], vertical_alignment='t' ),
    sg.Column([
      [
        sg.T('Current Usage   '),
        sg.T(
          text='',
          size=(12,1),
          justification='left',
          text_color='black',
          background_color='white',
          key='_STACK_USAGE_'
        )
      ],
      [
        sg.T('Max Stack Usage '),
        sg.T(
          text='',
          size=(12,1),
          justification='left',
          text_color='black',
          background_color='white',
          key='_STACK_MAX_USAGE_'
        )
      ]
    ], vertical_alignment='t' )
  ]]
  layout_vars = [[
    sg.Multiline(
      size=(100,70),
      font=('courier new',8),
      justification='left',
      text_color='black',
      background_color='white',
      write_only = True,
      disabled = True,
      key='_VARS_'
    )
  ]]
  layout_ext_rom = [
    [
      sg.T('EXT ROM Addr Ptr   '),
      sg.T(
        text='',
        size=(6,1),
        justification='left',
        text_color='black',
        background_color='white',
        key='_EXT_ROM_ADDR_'
      )
    ],
    [
      sg.T('EXT ROM Current Val'),
      sg.T(
        text='',
        size=(4,1),
        justification='left',
        text_color='black',
        background_color='white',
        key='_EXT_ROM_VAL_'
      )
    ],
    [
      sg.T(
        text='',
        size=(32*3+10,65),
        font=('courier new',6),
        justification='left',
        text_color='black',
        background_color='white',
        key='_EXT_ROM_'
      )
    ]
  ]
  layout_time = [[
    sg.Multiline(
      size=(100,70),
      font=('courier new',8),
      justification='left',
      text_color='black',
      background_color='white',
      write_only = True,
      disabled = True,
      key='_TIME_ANALYSIS_'
    )
  ]]

  tabs_layout = [[
    sg.TabGroup([[
      sg.Tab('SRAM', layout_sram),
      sg.Tab('STACK', layout_stack),
      sg.Tab('VARS', layout_vars),
      sg.Tab('EXT_ROM', layout_ext_rom),
      sg.Tab('TIME', layout_time),
      ]])
    ]]

  layout = [[
    sg.Column(layout_regs,  vertical_alignment='t'),
    sg.Column(layout_ctrl,  vertical_alignment='t'),
    sg.Column(layout_flags, vertical_alignment='t'),
    sg.Column(tabs_layout,  vertical_alignment='t')
  ]]

  if GUI:
    window = sg.Window('8B', layout, font=('courier new',11))
  else:
    window = None

  def update_uart(char):
    if GUI:
      window['_UART_'].update(char, append=True)
    else:
      pass
  mems.uart.callback = update_uart

  INDC_COLOR = ['gray', 'green']

  file = open(FILE_NAME, 'rb')
  eep_ptr = 0
  while 1:
    byte = file.read(1)
    if not byte:
      break
    mems.eeprom.value[eep_ptr] = ord(byte) & 0xFF
    # TODO: load real ext_rom bin
    mems.ext_eeprom.value[eep_ptr] = ord(byte) & 0xFF
    eep_ptr+=1

  file.close()

  vars, symbols, code = parse_vars(FILE_NAME)
  max_var_width = max([len(list(var.keys())[0]) for var in vars])

  if GUI:
    event, values = window.Read(timeout=0)
  else:
    event = None
    values = None

  inst = []
  inst_names = {}
  inst_stats = {}
  call_graph = [0x0000]
  previous_pc = 0x0000

  with open('instruction_set.yaml', 'r') as stream:
    try:
      IS = yaml.safe_load(stream)
      inst = [IS['instructions']['default']['ucode']]*int(0x80)
      for key, value in IS['instructions'].items():
        if key != 'default':
          inst[int(value['opcode'])] = value['ucode']
          inst_names[int(value['opcode'])] = key
          inst_stats[key] = {
            'calls': 0,
          }

      while True:
        if GUI:
          if clk_counter % UPDATE_RATE == 0 or ctrl['HT'] or breakpt:
            event, values = window.Read(timeout=0)

          if breakpt and not finish_inst:
            event, values = window.Read()
            if event == "STEP Inst.":
              finish_inst = True
            if event == "Resume":
              finish_inst = False
              breakpt = False
            if event == "uSTEP":
              pass

        clk_counter += 1

        ## rising edge
        # outputs
        #if ctrl['PO']+ctrl['MO']+ctrl['AO']+(ctrl['Mn']>0)+ctrl['PP'] > 1:
        #  raise Exception("more than one bus output enabled")
        if ctrl['PO']:
          data = pc.get(ctrl['LM'])
        elif ctrl['MO']:
          data = mems.get(addr)
        elif ctrl['NO']:
          data = stack.get(ctrl['LM'])
        elif ctrl['AO']:
          data = A.get()
        elif ctrl['BO']:
          data = B.get()
        elif ctrl['HO']:
          data = HL.get(ctrl['LM'])
        elif ctrl['JO']:
          data = J.get(ctrl['LM'])
        elif ctrl['KO']:
          data = K.get(ctrl['LM'])
        elif ctrl['DO']:
          data = D.get(ctrl['LM'])
        elif ctrl['SO']:
          data = stack.pop()
        elif ctrl['ADD']:
          # Add
          data = (X.value+Y.value) & 0xFF
        elif ctrl['SUB']:
          # Subtract
          data = (X.value+~(Y.value)+1) & 0xFF
        elif ctrl['AND']:
          # And
          data = (X.value & Y.value) & 0xFF
        elif ctrl['OR']:
          # Or
          data = (X.value | Y.value) & 0xFF
        elif ctrl['XOR']:
          # Xor
          data = (X.value ^ Y.value) & 0xFF
        elif ctrl['SHL']:
          # Left Shift 1
          data = (X.value << 1) & 0xFF
        elif ctrl['SHR']:
          # Right Shift 1
          data = (X.value >> 1) & 0xFF
        elif ctrl['ZERO']:
          data = 0x00
        elif ctrl['ONES']:
          data = 0xFF
        elif ctrl['ONE']:
          data = 0x01

        # FLAGS
        if ctrl['FI']:
          flags['ZF'] = ( (data & 0xFF) == 0 )
          flags['NF'] = ( data & 0x80) > 0
          if ctrl['ADD']:
            # Add
            flags['CF'] = ((X.value+Y.value) > 0xFF) or ((X.value+Y.value) < 0x00)
          elif ctrl['SUB']:
            # Subtract
            flags['CF'] = ((X.value+~(Y.value)+1) > 0xFF) or ((X.value+~(Y.value)+1) < 0x00)
          elif ctrl['AND']:
            flags['CF'] = 0
          elif ctrl['OR']:
            flags['CF'] = 0
          elif ctrl['XOR']:
            flags['CF'] = 0
          elif ctrl['SHL']:
            # Left Shift 1
            flags['CF'] = (X.value << 1) > 0xFF
          elif ctrl['SHR']:
            # Right Shift 1
            flags['CF'] = X.value & 0x01
          else:
            raise Exception("should only update CF on ALU operation")

        # CLK
        if ctrl['HT']:
          if EXIT_ON_HALT:
            break
          #if GUI:
          #  event, values = window.Read()
          breakpt = True

        # PC
        if (ctrl['PO'] or ctrl['PI']) and ctrl['CE']:
          raise Exception("CE asserted during PC access")
        if ctrl['PI']:
          pc.set(ctrl['LM'], data)
        if ctrl['CE']:
          pc.inc()

        # MAR
        if ctrl['MA'] and ctrl['MC']:
          raise Exception("CE asserted during PC access")
        if ctrl['MA']:
          mar.set(ctrl['LM'], data)
          addr = mar.value
        if ctrl['MC']:
          mar.inc()
          addr = mar.value

        # MEM
        if ctrl['MI']:
          mems.set(addr, data)

        # INST
        if ctrl['II']:
          ii.set(data)
          inst_stats[inst_names[ii.value]]['calls'] += 1
          # print(pc.value)
          # store dead code info
          list_addrs = [c['addr'] for c in code]
          indices = [i for i, x in enumerate(list_addrs) if x == pc.value-1]
          for i in indices:
            code[i]['execs'] += 1
            code[i]['dead'] = False

        # A REG
        if ctrl['AI']:
          A.set(data)
        # B REG
        if ctrl['BI']:
          B.set(data)
        # HL REG
        if ctrl['HI']:
          HL.set(data,ctrl['LM'])
        # X ALU REG
        if ctrl['XI']:
          X.set(data)
        # Y ALU REG
        if ctrl['YI']:
          Y.set(data)
        # J REG
        if ctrl['JI']:
          J.set(data,ctrl['LM'])
        # K REG
        if ctrl['KI']:
          K.set(data,ctrl['LM'])
        # D REG
        if ctrl['DI']:
          D.set(data,ctrl['LM'])

        # STACK
        if ctrl['SI']:
          stack.push(data)
        elif ctrl['IS']:
          stack.inc()
        elif ctrl['DS']:
          stack.dec()
        elif ctrl['NI']:
          stack.set(data,ctrl['LM'])

        # check if this is a jmp/call/ret instruction
        if ctrl['PI'] and ctrl['RU']:
          # if call_graph[-1] != previous_pc:
          #   call_graph.append(previous_pc)
          call_graph.append(pc.value)

        # U CODE
        UCC = (UCC + 1) & 0x1F
        if ctrl['RU']:
          UCC = 0

        if UCC == 0:
          previous_pc = pc.value

        ## falling edge
        # handle ucode
        ctrl = dict.fromkeys(ctrl, 0)
        if isinstance(inst[ii.value], dict):
          # branch/conditional instruction
          condition = True
          for c in inst[ii.value]['conditions']:
            if not flags[c]:
              condition = False
          if UCC < len(inst[ii.value][condition]):
            for u in inst[ii.value][condition][UCC]:
              ctrl[u] = 1

        # normal sequential instruction
        elif  UCC < len(inst[ii.value]):
          for u in inst[ii.value][UCC]:
            ctrl[u] = 1

          # handle the assert instructions. Only do anything on the last cycle
          if ctrl['RU']:
            if ii.value == int(IS['instructions']['assert_a']['opcode']):
              if mems.get(addr) == A.value:
                print(f"pass, a = 0x{A.value:02X}")
              else:
                raise Exception(f"test case failed, a = 0x{A.value:02X}, expected 0x{mems.get(addr):02X}\nPC=0x{pc.value:04X}")
            elif ii.value == int(IS['instructions']['assert_b']['opcode']):
              if mems.get(addr) == B.value:
                print(f"pass, b = 0x{B.value:02X}")
              else:
                raise Exception(f"test case failed, b = 0x{B.value:02X}, expected 0x{mems.get(addr):02X}\nPC=0x{pc.value:04X}")
            elif ii.value == int(IS['instructions']['assert_hl']['opcode']):
              if (mems.get(addr-1)<<8)+(mems.get(addr)) == HL.value:
                print(f"pass, HL = 0x{HL.value:04X}")
              else:
                raise Exception(f"test case failed, HL = 0x{HL.value:04X}, expected 0x{((mems.get(addr-1)<<8)+(mems.get(addr))):04X}\nPC=0x{pc.value:04X}")
            elif ii.value == int(IS['instructions']['assert_zf']['opcode']):
              if mems.get(addr) == flags['ZF']:
                print(f"pass, ZF = {flags['ZF']}")
              else:
                raise Exception(f"test case failed, ZF = {flags['ZF']}, expected {mems.get(addr)}\nPC=0x{pc.value:04X}")

        if GUI:
          if finish_inst:
            if UCC == 0:
              finish_inst = False

          if event is None or event == 'Exit':
            print()
            break

          if clk_counter % UPDATE_RATE == 0 or breakpt:
            window['_PC_'].update(f"0x{pc.value:04X}")
            window['_MAR_'].update(f"0x{mar.value:04X}")
            window['_INST_'].update(f"0x{ii.value:02X}")
            window['_UCC_'].update(f"0x{UCC:02X}")
            window['_A_'].update(f"0x{A.value:02X}")
            window['_B_'].update(f"0x{B.value:02X}")
            window['_HL_'].update(f"0x{HL.value:04X}")
            window['_X_'].update(f"0x{X.value:02X}")
            window['_Y_'].update(f"0x{Y.value:02X}")
            window['_J_'].update(f"0x{J.value:04X}")
            window['_K_'].update(f"0x{K.value:04X}")
            window['_D_'].update(f"0x{D.value:04X}")
            window['_DATA_'].update(f"0x{data:02X}")
            window['_ADDR_'].update(f"0x{addr:04X}")

            window['_ZF_'].update(text_color=INDC_COLOR[flags['ZF']])
            window['_CF_'].update(text_color=INDC_COLOR[flags['CF']])
            window['_NF_'].update(text_color=INDC_COLOR[flags['NF']])

            for ctrl_sig,val in ctrl.items():
              window[f'_{ctrl_sig}_'].update(text_color=INDC_COLOR[val>0])

            window['_STPR_'].update(f"0x{stack.get_pointer():04X}")

            window['_CNT_'].update(f"{clk_counter:,}")

            sram_values = ""
            for n in range(2**14):
              if mems.sram.value[n] is None:
                sram_values += "--"
              else:
                sram_values += f"{mems.sram.value[n]:02X}"
              # TODO: sram address is hard coded...
              if (n == stack.get_pointer()-0x8000-1):
                sram_values += "*"
              else:
                sram_values += " "
              if (n+1)%8 == 0:
                sram_values += " "

            window['_SRAM_'].update(sram_values)

            stack_values = ""
            for n in range(stack.starting_addr,stack.starting_addr-stack.pointer,-1):
              stack_values += f"0x{n:04X}:  {mems.get(n):02X}\n"

            window['_STACK_'].update(stack_values)
            window['_STACK_USAGE_'].update(stack.pointer)
            window['_STACK_MAX_USAGE_'].update(stack.max_used)

            var_values = ""
            for var in vars:
              # print(var, max_var_width)
              var_name = list(var.keys())[0]
              var_addr = list(var.values())[0]
              var_bytes = [None]*4
              for n in range(len(var_bytes)):
                var_bytes[n] = mems.get(var_addr+n,ignore_uninit=True)

              var_values += f"0x{var_addr:04X} {var_name:<{max_var_width}} "
              if var_bytes[0] is not None:
                var_values += f"0x{var_bytes[0]:02X} "
              else:
                var_values += "?"
              if not (None in var_bytes[0:2]):
                var_values += f"0x{var_bytes[0]:02X}{var_bytes[1]:02X} "
              if not (None in var_bytes):
                var_values += f"0x{var_bytes[0]:02X}{var_bytes[1]:02X}{var_bytes[2]:02X}{var_bytes[3]:02X} "
              var_values += "\n"
            window['_VARS_'].update(var_values)

            if  ii.value in inst_names:
              window['_INST_NAME_'].update(inst_names[ii.value])
            else:
              window['_INST_NAME_'].update("?")

            window['_EXT_ROM_ADDR_'].update(f"0x{mems.ext_eeprom.addr_reg:04X}")
            window['_EXT_ROM_VAL_'].update(f"0x{mems.ext_eeprom.get(0x00):02X}")

            ext_rom_values = ""
            ext_rom_base_addr = mems.ext_eeprom.addr_reg & (~(2**11-1))
            for n in range(2**11):
              rom_addr = ext_rom_base_addr + n
              if n%32 == 0:
                ext_rom_values += f"\n {rom_addr:04X} "
              elif(n)%8 == 0:
                ext_rom_values += " "
              if (rom_addr == mems.ext_eeprom.addr_reg):
                ext_rom_values += "*"
              else:
                ext_rom_values += " "
              if mems.ext_eeprom.value[rom_addr] is None:
                ext_rom_values += "--"
              else:
                ext_rom_values += f"{mems.ext_eeprom.value[rom_addr]:02X}"

            window['_EXT_ROM_'].update(ext_rom_values)

            inst_stats_str = ""
            for x in sorted(inst_stats.items(), key=lambda item: item[1]['calls'], reverse=True):
              name = x[0]
              stats = x[1]
              inst_stats_str += f"{name:<20} {stats}\n"
            window['_TIME_ANALYSIS_'].update(inst_stats_str)

            img = Image.new('L', [IMG_WID,IMG_HEI], 255)
            pixels = img.load()

            for row in range(IMG_HEI):
              for col in range(IMG_WID):
                dr_addr = int(row/8)+col*10
                v = 100
                if mems.dpram.value[dr_addr] is not None:
                  v = ((mems.dpram.value[dr_addr] >> (row%8)) & 0x01) * 255
                pixels[col,row] = v
            window["-IMAGE-"].update(data=ImageTk.PhotoImage(image=img))

            mp = Image.new('L', [128,128], 255)
            mp_pixels = mp.load()

            for row in range(128):
              for col in range(128):
                dr_addr = int(col/4)+row*int(128/4)
                v = 100
                if mems.sram.value[(dr_addr+0xA000)&(mems.sram.size-1)] is not None:
                  v = ((mems.sram.value[(dr_addr+0xA000)&(mems.sram.size-1)] >> (6-((col%4)*2)) ) & 0b11) * 85
                  #print(row,col,dr_addr,(col%4)*2,v)
                mp_pixels[col,row] = v
            window["-MAP-"].update(data=ImageTk.PhotoImage(image=mp))

      print(f"max stack usage {stack.max_used} bytes")
      print(f"clk cycles {clk_counter}")

      # call graph
      # print_call_graph(call_graph, symbols)

      # dead code
      # for l in code:
      #   print(l)

      if GUI:
        window.close()

    except yaml.YAMLError as exc:
      print(exc)

if __name__ == "__main__":
  main()


'''
単位変換プログラム
'''
def print_menu():
    print('1.kmをmilesへ')
    print('2.milesをkmへ')

def km_miles():
    km = float(input('Kmを入力してください'))
    miles =  km / 1.609
    print('kmをmileに直すと...{0}になります'.format(miles))

def miles_km():
    miles = float(input('マイルを入力してください'))
    km = miles * 1.609
    print('mileをkmに直すと...{0}になります.'.format(km))

if __name__ == '__main__':
    print_menu()
    choice = input('希望の処理を選んでください')
    if choice == '1':
        km_miles()
    if choice == '2':
        miles_km()


import collections
import datetime
import json
import logging
import urllib

import pilo

logger = logging.getLogger(__name__)

MIME = collections.namedtuple(
    'MIME', ['content_type', 'encode', 'decode', 'source']
)

def encoder_for(content_type):
    if content_type == json.content_type:
        return json.encode
    if content_type == url.content_type:
        return url.encode
    raise LookupError('No encoder for "{0}"'.format(content_type))

def decoder_for(content_type):
    if content_type == json.content_type:
        return json.decode
    raise LookupError('No decoder for "{0}"'.format(content_type))

def source_for(content_type):
    if content_type == json.content_type:
        return json.source
    if content_type == url.content_type:
        return url.source
    raise LookupError('No source for "{0}"'.format(content_type))

# json

class JSONEncoder(json.JSONEncoder):

    def __init__(self, indent=4, sort_keys=True, errors='strict'):
        super(JSONEncoder, self).__init__(indent=indent, sort_keys=sort_keys)
        self.errors = errors

    def default(self, o):
        if isinstance(o, datetime.datetime):
            return self._datetime(o)
        if self.errors == 'strict':
            raise TypeError(repr(o) + ' is not JSON serializable')
        if self.errors == 'warn':
            logger.warning(repr(o) + ' is not JSON serializable')

    def _datetime(self, o):
        return o.isoformat()

    def _date(self, o):
        return '{:04d}-{:02d}-{:02d}'.format(o.year, o.month, o.day)

    def _time(self, o):
        return o.strftime('%H:%M:%S')

    def _uuid(self, o):
        return str(o.hex)

json = MIME(
    content_type='application/json',
    encode=JSONEncoder().encode,
    decode=json.loads,
    source=pilo.source.JsonSource,
)

# url

class URLEncoder(object):

    def __init__(self, exclude_none=True, doseq=1, errors='strict'):
        super(URLEncoder, self).__init__()
        self.exclude_none = exclude_none
        self.doseq = doseq
        self.errors = errors

    def encode(self, o):

        query = []

        def _encode_value(key, value):
            if value is None and self.exclude_none:
                return
            if isinstance(value, (list, tuple)):
                for v in value:
                    _encode_value(key, v)
                return
            if not isinstance(value, basestring):
                v = self.default(value)
            if value is None and self.exclude_none:
                return
            query.append((key, value))

        for k, v in o.iteritems():
            _encode_value(k, v)

        return urllib.urlencode(query, self.doseq)

    def default(self, o):
        if isinstance(o, (int, long)):
            return str(o)
        if isinstance(o, bool):
            return 'true' if o else 'false'
        if isinstance(o, datetime.datetime):
            return self._datetime(o)
        if self.errors == 'strict':
            raise TypeError(repr(o) + ' is not URL serializable')
        if self.errors == 'warn':
            logger.warning(repr(o) + ' is not URL serializable')

    def _datetime(self, value):
        return value.isoformat()

    def _date(self, o):
        return o.strftime('%Y-%m-%d')

    def _time(self, o):
        return o.strftime('%H:%M:%S')

url = MIME(
    content_type='application/x-www-form-urlencoded',
    encode=URLEncoder().encode,
    decode=None,
    source=None,
)


import os.path
import sys
from unittest import mock

import pytest

from antsibull_core import subprocess_util
from antsibull_core.venv import FakeVenvRunner, VenvRunner, get_clean_environment

def test_venv_clean_env(monkeypatch):
    monkeypatch.setenv("PYTHONPATH", "/jfjfjfjfjfjfjfjfj")
    assert "PYTHONPATH" not in get_clean_environment()

def test_venv_run_init(tmp_path):
    with mock.patch("antsibull_core.subprocess_util.async_log_run") as log_run:
        runner = VenvRunner("asdfgh", tmp_path)
        assert runner.name == "asdfgh"
        assert runner.top_dir == tmp_path
        assert runner.venv_dir == str(tmp_path / "asdfgh")
        pip = os.path.join(runner.venv_dir, "bin", "pip")
        log_run.assert_called_once_with(
            [pip, "install", "--upgrade", "pip"],
            None,
            None,
            "debug",
            True,
            errors="strict",
            env=get_clean_environment(),
        )

def test_venv_log_run_error(tmp_path):
    runner = VenvRunner("zxcvb", tmp_path)
    echo = os.path.join(runner.venv_dir, "bin", "echo")
    with pytest.raises(ValueError, match=rf"^{echo!r} does not exist!"):
        runner.log_run(["echo", "This won't work!"])

def test_venv_log_run_error2(tmp_path):
    runner = VenvRunner("zxcvb", tmp_path)
    echo = "/usr/bin/echo"
    with pytest.raises(ValueError, match=rf"^{echo!r} must not be an absolute path!"):
        runner.log_run([echo, "This also won't work!"])

def test_fake_venv_python():
    with mock.patch.object(
        subprocess_util, "async_log_run", wraps=subprocess_util.async_log_run
    ) as log_run:
        args = ["python", "-c", 'import antsibull_core; print("Hello, world!")']
        p = FakeVenvRunner().log_run(args)
        assert p.stdout == "Hello, world!\n"

        args = args.copy()
        args[0] = sys.executable
        log_run.assert_called_once_with(
            args,
            None,
            None,
            "debug",
            True,
            errors="strict",
        )


import psonic
MARIO_NOTES = [
    76, 76, 76, 72, 76, 79, 67, 72, 67, 64, 69, 71,
    70, 69, 67, 76, 79, 81, 77, 79, 76, 72, 74, 71
]

# Challenge 1: use a *for* loop to PLAY all of the notes
# in the MARIO_NOTES list.
# for note in MARIO_NOTES:
#     psonic.play(note)
#     psonic.sleep(0.5)

# Challenge 2: when the song ends, play it again

while True:
    for note in MARIO_NOTES:
        psonic.play(note)
        psonic.sleep(0.2)


import firebase_admin
from firebase_admin import credentials
from firebase_admin import db
import paho.mqtt.client as mqtt
import json
import joblib
import  numpy as np
from mqtt import Publisher

mqtt_pub = Publisher("sensor/data", "localhost", 1883)
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("Successfully connected to broker")
        client.subscribe("sensor2/data")
    else:
        print("Connection failed with code %d." %rc)

def on_message(client, userdata, msg):

    recv_dict = json.loads(msg.payload)
    temp = np.array(recv_dict['payload'][0]).reshape(-1, 1)
    RH = np.array(recv_dict['payload'][1]).reshape(-1, 1)

    data = np.concatenate((temp, RH), axis = 1)
    env_humi = loaded_RH.predict(data)
    env_temp = loaded_Temp.predict(data)
    tempF= env_temp[0][0]*1.8+32
    data2 = np.concatenate(
        (
        np.array(tempF).reshape(-1,1),
        np.array(env_humi[0][0]).reshape(-1,1),
        )
        ,axis = 1)

    feelikeF = loaded_HI.predict(data2)
    feellikeC = (feelikeF[0][0]-32)/1.8
    json_msg = {
            'type': 'heat_injury',
            'env_temp':env_temp[0][0],
            'env_humi':env_humi[0][0],
            "FL_temp": feellikeC
    }
    mqtt_pub.publish(json.dumps(json_msg))

def setup(hostname):
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    client.connect(hostname)
    client.loop_start()
    return client

def main():
    global loaded_RH
    global loaded_Temp
    global loaded_HI
    loaded_RH = joblib.load("./Predict_env_humi.pkl")
    loaded_Temp = joblib.load("./Predict_env_temp.pkl")
    loaded_HI = joblib.load('./Predict_Heat_Injury.pkl')
    setup("localhost")
    mqtt_pub.run()
    while True:
        pass
if __name__ == "__main__":
    main()


from flask import render_template, flash, redirect, url_for, request
from flask_login import login_required, login_user, logout_user, current_user
from werkzeug.urls import url_parse
from app import app, db
from app.forms import LoginForm, RegistrationForm
from app.models import User, Task

@app.route('/')
@app.route('/index')
@login_required
def index():
    tasks_todo = []
    tasks_progress = []
    tasks_done = []
    if current_user.is_authenticated:
        # get task only if the users is authenticated
        tasks_todo = Task.query.filter_by(taskStatus='ToDo').filter_by(userId = current_user.id)
        tasks_progress = Task.query.filter_by(taskStatus='Progress').filter_by(userId = current_user.id)
        tasks_done = Task.query.filter_by(taskStatus='Done').filter_by(userId = current_user.id)
    return render_template('index.html', title='Home Page', tasks_todo=tasks_todo, tasks_progress=tasks_progress, tasks_done=tasks_done)

@app.route('/login', methods=['GET', 'POST'])
def login():
    '''
        Allows users to login
    '''
    if current_user.is_authenticated:
        # re-route users to homepage, if authenticated
        return redirect(url_for('index'))

    # present a login form to allow users to log-in
    form = LoginForm()
    if request.method == "POST":
        if form.validate_on_submit():
            user = User.query.filter_by(email=form.email.data).first()
            if user is None or not user.check_password(form.password.data):
                flash('Invalid username or password')
                return redirect(url_for('login'))
            login_user(user, remember=form.remember_me.data)
            flash('Welcome')
        return redirect(url_for('index'))
    return render_template('login.html', title='Sign In', form=form)

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('index'))

@app.route('/register', methods=['GET', 'POST'])
def register():
    '''
        Registers a new user to the system.
    '''
    if current_user.is_authenticated:
        return redirect(url_for('index'))

    # registration form
    form = RegistrationForm()
    if request.method == "POST":
        if form.validate_on_submit():
            # register new user only after validation
            user = User(firstName=form.firstName.data,
                lastName=form.lastName.data,
                email=form.email.data,
                maxProgressLimit=form.maxProgressLimit.data)
            user.set_password(form.password.data)
            db.session.add(user)
            db.session.commit()
            flash('Congratulations, you are now a registered user!')
            return redirect(url_for('login'))
    return render_template('register.html', title='Register', form=form)

@app.route('/add', methods=['POST'])
@login_required
def add():
    '''
        Allows to add-in new tasks
    '''
    task = Task(taskName=request.form['taskitem'], taskStatus='ToDo', userId = current_user.id)
    db.session.add(task)
    db.session.commit()
    return redirect(url_for('index'))

@app.route('/todo/<id>')
@login_required
def todo(id):
    '''
        Moves a given task to Todo column
    '''
    task = Task.query.filter_by(taskId = int(id)).first()
    task.taskStatus = 'ToDo'
    db.session.commit()
    return redirect(url_for('index'))

@app.route('/progress/<id>')
@login_required
def progress(id):
    '''
        Moves a given task to Progress column
    '''
    task = Task.query.filter_by(taskId = int(id)).first()
    task.taskStatus = 'Progress'
    db.session.commit()
    return redirect(url_for('index'))

@app.route('/done/<id>')
@login_required
def done(id):
    '''
        Moves a given task to done column
    '''
    task = Task.query.filter_by(taskId = int(id)).first()
    task.taskStatus = 'Done'
    db.session.commit()
    return redirect(url_for('index'))

@app.route('/delete_all')
@login_required
def delete_all():
    '''
        Delete all the tasks in Done column (That are already completed)
    '''
    tasks = Task.query.filter_by(taskStatus='Done')
    for task in tasks:
        db.session.delete(task)
    db.session.commit()
    return redirect(url_for('index'))

@app.route('/delete/<id>')
@login_required
def delete(id):
    '''
        Delete a given task from any column
    '''
    task = Task.query.filter_by(taskId = int(id)).first()
    db.session.delete(task)
    db.session.commit()
    return redirect(url_for('index'))


import itertools
import networkx as nx
import community as community_louvain
from networkx.algorithms.community.label_propagation import asyn_lpa_communities
from networkx.algorithms.community.modularity_max import greedy_modularity_communities
from networkx.algorithms.community.asyn_fluid import asyn_fluidc
import logging

# Users Table indexes
DB_ID_INDEX = 0

# Posts Table indexes
POST_ID_INDEX = 0
POST_AUTHOR_INDEX = 1
POST_CONTENT_INDEX = 2
POST_PARENT_INDEX = 4
POST_STANCE_INDEX = 7
POST_TIME_STAMP_INDEX = 8
POST_TOPIC_INDEX = 9
POST_TOPLEVEL_INDEX = 10
POST_ORIGINSPECIFIC_INDEX = 5

class NetworkMeasure(object):
    """
    Class for extracting the network structure of the users
    interaction (around o certain topic) from the dasp database.
    """

    def __init__(self, origin):
        """
        Constructor
        :param origin: Origin of the network to extract
                       - "TWITTER"
                       - "REDDIT"
        """
        self.origin = origin
        self.db = None
        self.network = None
        self.partition = None
        self.cmap = None
        self.node_users = []
        self.user_post_map = {}
        self.hex_colors = []

    def get_subreddit_users(self, subreddits):
        """
        Only for REDDIT:
        Filter out the user ids of the users that interacted in one of
        a given list of subreddits
        :param subreddits: The subreddits to filter
        :return: The list of found user ids
        """
        if not self.origin == "REDDIT":
            raise Exception("Filtering for subreddits is only available when working with data from reddit")
        else:
            try:
                self.db = OpinionDatabase()
                posts_full = self.all_posts_queue()
                filtered_users = set()
                for post in posts_full:
                    json_data = post[POST_ORIGINSPECIFIC_INDEX]
                    subreddit = json_data['subreddit']
                    if subreddit in subreddits:
                        filtered_users.add(post[POST_AUTHOR_INDEX])
                self.db.close_connection()
                return filtered_users
            except Exception as e:
                logging.warning(e)
                self.db.close_connection()
                raise Exception("Error during database queues!")

    def get_topic_users(self, topic):
        """
        Get a list of users that interacted in discussion around a certain topic.
        :param topic: The topic to filter for
        :return: The found list of user ids
        """
        try:
            self.db = OpinionDatabase()
            posts_full = self.all_posts_queue()
            topic_users = self.filter_topic_users(topic, posts_full)
            self.db.close_connection()
            return topic_users
        except Exception as e:
            logging.warning(e)
            self.db.close_connection()
            raise Exception("Error during database queues!")

    def build_network(self, filter_users=None, topic_filter=None, post_amount_filter=5, time_frame_filter=(None, None)):
        """
        Build the networkX user network based on the loaded thread map.
        The nodes (users) can filtered with some constraints.
        :param filter_users: Filter by a certain lsit of user ids
        :param topic_filter: Filter by users that interacted in a certain topic
        """
        print("Building social network graph...")
        try:
            self.db = OpinionDatabase()
            toplevel_posts = self.toplevel_posts_queue(time_frame=time_frame_filter)
            posts_full = self.all_posts_queue(time_frame=time_frame_filter)

            thread_mapping, unmatched_posts = NetworkMeasure.build_thread_mapping(toplevel_posts, posts_full)
            post_amount_map = self.post_amount_queue()
            self.user_post_map = NetworkMeasure.build_user_post_map(posts_full)
            author_set = set()  # Will represent the nodes of the network
            self.db.close_connection()
        except Exception as e:
            print(e)
            self.db.close_connection()
            raise Exception("Error during database queues!")

        edge_weights = {}
        for thread in thread_mapping.values():
            thread_participants = []
            topic_thread = False
            for post in thread:
                if post[POST_AUTHOR_INDEX] not in thread_participants and not post[POST_AUTHOR_INDEX] == '[deleted]':
                    thread_participants.append(post[POST_AUTHOR_INDEX])
                    if topic_filter is not None and post[POST_TOPIC_INDEX] == topic_filter:
                        topic_thread = True
            if topic_filter is not None and not topic_thread:
                continue
            if filter_users is not None:
                thread_participants = NetworkMeasure.filter_users(filter_users, thread_participants)
            thread_participants = NetworkMeasure.filter_users_amount(thread_participants, post_amount_map,
                                                                     threshold=post_amount_filter)
            author_set.update(thread_participants)
            raw_edges = get_network_edges(thread)
            for edge in raw_edges:
                if edge[0] in thread_participants and edge[1] in thread_participants:
                    if edge in edge_weights.keys():
                        edge_weights[edge] += 1
                    elif (edge[1], edge[0]) in edge_weights.keys():
                        edge_weights[(edge[1], edge[0])] += 1
                    else:
                        edge_weights[edge] = 1

        # This block needed to be added for the case where due to timeframed post fetching,
        # there are threads without a toplevel post in the timeframe fetched from the db
        if time_frame_filter[0] is not None and time_frame_filter[1] is not None:
            visited = []
            # Sort by timestamp (latest first) to rebuild the threads from the bottom up
            unmatched_posts = {k: v for k, v in reversed(
                sorted(unmatched_posts.items(), key=lambda item: item[1][POST_TIME_STAMP_INDEX]))}
            for pid, post in unmatched_posts.items():
                if pid not in visited:
                    thread_authors = set()
                    thread_authors.update(post[POST_AUTHOR_INDEX])
                    curr_post = post
                    visited.append(curr_post[POST_ID_INDEX])
                    parent_id = post[POST_PARENT_INDEX]
                    while parent_id in unmatched_posts.keys():
                        curr_post = unmatched_posts[curr_post[POST_PARENT_INDEX]]
                        thread_authors.update(curr_post[POST_AUTHOR_INDEX])
                        visited.append(curr_post[POST_ID_INDEX])
                        parent_id = curr_post[POST_PARENT_INDEX]
                    # Add authors
                    author_set.update(thread_authors)
                    # Add edges
                    raw_edges = itertools.combinations(thread_authors, 2)
                    for edge in raw_edges:
                        if edge[0] in thread_authors and edge[1] in thread_authors:
                            if edge in edge_weights.keys():
                                edge_weights[edge] += 1
                            elif (edge[1], edge[0]) in edge_weights.keys():
                                edge_weights[(edge[1], edge[0])] += 1
                            else:
                                edge_weights[edge] = 1

        logging.info(len(edge_weights))
        graph = nx.Graph()
        graph.add_nodes_from(author_set)
        for edge, weight_val in edge_weights.items():
            graph.add_edge(edge[0], edge[1], weight=weight_val)
        self.network = graph
        self.node_users = author_set
        logging.info(nx.info(self.network))

    def extract_largest_connected_component(self):
        """
        Extract the largest connected component in the networkX network.
        """
        for comp in nx.connected_components(self.network):
            if len(comp) > 5:
                logging.info("Component: " + str(len(comp)))

        user_set = max(nx.connected_components(self.network), key=len)
        self.network = self.network.subgraph(user_set)

    def color_network_nodes(self, profile='community_fluid'):
        """
        Color the nodes of the network with a certain
        strategy/profile:
        Most of them are community detection algorithms.
        Available profiles:
            - "community_louvain"
            - "community_max_modularity"
            - "community_propagation"
            - "community_fluid"
            - "gender"
            - "age_group"
            - "race"
        :param profile: The strategy to color the network nodes by
        """
        if profile == 'community_louvain':
            self.partition = community_louvain.best_partition(self.network)
        elif profile == 'community_max_modularity':
            sets = greedy_modularity_communities(self.network, weight='weight')
            mapping = {}

            for index, com_set in enumerate(sets):
                for uid in com_set:
                    mapping[uid] = index

            test_count = {}
            for user, cluster in mapping.items():
                if cluster in test_count.keys():
                    test_count[cluster] += 1
                else:
                    test_count[cluster] = 1
            logging.info([num for num in test_count.values() if num > 1])
            self.partition = mapping
        elif profile == 'community_propagation':
            sets = asyn_lpa_communities(self.network, "weight")
            mapping = {}

            for index, com_set in enumerate(sets):
                for uid in com_set:
                    mapping[uid] = index

            test_count = {}
            for user, cluster in mapping.items():
                if cluster in test_count.keys():
                    test_count[cluster] += 1
                else:
                    test_count[cluster] = 1
            logging.info([num for num in test_count.values() if num > 1])
            self.partition = mapping
        elif profile == 'community_fluid':
            best_partition = None
            best_score = 0

            for iteration in range(5):
                sets = asyn_fluidc(self.network, 3)
                mapping = {}

                for index, com_set in enumerate(sets):
                    for uid in com_set:
                        mapping[uid] = index

                test_count = {}
                for user, cluster in mapping.items():
                    if cluster in test_count.keys():
                        test_count[cluster] += 1
                    else:
                        test_count[cluster] = 1
                logging.info([num for num in test_count.values() if num > 1])

                score, per_cluster = self.score_partition(mapping)
                if score > best_score:
                    best_partition = mapping
                    best_score = score

            self.partition = best_partition
        elif profile == 'unicolor':
            mapping = {}
            for uid in list(self.network.nodes.keys()):
                mapping[uid] = 1
            self.partition = mapping
        else:
            raise Exception("Invalid profiling choice")

        self.cmap = cm.get_cmap('viridis', (max(self.partition.values()) + 1))
        colors = []
        for rgb in self.cmap.colors:
            colors.append(rgb_to_hex((int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))))
        self.hex_colors = colors

    def set_partition(self, partition):
        mapping = {}
        for uid in list(self.network.nodes.keys()):
            mapping[uid] = partition[uid]
        self.partition = mapping
        self.cmap = cm.get_cmap('viridis', (max(self.partition.values()) + 1))
        colors = []
        for rgb in self.cmap.colors:
            colors.append(rgb_to_hex((int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))))
        self.hex_colors = colors
        print(self.hex_colors)

    def get_partition_as_list(self):
        """
        Returns the current network partition
        as a list of list of user ids.
        :return: The current network partition
        as a list of list of user ids.
        """
        collector = {}

        for user, assignment in self.partition.items():
            if assignment in collector.keys():
                collector[assignment].add(user)
            else:
                new_set = set()
                new_set.add(user)
                collector[assignment] = new_set

        for cluster in collector.keys():
            if not self.hex_colors:
                break
            logging.info(self.hex_colors[cluster])

        return list(collector.values())

    def get_color_of_class(self, class_id):
        rgb = self.cmap(class_id)
        return rgb_to_hex((int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255)))

    def visualize_network(self):
        """
        Visualize the loaded network with pyplot.
        Can also be saved based on this.
        """
        layout = nx.spring_layout(self.network)
        plt.axis("off")
        nx.draw_networkx_nodes(self.network, layout, self.partition.keys(), node_size=15,
                               cmap=self.cmap, node_color=list(self.partition.values()))
        nx.draw_networkx_edges(self.network, layout, alpha=0.5)

    def score_partition(self, cluster_map, do_print=False):
        """
        Score the given partition with the
        community scores separability and density.
        :param cluster_map: The partition ot score
        :param do_print: Whether to print out hte results in the console
        :return: The average separability, tuples of the intermediate scores (separability,density)
        """
        """
        clean_edges = []
        for edge in self.network.edges:
            print("edge")
            if (edge[0], edge[1]) not in clean_edges and (edge[1], edge[0]) not in clean_edges:
                clean_edges.append((edge[0], edge[1]))"""
        clean_edges = self.network.edges()
        inter_edges = {}
        intra_edges = {}
        node_count = {}

        for node, community in cluster_map.items():
            if node not in self.network:
                continue
            if community not in inter_edges.keys():
                inter_edges[community] = 0
                intra_edges[community] = 0

            if community in node_count.keys():
                node_count[community] += 1
            else:
                node_count[community] = 1

        for edge in clean_edges:
            if cluster_map[edge[0]] == cluster_map[edge[1]]:
                intra_edges[cluster_map[edge[0]]] += 1
            else:
                inter_edges[cluster_map[edge[0]]] += 1
                inter_edges[cluster_map[edge[1]]] += 1

        sep_sum = 0
        scores = []
        for community, inter in inter_edges.items():
            nodes = node_count[community]
            if do_print:
                print(self.hex_colors[community])
                print("Separability:")
                print(intra_edges[community] / inter)
                print("Density:")
                print(intra_edges[community] / (nodes * (nodes - 1) / 2))

            scores.append((intra_edges[community] / inter, intra_edges[community] / (nodes * (nodes - 1) / 2)))
            sep_sum += intra_edges[community] / inter

        return sep_sum / len(node_count.keys()), scores


import tkinter as tk
from tkinter import CENTER, W, E, S
from tkinter import ttk
from tkinter import messagebox
from tkinter.filedialog import asksaveasfilename, askopenfilename
from tkcalendar import Calendar, DateEntry

from Controller.MainController import get_labs_list, get_customer_list, get_factors_list, generate_protocol, \
    get_measure_list, get_methodologies_list, get_experts_list, get_customer, add_laboratory_click, add_customer_click, \
    add_department_click, add_workplace_click, edit_department_click, edit_workplace_click, export_workplaces_click, \
    import_workplaces_click, import_wp_click

class MainWindow(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        root.title("Производственный контроль")
        root.geometry("650x445+300+200")
        root.resizable(False, False)
        self.init_main_window()

    def lab_checked(self, event):
        self.combo_cust.configure(values=get_customer_list(), state='normal')

    def customer_checked(self, event):
        self.combo_factor.configure(values=get_factors_list(self.combo_cust.get()), state='normal')
        self.show_button.configure(state='normal')
        self.import_wp_button.configure(state='normal')

    def factor_checked(self, event):
        self.create_button.configure(state='normal')
        self.export_button.configure(state='normal')
        self.import_button.configure(state='normal')

    def show_button_click(self):
        self.generate_tree(self.combo_cust.get())

    def create_button_click(self):
        if self.combo_factor.current() != 0:
            lab = self.combo_labs.get()
            cust = self.combo_cust.get()
            fact = self.combo_factor.get()
            GenerateProtocolWindow(lab, cust, fact)
        else:
            messagebox.showerror('Ошибка', 'Выберите один фактор')

    @staticmethod
    def add_lab_button_click():
        AddLabWindow()

    @staticmethod
    def add_cust_button_click():
        AddCustomerWindow()

    def add_department_button_click(self):
        customer_name = self.tree.heading('customer').get('text')
        AddDepartmentWindow(customer_name)

    def add_workplace_button_click(self):
        if self.tree.focus():
            customer_name = self.tree.heading('customer').get('text')
            if '.' in self.tree.focus():
                department_id = self.tree.parent(self.tree.focus())
            else:
                department_id = self.tree.focus()
            department_name = self.tree.item(department_id).get('values')[0]
            AddWorkplaceWindow(department_id, department_name, customer_name)
        else:
            messagebox.showerror('Ошибка', 'Выберите отдел')
            return 1

    def edit_button_click(self):
        customer_name = self.tree.heading('customer').get('text')
        if '.' in self.tree.focus():
            department_id = self.tree.parent(self.tree.focus())
            department_name = self.tree.item(department_id).get('values')[0]
            wp_name = self.tree.item(self.tree.focus()).get('values')[0]
            wp_id = self.tree.focus()
            wp_id = wp_id.partition('.')[0]
            AddWorkplaceWindow(department_id, department_name, customer_name, wp_name.split('. ')[1], int(wp_id))
        else:
            department_id = self.tree.focus()
            customer_name = self.tree.heading('customer').get('text')
            department_name = self.tree.item(department_id).get('values')[0]
            AddDepartmentWindow(customer_name, department_name, int(department_id))

    def export_button_click(self):
        file = asksaveasfilename(defaultextension=".csv", initialfile="report.csv")
        if file:
            export_workplaces_click(file, self.combo_cust.get(), self.combo_factor.get())
            messagebox.showinfo('Импорт файла', f'Файл {file} успешно сохранен.')

    def import_button_click(self):
        file = askopenfilename()
        if file:
            import_workplaces_click(file, self.combo_factor.get())
            messagebox.showinfo('Импорт файла', f'Файл {file} успешно импортирован.')

    def import_wp_button_click(self):
        file = askopenfilename()
        if file:
            import_wp_click(file, self.combo_cust.get())
            messagebox.showinfo('Импорт файла', f'Файл {file} успешно импортирован.')

    def update_treeview(self, customer_name):
        self.tree.heading('customer', text=customer_name, anchor=CENTER)
        dep_iid = ''
        wa_iid = ''
        count = 1
        if self.combo_factor.get() == '':
            factor = 'Все факторы'
        else:
            factor = self.combo_factor.get()

        customer = get_customer(customer_name, factor)
        for dep in customer.departments:
            dep_iid = str(dep.department_id)
            self.tree.insert(parent='', index='end', iid=dep_iid, values=(dep.name,))
            for wa in dep.working_areas:
                wa_iid = str(wa.number) + '. '
                self.tree.insert(parent=dep_iid, index='end', iid=wa_iid, values=(str(count) + '. ' + wa.name,))
                count += 1

    def generate_tree(self, customer_name):
        if hasattr(self, 'tree'):
            for i in self.tree.get_children():
                self.tree.delete(i)
            self.update_treeview(customer_name)

        else:
            self.grid_frame = tk.Frame(bd=2)
            self.grid_frame.pack(side=tk.TOP, fill=tk.X)

            self.add_department_button = ttk.Button(self.grid_frame, text='Добавить отдел', state='normal',
                                                    command=self.add_department_button_click)
            self.add_department_button.pack(side=tk.LEFT, fill=tk.X, expand=True)

            self.add_workplace_button = ttk.Button(self.grid_frame, text='Добавить РМ', state='normal',
                                                   command=self.add_workplace_button_click)
            self.add_workplace_button.pack(side=tk.LEFT, fill=tk.X, expand=True)

            self.edit_button = ttk.Button(self.grid_frame, text='Изменить', state='normal',
                                          command=self.edit_button_click)
            self.edit_button.pack(side=tk.LEFT, fill=tk.X, expand=True)

            self.tree_frame = tk.Frame(bd=2)
            self.tree_frame.pack(side=tk.TOP, fill=tk.BOTH)

            self.tree = ttk.Treeview(self.tree_frame, height=14, selectmode='browse')
            self.tree['columns'] = ('customer',)
            self.tree.column('#0', width=5)
            self.tree.column('customer', anchor=W, width=635)

            self.update_treeview(customer_name)

            self.tree.pack(side=tk.LEFT, fill=tk.BOTH)

    def init_main_window(self):
        self.toolbar = tk.Frame(bd=2)
        self.toolbar.pack(side=tk.TOP, fill=tk.X)

        lab_list = get_labs_list()

        tk.Label(self.toolbar, text="Лаборатория:").grid(column=0, row=0)
        self.combo_labs = ttk.Combobox(self.toolbar)
        self.combo_labs.configure(values=lab_list)
        self.combo_labs.grid(column=1, row=0)
        self.combo_labs.bind("<<ComboboxSelected>>", self.lab_checked)

        self.add_lab_button = ttk.Button(self.toolbar, text='+', state='normal', width=2,
                                         command=self.add_lab_button_click)
        self.add_lab_button.grid(column=2, row=0)

        tk.Label(self.toolbar, text="Организация:").grid(column=0, row=1)
        self.combo_cust = ttk.Combobox(self.toolbar, state='disabled')
        self.combo_cust.grid(column=1, row=1)
        self.combo_cust.bind("<<ComboboxSelected>>", self.customer_checked)

        self.add_cust_button = ttk.Button(self.toolbar, text='+', state='normal', width=2,
                                          command=self.add_cust_button_click)
        self.add_cust_button.grid(column=2, row=1)

        tk.Label(self.toolbar, text="Производственный фактор:").grid(column=0, row=2)
        self.combo_factor = ttk.Combobox(self.toolbar, state='disabled')
        self.combo_factor.grid(column=1, row=2)
        self.combo_factor.bind("<<ComboboxSelected>>", self.factor_checked)

        self.show_button = ttk.Button(self.toolbar, text='Показать данные', state='disable', width=20,
                                      command=self.show_button_click)
        self.show_button.grid(column=4, row=1)

        self.create_button = ttk.Button(self.toolbar, text='Создать протокол', state='disable', width=20,
                                        command=self.create_button_click)
        self.create_button.grid(column=4, row=2)

        self.import_wp_button = ttk.Button(self.toolbar, text='Импорт РМ', state='disable', width=15,
                                           command=self.import_wp_button_click)
        self.import_wp_button.grid(column=3, row=0)

        self.export_button = ttk.Button(self.toolbar, text='Экспорт зон', state='disable', width=15,
                                        command=self.export_button_click)
        self.export_button.grid(column=3, row=1)

        self.import_button = ttk.Button(self.toolbar, text='Импорт зон', state='disable', width=15,
                                        command=self.import_button_click)
        self.import_button.grid(column=3, row=2)

        tk.Label(self.toolbar, text="").grid(column=0, row=3)

class GenerateProtocolWindow(tk.Toplevel):
    def __init__(self, lab, cust, fact):
        super().__init__()
        self.lab = lab
        self.cust = cust
        self.fact = fact
        self.init_window()

    def add_measuring_button_click(self):
        var = tk.StringVar()
        var.set(get_measure_list(self.fact))
        AddListboxCheckWindow(self.combo_measuring, 'методики', var)

    def add_methodologies_button_click(self):
        var = tk.StringVar()
        var.set(get_methodologies_list(self.fact))
        AddListboxCheckWindow(self.combo_methodologies, 'СИ', var)

    def create_button_click(self):
        zamer = self.combo_zam.get()
        oformitel = self.combo_oformit.get()
        measure = tuple(self.combo_measuring.get().split(' '))
        methodologies = tuple(self.combo_methodologies.get().split(' '))
        date = self.date.get_date()
        date_izm = self.date_zamer.get_date()
        if zamer == '':
            messagebox.showerror('Ошибка', 'Не выбран замерщик.')
            return 1
        elif oformitel == '':
            messagebox.showerror('Ошибка', 'Не выбран оформитель.')
            return 1
        elif measure == '':
            messagebox.showerror('Ошибка', 'Не выбраны приборы.')
            return 1
        elif methodologies == '':
            messagebox.showerror('Ошибка', 'Не выбран методики.')
            return 1
        elif date == '':
            messagebox.showerror('Ошибка', 'Не выбрана дата протокола.')
            return 1
        elif date_izm == '':
            messagebox.showerror('Ошибка', 'Не выбрана дата замера.')
            return 1

        else:
            result = generate_protocol(self.lab, self.cust, self.fact, zamer.split(': ')[0], oformitel.split(':')[0],
                                       measure, methodologies, date, date_izm)
            if result == 0:
                messagebox.showinfo('Создание протокола', 'Протокол успешно создан.')
            elif result == 1:
                messagebox.showerror('Ошибка', 'Не заполнены данные об измерениях и метеообстановке, '
                                               'заполните их и повторите попытку')
            elif result == 2:
                messagebox.showerror('Ошибка', 'Не заполнены данные о метеообстановке, '
                                               'заполните их и повторите попытку')
            elif result == 3:
                messagebox.showerror('Ошибка', 'Не заполнены данные об измерениях, '
                                               'заполните их и повторите попытку')
            self.destroy()

    def init_window(self):
        self.title('Создание протокола')
        self.geometry('420x210+400+300')
        self.resizable(False, False)

        self.grab_set()
        self.focus_set()

        tk.Label(self, text="Выберите замерщика:", bd=5).grid(column=0, row=1)
        self.combo_zam = ttk.Combobox(self, values=get_experts_list(), width=40)
        self.combo_zam.grid(column=1, row=1, columnspan=3)

        tk.Label(self, text="Выберите оформителя:", bd=5).grid(column=0, row=2)
        self.combo_oformit = ttk.Combobox(self, values=get_experts_list(), width=40)
        self.combo_oformit.grid(column=1, row=2, columnspan=3)

        tk.Label(self, text="Выберите приборы:", bd=5).grid(column=0, row=3)

        self.combo_measuring = ttk.Entry(self, width=38)
        self.combo_measuring.grid(column=1, row=3, columnspan=2)

        self.add_measuring_button = ttk.Button(self, text='+', width=3, command=self.add_measuring_button_click)
        self.add_measuring_button.grid(column=3, row=3)

        tk.Label(self, text="Выберите методики:", bd=5).grid(column=0, row=4)

        self.combo_methodologies = ttk.Entry(self, width=38)
        self.combo_methodologies.grid(column=1, row=4, columnspan=2)

        self.add_methodologies_button = ttk.Button(self, text='+', width=3,
                                                   command=self.add_methodologies_button_click)
        self.add_methodologies_button.grid(column=3, row=4)

        tk.Label(self, text="Дата протокола:", bd=5).grid(column=0, row=5)
        self.date = DateEntry(self, width=40, foreground="white", bd=2)
        self.date.grid(column=1, row=5, columnspan=3)

        tk.Label(self, text="Дата замера:", bd=5).grid(column=0, row=6)
        self.date_zamer = DateEntry(self, width=40, foreground="white", bd=2)
        self.date_zamer.grid(column=1, row=6, columnspan=3)

        self.exit_button = ttk.Button(self, text='Отмена', width=15, command=self.destroy)
        self.exit_button.grid(column=1, row=7)

        self.create_button = ttk.Button(self, text='Добавить', width=15, command=self.create_button_click)
        self.create_button.grid(column=2, row=7)

class AddLabWindow(tk.Toplevel):
    def __init__(self):
        super().__init__()
        self.init_window()

    def add_button_click(self):
        if self.entry_short_name.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Сокращенное название" должно быть заполнено.')
            return 1
        elif self.entry_name.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Название полное" должно быть заполнено.')
            return 1
        elif self.entry_name_lab.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Название лаборатории" должно быть заполнено.')
            return 1
        elif self.entry_logo.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Логотип" должно быть заполнено.')
            return 1
        elif self.entry_director.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Руководитель" должно быть заполнено.')
            return 1
        elif self.entry_address.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Адрес" должно быть заполнено.')
            return 1
        elif self.entry_certificate_number.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Номер сертификата" должно быть заполнено.')
            return 1
        elif self.entry_phone.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Телефон" должно быть заполнено.')
            return 1
        elif self.entry_e_mail.get() == '':
            messagebox.showerror('Ошибка', 'Поле "E-mail" должно быть заполнено.')
            return 1
        else:
            add_laboratory_click(self.entry_short_name.get(), self.entry_name.get(), self.entry_name_lab.get(),
                                 self.entry_logo.get(), self.entry_director.get(), self.entry_address.get(),
                                 self.entry_certificate_number.get(), self.entry_phone.get(), self.entry_e_mail.get())
            self.destroy()

    def init_window(self):
        self.title('Добавить лабораторию')
        self.geometry('500x280+400+300')
        self.resizable(False, False)

        self.grab_set()
        self.focus_set()

        tk.Label(self, text="Название сокращенное:", bd=5).grid(column=0, row=1)
        self.entry_short_name = ttk.Entry(self, width=55)
        self.entry_short_name.grid(column=1, row=1, columnspan=2)

        tk.Label(self, text="Название полное:", bd=5).grid(column=0, row=2)
        self.entry_name = ttk.Entry(self, width=55)
        self.entry_name.grid(column=1, row=2, columnspan=2)

        tk.Label(self, text="Название лаборатории:", bd=5).grid(column=0, row=3)
        self.entry_name_lab = ttk.Entry(self, width=55)
        self.entry_name_lab.grid(column=1, row=3, columnspan=2)

        tk.Label(self, text="Логотип:", bd=5).grid(column=0, row=4)
        self.entry_logo = ttk.Entry(self, width=55)
        self.entry_logo.grid(column=1, row=4, columnspan=2)

        tk.Label(self, text="Руководитель:", bd=5).grid(column=0, row=5)
        self.entry_director = ttk.Entry(self, width=55)
        self.entry_director.grid(column=1, row=5, columnspan=2)

        tk.Label(self, text="Адрес:", bd=5).grid(column=0, row=6)
        self.entry_address = ttk.Entry(self, width=55)
        self.entry_address.grid(column=1, row=6, columnspan=2)

        tk.Label(self, text="Номер сертификата:", bd=5).grid(column=0, row=7)
        self.entry_certificate_number = ttk.Entry(self, width=55)
        self.entry_certificate_number.grid(column=1, row=7, columnspan=2)

        tk.Label(self, text="Телефон:", bd=5).grid(column=0, row=8)
        self.entry_phone = ttk.Entry(self, width=55)
        self.entry_phone.grid(column=1, row=8, columnspan=2)

        tk.Label(self, text="E-mail:", bd=5).grid(column=0, row=9)
        self.entry_e_mail = ttk.Entry(self, width=55)
        self.entry_e_mail.grid(column=1, row=9, columnspan=2)

        self.exit_button = ttk.Button(self, text='Отмена', width=20, command=self.destroy)
        self.exit_button.grid(column=1, row=10)

        self.create_button = ttk.Button(self, text='Создать', width=20, command=self.add_button_click)
        self.create_button.grid(column=2, row=10)

class AddCustomerWindow(tk.Toplevel):
    def __init__(self):
        super().__init__()
        self.init_window()

    def add_button_click(self):
        if self.entry_short_name.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Сокращенное название" должно быть заполнено.')
            return 1
        elif self.entry_name.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Название полное" должно быть заполнено.')
            return 1
        elif self.entry_legal_address.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Юридический адрес" должно быть заполнено.')
            return 1
        elif self.entry_actual_address.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Фактический адрес" должно быть заполнено.')
            return 1
        elif self.entry_contract_number.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Номер договора" должно быть заполнено.')
            return 1
        elif self.entry_contract_date.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Дата договора" должно быть заполнено.')
            return 1
        else:
            add_customer_click(self.entry_short_name.get(), self.entry_name.get(), self.entry_legal_address.get(),
                               self.entry_actual_address.get(), self.entry_contract_number.get(),
                               self.entry_contract_date.get())
            self.destroy()

    def init_window(self):
        self.title('Добавить заказчика')
        self.geometry('500x220+400+300')
        self.resizable(False, False)

        self.grab_set()
        self.focus_set()

        tk.Label(self, text="Название сокращенное:", bd=5).grid(column=0, row=1)
        self.entry_short_name = ttk.Entry(self, width=55)
        self.entry_short_name.grid(column=1, row=1, columnspan=2)

        tk.Label(self, text="Название полное:", bd=5).grid(column=0, row=2)
        self.entry_name = ttk.Entry(self, width=55)
        self.entry_name.grid(column=1, row=2, columnspan=2)

        tk.Label(self, text="Юридический адрес:", bd=5).grid(column=0, row=3)
        self.entry_legal_address = ttk.Entry(self, width=55)
        self.entry_legal_address.grid(column=1, row=3, columnspan=2)

        tk.Label(self, text="Фактический адрес:", bd=5).grid(column=0, row=4)
        self.entry_actual_address = ttk.Entry(self, width=55)
        self.entry_actual_address.grid(column=1, row=4, columnspan=2)

        tk.Label(self, text="Номер договора:", bd=5).grid(column=0, row=5)
        self.entry_contract_number = ttk.Entry(self, width=55)
        self.entry_contract_number.grid(column=1, row=5, columnspan=2)

        tk.Label(self, text="Дата договора:", bd=5).grid(column=0, row=6)
        self.entry_contract_date = DateEntry(self, width=50, foreground="white", bd=2)
        self.entry_contract_date.grid(column=1, row=6, columnspan=2)

        self.exit_button = ttk.Button(self, text='Отмена', width=20, command=self.destroy)
        self.exit_button.grid(column=1, row=7)

        self.create_button = ttk.Button(self, text='Создать', width=20, command=self.add_button_click)
        self.create_button.grid(column=2, row=7)

class AddDepartmentWindow(tk.Toplevel):
    def __init__(self, customer_name, department_name='', department_id=0):
        super().__init__()
        self.customer_name = customer_name
        self.department_name = department_name
        self.department_id = department_id
        self.init_window()

    def add_button_click(self):
        if self.entry_department_name.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Название отдела" должно быть заполнено.')
            return 1
        else:
            if self.department_id == 0:
                add_department_click(self.entry_department_name.get(), self.customer_name)
            else:
                edit_department_click(self.department_id, self.entry_department_name.get())
            self.destroy()

    def init_window(self):
        self.title('Добавить отдел')
        self.geometry('500x130+400+300')
        self.resizable(False, False)

        self.grab_set()
        self.focus_set()

        tk.Label(self, text=self.customer_name, bd=5).grid(column=0, row=0)

        tk.Label(self, text="Название отдела:", bd=5).grid(column=0, row=1)
        self.entry_department_name = ttk.Entry(self, width=55)
        self.entry_department_name.insert(0, self.department_name)
        self.entry_department_name.grid(column=1, row=1, columnspan=2)

        self.exit_button = ttk.Button(self, text='Отмена', width=20, command=self.destroy)
        self.exit_button.grid(column=1, row=2)

        self.create_button = ttk.Button(self, text='Создать', width=20, command=self.add_button_click)
        self.create_button.grid(column=2, row=2)

class AddWorkplaceWindow(tk.Toplevel):
    def __init__(self, department_id, department_name, customer_name, wp_name='', wp_id=0):
        super().__init__()
        self.department_id = department_id
        self.customer_name = customer_name
        self.department_name = department_name
        self.wp_name = wp_name
        self.wp_id = wp_id
        self.init_window()

    def add_button_click(self):
        if self.entry_wp_name.get() == '':
            messagebox.showerror('Ошибка', 'Поле "Название точки" должно быть заполнено.')
            return 1
        elif not self.check_noise.get() and not self.check_loc_vib.get() and not self.check_gen_vib.get():
            messagebox.showerror('Ошибка', 'Необходимо выбрать как минимум один фактор.')
            return 1
        else:
            if self.wp_id != 0:
                edit_workplace_click(self.wp_id, self.entry_wp_name.get(), self.check_noise.get(),
                                     self.check_loc_vib.get(), self.check_gen_vib.get())
            else:
                add_workplace_click(self.entry_wp_name.get(), self.department_id, self.check_noise.get(),
                                    self.check_loc_vib.get(), self.check_gen_vib.get())
            self.destroy()

    def init_window(self):
        self.title('Добавить рабочую зону')
        self.geometry('400x200+400+300')
        self.resizable(False, False)

        self.grab_set()
        self.focus_set()

        tk.Label(self, text=self.customer_name + ' - ' + self.department_name, bd=5). \
            grid(column=0, row=0, columnspan=3, sticky=W)

        tk.Label(self, text="Название точки:", bd=5).grid(column=0, row=2, sticky=W)
        self.entry_wp_name = ttk.Entry(self, width=40)
        self.entry_wp_name.insert(0, self.wp_name)
        self.entry_wp_name.grid(column=1, row=2, columnspan=2)

        tk.Label(self, text="Выберите факторы:", bd=5).grid(column=0, row=3, sticky=W)
        self.check_noise = tk.BooleanVar()
        self.check_noise.set(0)
        self.check_loc_vib = tk.BooleanVar()
        self.check_loc_vib.set(0)
        self.check_gen_vib = tk.BooleanVar()
        self.check_gen_vib.set(0)
        ttk.Checkbutton(self, text='Шум', variable=self.check_noise, onvalue=1, offvalue=0). \
            grid(column=1, row=3, columnspan=2, sticky=W)
        ttk.Checkbutton(self, text='Вибрация локальная', variable=self.check_loc_vib, onvalue=1, offvalue=0). \
            grid(column=1, row=4, columnspan=2, sticky=W)
        ttk.Checkbutton(self, text='Вибрация общая', variable=self.check_gen_vib, onvalue=1, offvalue=0). \
            grid(column=1, row=5, columnspan=2, sticky=W)

        tk.Label(self, text=" ", bd=5).grid(column=0, row=6, columnspan=3)

        self.exit_button = ttk.Button(self, text='Отмена', width=20, command=self.destroy)
        self.exit_button.grid(column=1, row=6)

        self.create_button = ttk.Button(self, text='Создать', width=20, command=self.add_button_click)
        self.create_button.grid(column=2, row=6)

class AddListboxCheckWindow(tk.Toplevel):
    def __init__(self, parent_window_box, name, var):
        super().__init__()
        self.name = name
        self.var = var
        self.result = []
        self.parent_window_box = parent_window_box
        self.init_window()

    def add_button_click(self):
        if not self.list_box.curselection():
            messagebox.showerror('Ошибка', 'Выберите хотя бы один пункт.')
            return 1
        else:
            items_list = self.list_box.curselection()
            for item in items_list:
                self.result.append(self.list_box.get(item).split(': ')[0])
            self.parent_window_box.insert(0, self.result)
            self.destroy()

    def init_window(self):
        self.title(f'Выберите {self.name}')
        self.geometry('370x190+400+300')
        self.resizable(False, False)

        self.grab_set()
        self.focus_set()

        tk.Label(self, text=f'Выберите {self.name}', bd=5).grid(column=0, row=0)
        self.list_box = tk.Listbox(self, listvariable=self.var, width=60, height=8, selectmode='extended')
        self.list_box.grid(column=0, row=1, columnspan=2)

        self.exit_button = ttk.Button(self, text='Отмена', width=20, command=self.destroy)
        self.exit_button.grid(column=0, row=2)

        self.create_button = ttk.Button(self, text='Добавить', width=20, command=self.add_button_click)
        self.create_button.grid(column=1, row=2)

    def __del__(self):
        return self.result


def _2016():
    """
     `2016`年复试上机题
    **要求：**
    文本文件`input.txt`由若干英文单词和分隔符(空格，回车，换行)构成. 根据如下
    说明编写程序统计不同单词出现的次数(频度).
    将统计结果按出现频度从高到低排序，并将出现频度大于`5`的单词及
    其频度输出到文件`output.txt`中. 文件格式如图所示
    ![format](E:\Markdown\img\format.png)
    * 多个连续的分隔符被视为一个分隔符.
    * 大小写敏感.
    * 每个单词的长度不超过`20`个字符.
    * 单词的数量未知. 如使用定义静态大数组的方式来统计，将被扣除`5`分.
    **程序：**
    :return:
    """
    file = open('input.txt', encoding='utf-8')
    txt = file.read()
    file.close()
    import re
    res = re.split(r'\s+', txt)
    print(res)
    dic = {}
    for word in res:
        dic[word] = dic.get(word, 0) + 1
    ans = []
    for k in dic:
        if dic.get(k) > 1:
            ans.append('{}: {}'.format(k, dic.get(k)))
    with open('output.txt', 'w', encoding='utf-8') as f:
        f.write('\n'.join(ans))

_2016()
with open("output.txt", 'r')as fp:
    print(fp.read())


import networkx as nx

from gurobipy import *

# Provides a subroutine to run gurobilp

def solve(sloc, stas, G, donttouch=set()):
    pcessors, alldists = nx.floyd_warshall_predecessor_and_distance(G)

    nodes = set(G.nodes)

    m = Model()

    """ Add variables and objective """

    # Decision variable for each edge, if pick it, adds driving cost, directed to simplify things
    e = {}
    for i in nodes:
        for j in nodes:
            e[i,j] = m.addVar(obj=2/3*alldists[i][j], vtype=GRB.BINARY, name="e{}_{}".format(i, j))
    m.update()

    # Decision variable for each TA at each vertex, if pick it, TA walks home from there
    v = {}
    for i in nodes:
        for k in stas:
            v[i,k] = m.addVar(obj=alldists[i][k], vtype=GRB.BINARY, name="v{}_{}".format(i, k))
    m.update()

    # Indicator variables, if 1 then this is a dropoff point, 0 otherwise
    ind = {}
    for i in nodes:
        ind[i] = m.addVar(vtype=GRB.BINARY, name="ind"+str(i))
    m.update()

    # # Sum variable to count number of non-home dropoffs
    # sumtot = m.addVar(name="sumtot")
    # m.update()

    # # Indicator variable if there is a non-home dropoffs
    # indtot = m.addVar(vtype=GRB.BINARY, name="indtot")
    # m.update()

    """ Add constraints """

    # Force dropoffs
    for i in donttouch:
        m.addLConstr(v[i,i] == 1)
    m.update()

    # No self loops
    for i in nodes:
        e[i,i].ub = 0
    m.update()

    # All TAs must be dropped off and only once
    for k in stas:
        m.addLConstr(quicksum(v[i,k] for i in nodes) == 1)
    m.update()

    # Setup dropoff indicators
    for i in nodes:
        m.addGenConstrOr(ind[i], [v[i,k] for k in stas])
    m.update()

    # # Add non-home sum constraint
    # m.addLConstr(quicksum(ind[i] for i in nodes if i != sloc) == sumtot)
    # m.update()

    # # Setup non-home indicator
    # m.addGenConstrOr(indtot, [ind[i] for i in nodes if i != sloc])
    # m.update()

    # Add in and out degree constraints for non-home vertices
    for i in nodes:
        if i != sloc:
            m.addLConstr(quicksum(e[i,j] for j in nodes) == ind[i])
            m.addLConstr(quicksum(e[j,i] for j in nodes) == ind[i])
    m.update()

    # # Add constraints for home, if non-home dropoff, then must leave and come back
    # m.addGenConstrIndicator(indtot, True, quicksum(e[sloc,i] for i in nodes) == 1.0)
    # m.addGenConstrIndicator(indtot, True, quicksum(e[i,sloc] for i in nodes) == 1.0)

    # Add constraints for home, if non-home dropoff, then must leave and come back
    m.addLConstr(quicksum(e[sloc,i] for i in nodes) == 1.0)
    m.addLConstr(quicksum(e[i,sloc] for i in nodes) == 1.0)

    # # Limit number of edges to form a cycle through dropoffs
    # expr = 0
    # for i in nodes:
    #     for j in nodes:
    #         expr += e[i,j]
    # m.addGenConstrIndicator(indtot, True, expr == sumtot + 1)

    """ Add callback to handle subtours """

    def cbSubtourElim(model, where):
        if where == GRB.callback.MIPSOL:
            ed = [] # make list of selected edges
            for i in nodes:
                for j in nodes:
                    sol = model.cbGetSolution(e[i,j]);
                    if sol > 0.5:
                        ed += [(i,j)]
            cycles = getCycles(ed)
            if len(cycles) > 1: # if more than 1 cycle, then we have a subtour
                for c in cycles:
                    expr = 0 # all edges in the subgraph must total <= |S|-1
                    for i in c:
                        for j in c:
                            expr += e[i,j]
                    model.cbLazy(expr <= len(c) - 1)

    def getCycles(edges):
        visited = {i[0]:False for i in edges}
        nexts = {i[0]:i[1] for i in edges}
        cycles = []

        while True:
            curr = -1
            for i in visited:
                if visited[i] == False:
                    curr = i
                    break
            if curr == -1:
                break
            thiscycle = []
            while not visited[curr]:
                visited[curr] = True
                thiscycle.append(curr)
                curr = nexts[curr]
            cycles.append(thiscycle)

        return cycles

    """ Run optimizer """

    m.params.TimeLimit = 1200 # allow only 20 min
    # m.params.MIPGap = 0.05 # allow 5% tolerance
    m.params.MIPFocus = 2 # focus on optimality
    m.params.LazyConstraints = 1
    m.optimize(cbSubtourElim)

    """ Reconstruct solution """

    # Get travel cycle
    ed = [] # get selected edges
    edec = m.getAttr('x', e)
    for i in nodes:
        for j in nodes:
            if edec[i,j] > 0.5:
                ed.append((i,j))
    cycles = getCycles(ed) # find the one cycle
    if cycles:
        c = cycles[0] # rearrange so sloc first
        indstart = c.index(sloc)
        jumpcycle = c[indstart:] + c[:indstart] + [sloc]
    else:
        jumpcycle = [sloc, sloc]

    # Reconstruct intermediate nodes
    listlocs = []
    curr = -1
    for i in range(len(jumpcycle)-1):
        curr = jumpcycle[i]
        while curr != jumpcycle[i+1]:
            listlocs.append(curr)
            curr = pcessors[jumpcycle[i+1]][curr]
    listlocs.append(curr)

    # Get list of dropoffs
    vdec = m.getAttr('x', v)
    listdropoffs = {}
    for i in nodes:
        for k in stas:
            if vdec[i,k] > 0.5:
                if i in listdropoffs:
                    listdropoffs[i].append(k)
                else:
                    listdropoffs[i] = [k]

    return listlocs, listdropoffs


n = 42
# find square root of all integers up to n
# we can map a lambda function
roots = map(lambda x: x**0.5, range(1, n+1))
for root in roots:
    print(root)

def my_comp(): # here we have a list comprehension
    roots = [x**0.5 for x in range(1, n+1)]
    # print(type(roots))
    for root in roots:
        print(root)

def my_gen(): # here is a generator comprehension
    roots = (x**0.5 for x in range(1, n+1)) # a tuple results in a generator object
    # print(type(roots))
    for root in roots:
        print(root)

if __name__ == '__main__':
    my_comp()
    my_gen()


import unittest

from antelope import EntityNotFound

from ...from_json import from_json
from ...archives import Qdb, REF_QTYS
from ...entities.tests.base_testclass import BasicEntityTest

mass_uuid = '93a60a56-a3c8-11da-a746-0800200b9a66'

ref_qs = from_json(REF_QTYS)
num_ref_quantities = len(ref_qs['quantities'])

class QdbTestCase(BasicEntityTest):
    """
    lots to test here
    """
    @classmethod
    def setUpClass(cls):
        super(QdbTestCase, cls).setUpClass()
        cls._qdb = Qdb.new()

    def test_0_qdb_initialization(self):
        self.assertEqual(self._qdb.count_by_type('quantity'), num_ref_quantities)
        self.assertGreaterEqual(num_ref_quantities, 26)  # current number as of 2021-10-30

    def test_mass(self):
        self.assertEqual(self._qdb.query.get_canonical('mass').uuid, mass_uuid)

    def test_canonical(self):
        m = self._qdb.query.get_canonical('mass')
        self.assertEqual(m['Name'], 'Mass')

    def test_kwh_mj_conversion(self):
        elec = next(self._qdb.search('flow', Name='electricity'))
        ncv = self._qdb.query.get_canonical('net calorific value')
        self.assertEqual(ncv.cf(elec), 3.6)

    def test_make_basic_interface(self):
        bi = self._qdb.query.get('93a60a56-a3c8-11da-a746-0800200c9a66')
        self.assertEqual(bi.entity_type, 'quantity')
        self.assertEqual(bi.origin, 'elcd.3.2')

    def test_external_quantity_lookup(self):
        """
        Some quantities should lookup, others should not
        :return:
        """
        for k in self.A.query.quantities():
            if k.external_ref in ('f6811440-ee37-11de-8a39-0800200c9a66', 'e288b5d2-9fcc-4a10-b13c-440786090f43'):
                # 'energy' (unspecified) and 'emissive coolness' respectively
                with self.assertRaises(EntityNotFound):
                    self._qdb.query.get_canonical(k.external_ref)
                self._qdb.add_entity_and_children(k)  # we commented out as foolhardy the code that auto-adds quantities in get_canonical
                self.assertIs(self._qdb.query.get_canonical(k.external_ref), k)
            else:
                qc = self._qdb.query.get_canonical(k)
                self.assertEqual(qc.origin, 'local.qdb')

if __name__ == '__main__':
    unittest.main()


from copy import deepcopy
from random import shuffle

import numpy as numpy

from hw4_knn import KnnClassifier
from hw2 import get_column, read_csv, remove_incomplete_rows
from hw3 import print_confusion
from hw4_Naive_Bayes import ContinuousNaiveBayes, NaiveBayes, NaiveBayesTitanic

class StratifiedFolds:

    def __init__(self, table, indexes, label_index):
        self.table = deepcopy(table)
        self.indexes = indexes
        self.label_index = label_index
        self.labels = []
        self.get_labels()
        self.num_labels = len(self.labels)

    def get_labels(self):

        for row in self.table:
            label = self.convert(row[self.label_index], [13, 14, 16, 19, 23, 26, 30, 36, 44])
            if label not in self.labels:
                self.labels.append(label)

    def stratified_k_folds(self, k):

        new_table = deepcopy(self.table)
        shuffle(new_table)

        partition_len = len(new_table)/(k-1)
        partitions = [new_table[i:i + partition_len] for i in range(0, len(new_table), partition_len)]

        init = [[0] * self.num_labels] * self.num_labels
        confusion = numpy.matrix(init)

        for part in partitions:
            temp = []
            for p in partitions:
                if part is not p:
                    temp += deepcopy(p)
            confusion += self.construct_confusion_matrix(part, temp)
        matrix = numpy.squeeze(numpy.asarray(confusion))

        return matrix.tolist()

    def construct_confusion_matrix(self, test_set, training_set):

        classifier = self.classification(training_set)

        init = [[0] * self.num_labels] * self.num_labels
        confusion = numpy.array(init)
        total = 0

        for instance in test_set:

            c = int(classifier.classify(instance))
            r = classifier.convert(instance[0], [13, 14, 16, 19, 23, 26, 30, 36, 44])

            confusion[r-1][c-1] += 1
            total += 1

        return numpy.matrix(confusion).tolist()

    @staticmethod
    def get_accuracy_of_confusion(matrix):

        total = (sum([sum(row) for row in matrix]))
        accuracies = []

        for i in range(len(matrix)):
            row = matrix[i]
            col = [r[i] for r in matrix]

            accuracies.append((total - (sum(col) + sum(row) - (2*row[i]))) / float(total))

        return round(sum(accuracies) / float(len(accuracies)), 2), accuracies

    def classification(self, training_set):
        return NaiveBayes(training_set, self.indexes, self.label_index)

    def categorize_table(self):

        for row in self.table:
            self.categorize_instance(row)

    def categorize_instance(self, row):

        row[0] = self.convert(row[0], [13, 14, 16, 19, 23, 26, 30, 36, 44])

    @staticmethod
    def convert(value, cutoffs):

        for i, item in enumerate(cutoffs):
            if float(value) < item:
                return i + 1
            elif float(value) > cutoffs[-1]:
                return len(cutoffs) + 1

class ContinuousStratifiedFolds(StratifiedFolds):

    def __init__(self, table, cat_indexes, cont_indexes, label_index):
        StratifiedFolds.__init__(self, table, cat_indexes, label_index)
        self.cont_indexes = cont_indexes

    def classification(self, training_set):
        return ContinuousNaiveBayes(training_set, self.indexes, self.cont_indexes, self.label_index)

class StratifiedFoldsKnn(StratifiedFolds):

    def __init__(self, table, indexes, label_index):
        StratifiedFolds.__init__(self, table, indexes, label_index)

        self.labels = [0, 1]

        self.num_labels = 2

    def get_labels(self):
        pass

    @staticmethod
    def convert_label(value):

        if value == 'yes':
            return 0
        else:
            return 1

    def construct_confusion_matrix_knn(self, test_set, training_set):

        classifier = self.classification(training_set)

        init = [[0] * self.num_labels] * self.num_labels
        confusion = numpy.array(init)
        total = 0

        for instance in test_set:

            c = classifier.classify(instance)
            r = self.convert_label(instance[3])
            confusion[r][c] += 1
            total += 1

        return numpy.matrix(confusion).tolist()

    def stratified_k_folds(self, k):

        new_table = deepcopy(self.table)
        shuffle(new_table)

        partition_len = len(new_table)/(k-1)
        partitions = [new_table[i:i + partition_len] for i in range(0, len(new_table), partition_len)]
        init = [[0] * self.num_labels] * self.num_labels
        confusion = numpy.matrix(init)
        for part in partitions:
            temp = []
            for p in partitions:
                if part is not p:
                    temp += deepcopy(p)
            confusion += self.construct_confusion_matrix_knn(part, temp)
        matrix = numpy.squeeze(numpy.asarray(confusion))

        return matrix.tolist()

    def classification(self, training_set):
        return KnnClassifier(training_set, self.indexes, self.label_index, 10)

class StratifiedFoldsTitanic(StratifiedFoldsKnn):

    def __init__(self, table, indexes, label_index):
        StratifiedFoldsKnn.__init__(self, table, indexes, label_index)

    def classification(self, training_set):
        return NaiveBayesTitanic(training_set, self.indexes, self.label_index)


#!/usr/bin/python3
""" Ex 5. Island Perimeter """

def island_perimeter(grid):
    """ returns the perimeter of the island """
    len_g = len(grid)
    len_f = len(grid[0])
    mts = 0

    for i in range(len_g):
        for j in range(len_f):
            if grid[i][j] == 1:
                if j == 0 or grid[i][j - 1] == 0:
                    mts = mts + 1
                if j == (len_f - 1) or grid[i][j + 1] == 0:
                    mts = mts + 1
                if i == 0 or grid[i - 1][j] == 0:
                    mts = mts + 1
                if i == (len_g - 1) or grid[i + 1][j] == 0:
                    mts = mts + 1

    return mts


from sklearn.model_selection import cross_val_score
from sklearn import preprocessing
import pandas as pd
import statistics
import operator
import tqdm
import pprint

#Grades features based on how well they do in the model.
#Starts out with zero features -> finds the best first feature.
#Then tries all other features together with that intial feature Etc.
#Until all features have been added.
#The order is their grade of "usefullness"

#Inputs:
#Model: The model you want to run

def feature_finder(model):
    score_keeper = {} # Will keep track of how much better we get when we add features
    features_added = pd.DataFrame() # Empty
    features_pending = pd.read_csv("training_data.csv") # Has all features + labels
    y = features_pending.pop("label") # Separate label from features

    categorical_features = features_pending[["key", "mode", "time_signature"]].copy() #Do not normalize categorical data!
    features_pending = features_pending.drop(["key", "mode", "time_signature"], axis=1) #Do not normalize categorical data!
    features_pending=(features_pending-features_pending.mean())/features_pending.std() #Normalize data!
    features_pending = pd.concat([features_pending,categorical_features],axis = 1)

    for i in tqdm.tqdm(range(len(features_pending.columns))):
        iteration_counter = 1
        temp_score_keeper = {}
        for feature in features_pending:

            temp_dataset = features_added.copy()
            temp_dataset = pd.concat([temp_dataset,features_pending[feature]],axis = 1)

            average_score = statistics.mean(cross_val_score(model, temp_dataset, y, cv=5)) #Take average of the 5 runs.
            temp_score_keeper[feature] = average_score

        best_feature = max(temp_score_keeper.items(), key=operator.itemgetter(1))[0] #Gets label with best score
        features_added = pd.concat([features_added,features_pending.pop(best_feature)],axis = 1)
        score_keeper[str(i+1)+"_"+best_feature] = temp_score_keeper[best_feature]

    return pprint.pformat(score_keeper)

#Test / Demo
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(random_state=0)
print(feature_finder(clf))