"""
Does the work to translate colour/effect names to ANSI codes
"""

# std libs
import numbers
import functools as ftl

# third-party libs
import numpy as np
from matplotlib.colors import to_rgb

# local libs
from recipes.dicts import ManyToOneMap

# relative libs
from .ansi import parse

# source: https://en.wikipedia.org/wiki/ANSI_escape_code
# http://ascii-table.com/ansi-escape-sequences.php

# Escape sequence
ESC = '\033'  # All sequences start with this character # equivalent to \x1b
CSI = ESC + '['  # Control Sequence Initiator
END = CSI + '0m'

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ANSI Codes for Text effects and colours  FG_CODES BG_CODE
FG_CODES = {
    'bold': 1,
    'dim': 2,  # faint
    'italic': 3,
    'underline': 4,
    'blink': 5,  # blink slow
    # 'blink' : 6,           # blink fast
    'invert': 7,
    'hidden': 8,  # conceal
    'strike': 9,
    # ------------------
    # 10	Primary(default) font
    # 11–19	{\displaystyle n} n-th alternate font	Select the {\displaystyle n}
    # n-th alternate font (14 being the fourth alternate font, up to 19 being
    # the 9th alternate font).
    # 20	Fraktur	hardly ever supported
    # 21	Bold: off or Underline: Double	Bold off not widely supported;
    # double underline hardly ever supported.
    # 22	Normal color or intensity	Neither bold nor faint
    # 23	Not italic, not Fraktur
    # 24	Underline: None	Not singly or doubly underlined
    # 25	Blink: off
    # 26	Reserved
    # 27	Image: Positive
    # 28	Reveal  conceal off
    # 29	Not crossed out
    # ------------------
    'black': 30,
    'red': 31,
    'green': 32,
    'yellow': 33,
    'blue': 34,
    'magenta': 35,
    'cyan': 36,
    'light gray': 37,
    # 38	Reserved for extended set foreground color typical supported next
    # arguments are 5;n where {\displaystyle n}￼ is color index (0..255) or
    # 2;r;g;b where {\displaystyle r,g,b}￼ are red, green and blue color
    # channels (out of 255)
    'default': 39,  # Default text color (foreground)
    # ------------------
    'frame': 51,
    'circle': 52,
    'overline': 53,
    # 54	Not framed or encircled
    # 55	Not overlined
    # ------------------
    'dark gray': 90,
    'gray': 90,
    'light red': 91,
    'light green': 92,
    'light yellow': 93,
    'light blue': 94,
    'light magenta': 95,
    'light cyan': 96,
    'white': 97,
}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Background Colours
BG_CODES = {
    'black': 40,
    'red': 41,
    'green': 42,
    'yellow': 43,
    'blue': 44,
    'magenta': 45,
    'cyan': 46,
    'light gray': 47,
    # ------------------
    # 48	Reserved for extended set background color	typical supported next
    # arguments are 5;n where {\displaystyle n}￼ is color index (0..255) or
    # 2;r;g;b where {\displaystyle r,g,b}￼ are red, green and blue color
    # channels (out of 255)
    'default': 49,
    # 49	Default background color	implementation defined (according to
    # standard)
    # 50	Reserved
    # ------------------
    # 56–59	Reserved
    # 60	ideogram underline or right side line	hardly ever supported
    # 61	ideogram double underline or double line on the right side	hardly
    # ever supported
    # 62	ideogram overline or left side line	hardly ever supported
    # 63	ideogram double overline or double line on the left side
    # hardly ever supported
    # 64	ideogram stress marking	hardly ever supported
    # 65	ideogram attributes off	hardly ever supported, reset the effects of
    # all of 60–64
    # ------------------
    'dark gray': 100,
    'light red': 101,
    'light green': 102,
    'light yellow': 103,
    'light blue': 104,
    'light magenta': 105,
    'light cyan': 106,
    'white': 107,
}
# TODO: alternate colour names here: https://en.wikipedia.org/wiki/ANSI_escape_code

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Convenient short colour descriptions a la matplotlib
mplShortsMap = {
    'b': 'blue',
    'g': 'green',
    'r': 'red',
    'c': 'cyan',
    'm': 'magenta',
    'y': 'yellow',
    'k': 'black',
    'w': 'white',
}

#
effectShortsMap = {'B': 'bold',
                   'I': 'italic',
                   'U': 'underline',
                   'S': 'strike',
                   'unbold': 'dim',
                   'strikethrough': 'strike',
                   'blink': 'blink_slow',
                   'hide': 'hidden',
                   'faint': 'dim'
                   }
# volcab is translated before keyword mappings in Many2One, so the uppercase
# here works

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# alias map for allowed keywords for functions
kwAliasMap = {
    # text
    'text': 'fg',
    'txt': 'fg',
    'colour': 'fg',
    'color': 'fg',
    'c': 'fg',
    'fg': 'fg',
    'foreground': 'fg',
    'rgb': 'fg',
    # background
    'highlight': 'bg',
    'background': 'bg',
    'bg': 'bg',
    'bc': 'bg',
    'bgc': 'bg'
}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Movement = {} # TODO

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# def _aliasFactory(codes, aliases):
#     """Create the code translation dict"""
#     Codes = ManyToOneMap(codes)
#     Codes.add_vocab(aliases)
#     Codes.add_map(str.lower)
#     return Codes

class KeyMap(ManyToOneMap):
    """
    Resolve all the various ways in which colours or effects can be specified.
    """

    def __init__(self, dic=None, **kws):
        super().__init__(dic, **kws)
        self.add_vocab(kwAliasMap)  # translation

    def __missing__(self, key):
        try:
            return super().__missing__(key)
        except KeyError:
            pass
        raise KeyError(
            f'{key} is not a valid description for text or background effect.')

class CodeMap(ManyToOneMap):  # CodeMap
    """
    Resolve all the various names for colours or effects into codes
    """

    # def __init__(self, dic=None, **kws):
    #     super().__init__(dic, **kws)
    # add mappings for matplotlib color names eg: 'r' --> 'red' etc..
    # self.add_vocab(mplShortsMap)
    # add a layer that maps to lower case: 'REd' --> 'red'
    # self.add_mapping(str.lower)

    def __getitem__(self, key):
        # make sure we always return a str
        return str(super().__getitem__(key))

    def __missing__(self, key):
        try:
            return super().__missing__(key)
        except KeyError:
            raise KeyError(f'Unknown colour or effect {key!r}') from None

# additional shorthands for bold / italic text
fg_codes = CodeMap(FG_CODES)
fg_codes.add_vocab(effectShortsMap)

codes = KeyMap(fg=fg_codes,
               bg=CodeMap(BG_CODES))

FORMAT_8BIT = KeyMap(fg='38;5;{:d}',
                     bg='48;5;{:d}')
FORMAT_24BIT = KeyMap(fg='38;2;{:d};{:d};{:d}',
                      bg='48;2;{:d};{:d};{:d}')

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Dispatch functions for translating user input to ANSI codes

@ftl.singledispatch
def resolve(obj, fg_or_bg='fg'):
    """default dispatch func for resolving ANSI codes from user input"""
    raise TypeError('Could not interpret %r (type %r) as a colour / effect '
                    'for %r' % (obj, type(obj), fg_or_bg))

# TODO: might want to give the functions below names for more readable traceback

@resolve.register(type(None))
def _(obj, fg_or_bg='fg'):
    return
    yield

@resolve.register(str)
def _(obj, fg_or_bg='fg'):
    if obj == '':
        return

    try:
        yield from resolve(to_24bit(obj), fg_or_bg)
    except ValueError:
        yield codes[fg_or_bg][obj]

@resolve.register(numbers.Integral)
def _(obj, fg_or_bg='fg'):
    # integers are interpreted as 8-bit colour codes
    if 0 <= obj < 256:
        yield FORMAT_8BIT[fg_or_bg].format(obj)
    else:
        raise ValueError('Could not interpret key %r as a 8 bit colour' % obj)

@resolve.register(np.ndarray)
@resolve.register(list)
@resolve.register(tuple)
def _(obj, fg_or_bg='fg'):
    # tuple, lists are interpreted as 24-bit rgb true colour codes
    if is_24bit(obj):
        if all(0 <= _ < 256 for _ in obj):
            yield FORMAT_24BIT[fg_or_bg].format(*obj)
        else:
            raise ValueError(
                f'Could not interpret key {obj!r} as a 24 bit colour'
            )
    else:
        for p in obj:
            yield from resolve(p, fg_or_bg)

@resolve.register(dict)
def _(obj, _=''):
    for key, val in obj.items():
        # `val` may have tuple of effects: eg: ((55, 55, 55), 'bold', 'italic')
        # but may also be a rgb tuple eg: (55, 55, 55)
        yield from resolve(val, key)

def is_24bit(obj):
    if len(obj) != 3:
        return False

    return all(isinstance(o, numbers.Integral) for o in obj)

def to_24bit(name):
    return tuple((np.multiply(to_rgb(name), 255).astype(int)))

def _gen_codes(*properties, **kws):
    """

    Parameters
    ----------
    properties
    kws

    Yields
    -------
    code: int
    """

    yield from resolve(properties)
    yield from resolve(kws)

def _get_params(*properties, **kws):
    # get the nrs '34;48;5;22' part of the code
    return ';'.join(_gen_codes(*properties, **kws))

def get(*properties, **kws):
    """
    Get the ANSI code for `properties` and `kws`

    Parameters
    ----------
    properties:
    kws:

    Returns
    -------

    """
    return ''.join((CSI, _get_params(*properties, **kws), 'm'))

def from_list(fg=None, bg=None):
    if fg is not None:
        return list(map(get, fg))

    if bg is not None:
        return [get(bg=_) for _ in bg]

def apply(s, *properties, **kws):
    """
    Apply the ANSI codes mapped to by `properties` and `kws` to the string `s`

    Parameters
    ----------
    s
    properties
    kws

    Returns
    -------

    """
    # first convert to str
    # string = str(s)

    # get code bits eg: '34;48;5;22'
    new_codes = _get_params(*properties, **kws)
    if not len(new_codes):
        return s

    # In order to get the correct representation of the string,
    # we strip and ANSI codes that are in place and stack the new codes
    # This means previous colours are replaced, but effects like 'bold' or
    # 'italic' will stack for recursive invocations.  This also means we get
    # the shortest representation of the string given the parameters which is
    # nice and efficient. If we were to apply blindly our string would be
    # longer than needed by a few (non-display) characters. This might seem
    # innocuous but becomes deadly once you start doing more complicated
    # effects on longer strings
    # note: final byte 'm' only valid for SGR (Select Graphic Rendition) and
    #  not other codes, but this is all we support for now

    return ''.join(''.join((CSI, params, ';', new_codes, 'm', w, END))
                   for _, params, _, w, _ in parse(s)
                   )  # .replace('\n', f'{END}\n{CSI}{new_codes}m'
    #
    # Finally, terminate and restart all codes at a newline boundary so that
    # we can more easily stack text blocks horizontally

def apply_naive(s, *properties, **kws):
    """
    Initial naive implementation of `apply` that blindly wraps the string with
    the ANSI codes.  Use `apply` instead of this function.
    """

    # get code string eg: '34;48;5;22'
    code = get(*properties, **kws)
    if not len(code):
        return s

    return ''.join((code, s, END))


m, n = map(int, input().split())
key = [list(map(int, input().split())) for _ in range(m)]
lock = [list(map(int, input().split())) for _ in range(n)]

def solution(key, lock):
    # 90도 회전
    def rotate_90(key):
        n = len(key)
        rot = [[0]*n for _ in range(n)]

        for r in len(n):
            for c in len(n):
                rot[c][n-1-r] = key[r][c]
        return rot
    # 맞는지 체크

    def check(new_lock):
        lock_length = len(new_lock) // 3
        for i in range(lock_length, lock_length*2):
            for j in range(lock_length, lock_length*2):
                if new_lock[i][j] != 1:
                    return False
        return True

    n = len(lock)  # lock 길이
    m = len(key)  # key 길이
    new_lock = [[0]*(n*3) for _ in range(n*3)]
    # 중간에 위치시킴
    for i in range(n):
        for j in range(n):
            new_lock[i+n][j+n] = lock[i][j]

    # 4방향에 대해 확인
    for rotation in range(4):
        key = rotate_90(key)
        # 0 ~ 2n - lock
        for x in range(n*2):
            # 0 ~ 2n
            for y in range(n*2):
                # 0 ~ m - key
                # 자물쇠에 열쇠 넣기
                for i in range(m):
                    for j in range(m):
                        new_lock[x+i][y+j] += key[i][j]
                if check(new_lock):
                    return True
                for i in range(m):
                    for j in range(m):
                        new_lock[x+i][y+j] -= key[i][j]
    return False


from functools import cache
from tkinter import W
from typing import List, Tuple
from unittest import TestCase, main

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        len_x, len_y = len(matrix[0]), len(matrix)

        @cache
        def dfs(x: int, y: int):
            if x >= len_x or y >= len_y:
                return False

            if matrix[y][x] == target:
                return True

            if matrix[y][x] > target:
                return False

            return dfs(x + 1, y) or dfs(x, y + 1)

        return dfs(0, 0)

class Test(TestCase):
    s = Solution()
    data: List[Tuple[List[List[int]], int, bool]] = [
        (
            [
                [1, 4, 7, 11, 15],
                [2, 5, 8, 12, 19],
                [3, 6, 9, 16, 22],
                [10, 13, 14, 17, 24],
                [18, 21, 23, 26, 30],
            ],
            5,
            True,
        ),
        (
            [
                [1, 4, 7, 11, 15],
                [2, 5, 8, 12, 19],
                [3, 6, 9, 16, 22],
                [10, 13, 14, 17, 24],
                [18, 21, 23, 26, 30],
            ],
            5,
            True,
        ),
        (
            [
                [1, 4, 7, 11, 15],
                [2, 5, 8, 12, 19],
                [3, 6, 9, 16, 22],
                [10, 13, 14, 17, 24],
                [18, 21, 23, 26, 30],
            ],
            20,
            False,
        ),
    ]

    def test_solution(self):
        for matrix, target, expected in self.data:
            self.assertEqual(self.s.searchMatrix(matrix, target), expected)

if __name__ == "__main__":
    main()


import unittest

import time
from BeautifulReport import BeautifulReport

from base.web_driver import browser
from config.setting import logging
from util.send_email import inser_img,get_time
now = time.strftime("%Y-%m-%d %H_%M_%S")
class StartEnd(unittest.TestCase):
    name=''

    @classmethod
    def setUpClass(cls):
            cls.driver = browser()
            logging.info("打开浏览器")

    def setUp(self):
        self.driver.implicitly_wait(10)
        self.driver.maximize_window()

    def tearDown(self):
        logging.info("检测异常处理")

        self._testMethodName=self._testname
        for method_name, error in self._outcome.errors:  # case如果执行失败，错误会保存到_outcome.errors 中
            if error:
                case_name = self._testname  # case名，即定义好的方法名
                report_error_name =get_time()+ case_name + '.png'
                # logging.error("report_error:", report_error_name)
                inser_img(self.driver,report_error_name)

    @classmethod
    def tearDownClass(cls):
        # if "end" not in cls.name:
        logging.info("用例正在结束："+cls.name)
        logging.info("关闭浏览器")
        cls.driver.quit()


#! /usr/bin/python
from getRoomKitData import send_command, data_to_dict, dict_to_json, start_connect
from dotenv import load_dotenv
import os

load_dotenv()

def get_xconfig(session):
    command = "xConfiguration\r"
    commandTrimmed = "xConfiguration"
    data = send_command(session, command)
    # with open('./callHistory.txt', 'r') as ch:
    #     data = ch.read()    callsArray.pop(0)
    mydict = data_to_dict(data, pattern="*c ")
    dict_to_json(mydict, commandTrimmed)

def main():
    session = start_connect()
    get_xconfig(session)

if __name__ == "__main__":
    main()


# IMPORT RANDRANGE FUNCTION FOR COMPUTER MOVEMENTS
from random import randrange

# CREATE THE STARTING BOARD
board = []
n = 1
for row in range(3):
    column = [n, n+1, n+2]
    n += 3
    board.append(column)
board[1][1] = 'X'

# FUNCTIONS
def displayBoard():

    # DISPLAY THE CURRENT STATUS OF THE BOARD

    print(
            f"""
            +-------+-------+-------+
            |       |       |       |
            |   {board[0][0]}   |   {board[0][1]}   |   {board[0][2]}   |
            |       |       |       |
            +-------+-------+-------+
            |       |       |       |
            |   {board[1][0]}   |   {board[1][1]}   |   {board[1][2]}   |
            |       |       |       |
            +-------+-------+-------+
            |       |       |       |
            |   {board[2][0]}   |   {board[2][1]}   |   {board[2][2]}   |
            |       |       |       |
            +-------+-------+-------+
            """
        )

def enterMove():

    # ASKS THE USER ABOUT THEIR MOVE, CHECKS THE INPUT AND UPDATES THE BOARD

    isFree = False

    try:
        move = int(input("Enter your move: "))

        if move < 1 or move > 9:
            print("ERROR! You can't enter a value less than 1 or greater than 9.")
            enterMove()
        else:

            for r, c in makeAListOfFreeFields():
                if board[r][c] == move:
                    isFree = True
                    move = (r, c)
            if isFree:
                board[move[0]][move[1]] = 'O'
            else:
                print("Invalid move.")
                enterMove()

    except:
        print("Invalid input!")
        enterMove()

def makeAListOfFreeFields():

    # BUILDS A LIST OF ALL THE FREE SQUARES, CONSISTING OF A TUPLE LIST

    freeFields = []
    for r in range(3):
        for c in range(3):
            if board[r][c] == 'X' or board[r][c] == 'O': continue
            freeFields.append((r, c))
    return freeFields

def victoryFor(sign):

    # THE FUNCTION VERIFY IF SOMEONE HAS WON THE GAME

    global gameStatus
    global winner

    gameStatus = True
    winner = ''

    if \
    (board[2][0] == board[1][1] and board[1][1] == board[0][2]) or \
    (board[0][0] == board[1][1] and board[1][1] == board[2][2]) or \
    (board[0][0] == board[1][0] and board[1][0] == board[2][0]) or \
    (board[0][1] == board[1][1] and board[1][1] == board[2][1]) or \
    (board[0][2] == board[1][2] and board[1][2] == board[2][2]) or \
    (board[0][0] == board[0][1] and board[0][1] == board[0][2]) or \
    (board[1][0] == board[1][1] and board[1][1] == board[1][2]) or \
    (board[2][0] == board[2][1] and board[2][1] == board[2][2]):
        gameStatus = False
        winner = sign
    elif len(makeAListOfFreeFields()) == 0:
        gameStatus = False
        winner = 'none'

def drawMove():

    # The function draws the computer's move and updates the board.

    cpuMove = randrange(1, 10)
    isFree = False

    for r, c in makeAListOfFreeFields():
        if board[r][c] == cpuMove:
            isFree = True
            cpuMove = (r, c)

    if isFree:
        board[cpuMove[0]][cpuMove[1]] = 'X'
    else:
        drawMove()

# EXE

displayBoard()

while True:

    enterMove()
    displayBoard()
    victoryFor('O')
    if not gameStatus: break

    drawMove()
    displayBoard()
    victoryFor('X')
    if not gameStatus: break

if winner == 'O': print("You won!")
elif winner == 'X': print("You lost!")
elif winner == 'none' : print("Tie.")


import csv
import datetime
import time
from datetime import date, timedelta
import netsvc
logger = netsvc.Logger()

if __name__ != '__main__':
    from tools import config
else:
    config={'addons_path':'/home/quentin/tinydev/cci/code/server/bin/addons'}

partner_dict = {}
partner_dict[''] = ''

dict_partner = {}

def _get_partner_id(char):
    return char

def convert2utf(row):
    if row:
        retRow = {}
        for k,v in row.items():
            retRow[k] = v.decode('latin1').encode('utf8').strip()
        return retRow
    return row

def get_first_day(dt, d_years=0, d_months=0):
    # d_years, d_months are "deltas" to apply to dt
    y, m = dt.year + d_years, dt.month + d_months
    a, m = divmod(m-1, 12)
    return date(y+a, m+1, 1)

def get_last_day(dt):
    return get_first_day(dt, 0, 1) + timedelta(-1)

def mkDateTime(dateString,strFormat="%Y-%m-%d"):
    # Expects "YYYY-MM-DD" string
    # returns a datetime object
    eSeconds = time.mktime(time.strptime(dateString,strFormat))
    return datetime.datetime.fromtimestamp(eSeconds)

def _get_tax_code_id(char):
    if char == '':
        return ''
    tmp = []
    for c in char.split(';'):
        if c != '':
            if c[0] == '+':
                tmp.append(('+','l10n_be.vat_code_a'+c[2:4]))
            else:
                tmp.append(('-','l10n_be.vat_code_a'+c[2:4]))
    return tmp

def construct_vat_dict(reader_vat_code, reader_vat, vat_dict):
    count = 0
    for row in reader_vat_code:
        #fill the first line with False
        if count != "0":
            if row['VSTORED,A,15']:
                vat_dict[row['VSTORED,A,15']] = {
                    'inv':_get_tax_code_id(row['VLBAINV,A,30']),
                    'vat':_get_tax_code_id(row['VLTAINV,A,30']),
                    'ref_inv':_get_tax_code_id(row['VLBACRE,A,30']),
                    'ref_vat':_get_tax_code_id(row['VLTACRE,A,30']),
                }
        else:
            vat_dict[''] = False
        count += 1
    count = 0
    for row in reader_vat:
        #fill the first line with False
        if count != "0":
            if row['VSTORED,A,15'] and vat_dict.has_key(row['VSTORED,A,15']):
                vat_dict[row['VSTORED,A,15']]['inv_account'] = row['VIMPINV,A,10']
                vat_dict[row['VSTORED,A,15']]['ref_account'] = row['VIMPCRE,A,10']
            else:
                vat_dict[row['VSTORED,A,15']]={
                    'inv':'',
                    'vat':'',
                    'ref_inv':'',
                    'ref_vat':'',
                    'inv_account':'',
                    'ref_account':'',
                }
        count += 1
    return vat_dict

# -=====================================-
# -= 1. Defining Structure and Mapping =-
# -=====================================-

# -= A. Chart of Accounts =-

def _check_code_4_usertype(x):
    if x['ABALANCE,A,10'] == 'LIABILIT':
        return 'account_type_liability'
    if x['ABALANCE,A,10'] == 'ASSETS':
        return 'account_type_asset'
    if x['ABALANCE,A,10'] == 'FXASSETS':
        return 'account_type_asset'
    if x['ABALANCE,A,10'] == 'INCOME':
        return 'account_type_income'
    if x['ABALANCE,A,10'] == 'DISCINC':
        return 'account_type_income'
    if x['ABALANCE,A,10'] == 'EXPENSE':
        return 'account_type_expense'
    if x['ABALANCE,A,10'] == 'DISCEXP':
        return 'account_type_expense'
    if x['ABALANCE,A,10'] == 'UNDEF':
        return 'account_type_root'
#    if x['AID,A,10'].startswith('6'):
#        return 'account_type_expense'
#    if x['AID,A,10'].startswith('7'):
#        return 'income'
    return 'account_type_root'

def _check_code_4_type(x):
    if x['AID,A,10'].startswith('40'):
        if x['AID,A,10'].startswith('406'):
            return 'payable'
        return 'receivable'
    if x['AID,A,10'].startswith('44'):
        return 'payable'
    if len(x['AID,A,10']) <= 4:
        return 'view'
    return 'other'

account_map = {
   'id': lambda x: 'account_'+x['AID,A,10'],
    'code': lambda x: x['AID,A,10'],
    'name': lambda x: x['HEADING1,A,40'],
    'note': lambda x: x['AMEMO,M,11'],
    'type': lambda x: _check_code_4_type(x),
    'user_type:id': lambda x: _check_code_4_usertype(x),
    'parent_id:id': lambda a: ''#'account_bob_0'
}

def import_account(reader, writer, mapping):
    record = {}
    for key, column_name in mapping.items():
        record[key] = key
    writer.writerow(record)
    temp_dict = {}
    list_ids = []
    list_rows = []
    for row in reader:
        record = {}
        for key,fnct in mapping.items():
            record[key] = fnct(convert2utf(row))
        temp_dict[record['code']]=record
        list_ids.append(record['code'])

    temp_keys = map(lambda x: int(x),temp_dict.keys())
    temp_keys.sort()
    temp_str_keys = map(lambda x: str(x),temp_keys)
    for t in temp_str_keys:
        if len(t)>1:
            l = len(temp_dict[t]['code'])
            aa = range(l+1)
            aa.reverse()
            aa.pop()
            for i in aa:
                if temp_dict[t]['code'][0:i-1] in list_ids:
                    temp_dict[t]['parent_id:id'] = 'account_' + str(temp_dict[t]['code'][0:i-1])
                    break
        else:
            temp_dict[t]['parent_id:id'] = 'account_bob_import.account_bob_0'
        list_rows.append(temp_dict[t])
    writer.writerows(list_rows)
    return True

# -= B. Financial Journals =-

journals_map = {
    'id' : lambda x: 'journal_'+x['DBID,A,4'],
    'code': lambda x: x['DBID,A,4'],
    'name': lambda x: x['HEADING1,A,30'],
    'view_id:id': lambda x: 'account.account_journal_view', # journal view for all except the ones that are of type cash => cash journal view
    'currency:id': lambda x: x['DBCURRENCY,A,3'],#to be check
    'sequence_id:id': lambda x: 'account.sequence_journal', #entry journal for all
    'type': lambda x: {
        'PUR': 'purchase',
        'PUC': 'purchase',
        'SAL': 'sale',
        'SAC': 'sale',
        'CAS': 'cash',
        'ISB': 'general',#default
        'PRI': 'general',#default
        'ISD': 'general',#default
        'ICO': 'general',#default
        'ISO': 'general',#default
        'PRO': 'general',#default
        'COP': 'general',#default
        'ISI': 'general',#default
        'ISM': 'general',#default
        'IDN': 'general',#default
        'ICE': 'general',#default
        '':'general'
        #else should be of 'general' type

    }[x['DBTYPE,A,3']],
    'default_debit_account_id:id':lambda x: x['DBACCOUNT,A,10'], #filled with the id of the account_account with code = x['DBACCOUNT,A,10'],
    'default_credit_account_id:id':lambda x: x['DBACCOUNT,A,10'] ,#filled with the id of the account_account with code =
}

def import_journal(reader_journal, writer_journal, journals_map):
    record = {}
    for key, column_name in journals_map.items():
        record[key] = key
    writer_journal.writerow(record)
    for row in reader_journal:
        record = {}
        for key,fnct in journals_map.items():
            record[key] = fnct(convert2utf(row))
        if record['default_debit_account_id:id']:
            record['default_debit_account_id:id'] = 'account_' + str(record['default_debit_account_id:id'])
        if record['default_credit_account_id:id']:
            record['default_credit_account_id:id'] = 'account_' + str(record['default_credit_account_id:id'])
        if record['type']=='cash':
            record['view_id:id']='account.account_journal_bank_view'
        cur = ''
        if record['currency:id']:
            cur = 'base.' + record['currency:id'].upper()
        record['currency:id'] = cur
        writer_journal.writerow(record)
    return True

# -= C. Partners Data =-
#Beware: If 2 partners have the same name, we have to create only one partner with several adresses.
#We also have to record all their old names because they can be referenced in another files (e.g. the account_move_line one).
#That's the reason why we keep a dictionary to match the IDS.

def _get_cat(record):
#have to put the partner into category suppliers if CSUPTYPE,A,1 == 'S'
#have to put the partner into category customers if CCUSTYPE,A,1 == 'C'
    res=[]
    if 'CSUPTYPE,A,1' in record and record['CSUPTYPE,A,1'].upper() in ['S'] :
        res.append('base.res_partner_category_8')
    if 'CCUSTYPE,A,1' in record and record['CCUSTYPE,A,1'].upper() in ['C']:
        res.append('base.res_partner_category_0')
    return ','.join(res)

partners_map = {
    'id':lambda x: x['CID,A,10'],
    'ref': lambda x: x['CID,A,10'],
    'name': lambda x: x['CNAME1,A,40'],
    'lang': lambda x: {
        #/!\ if a lang isn't installed, the value should be filled with ''

        'E': 'en_US', #'E' for English
        'D': 'de_DE',#'de_DE',#?? #'D' for German....de_DE
        'F': 'fr_FR',#'fr_FR',#??#'F' for French..fr_FR
        'N': 'nl_NL',#'nl_NL',#??#'N' for Dutch....nl_NL
        'A': '',#no lang
        '' : ''
    }[x['CLANGUAGE,A,2']],
    'vat': lambda x: x['CVATNO,A,12'],
    'website': lambda x: x['HTTPADDRESS,A,60'],
    'comment': lambda x: x['CMEMO,M,11'],
    'domiciliation_bool': lambda x : x['CBANKORDERPAY,L,1'],
    'domiciliation': lambda x : x['CBANKORDERPAYNO,A,15'],
    'category_id:id':lambda x:_get_cat(x),
}

#have to create one res.partner.adress for this partner with this
partner_add_map = {
    'id' : lambda x: '',
    'city' : lambda x: x['CLOCALITY,A,40'],
    'fax': lambda x: x['CFAXNO,A,25'],
    'zip' :  lambda x: x['CZIPCODE,A,10'],
    'country_id:id':lambda x: x['CCOUNTRY,A,6'], #filled with id of res.country that have code == x['CCOUNTRY,A,6']
    'phone' : lambda x: x['CTELNO,A,25'],
    'street' : lambda x: x['CADDRESS1,A,40'],
    'type' : lambda x: 'default',
    'partner_id:id':lambda x: ''
}

#have to create res.partner.bank if x['CBANKNO,A,20'] <> False
partner_bank_map = {
    'state': lambda x:'bank',#should be filled with id of res.Partner.bank.type that have name == 'Bank Account'
    'acc_number': lambda x: x['CBANKNO,A,20'],
    'partner_id:id':lambda x:''
}

def import_partner(reader_partner, writer_partner, partners_map, writer_address, partner_add_map, writer_bank, partner_bank_map):
    record = {}
    record_address = {}
    record_bank = {}
    list_partners = {}

    for key, column_name in partners_map.items():
        record[key] = key
    for key, column_name in partner_add_map.items():
        record_address[key] = key
    for key, column_name in partner_bank_map.items():
        record_bank[key] = key

    writer_partner.writerow(record)
    writer_address.writerow(record_address)
    writer_bank.writerow(record_bank)
    count_address = 0
    for row in reader_partner:
        record = {}
        record_address = {}
        record_bank = {}
        for key,fnct in partners_map.items():
            record[key] = fnct(convert2utf(row))
        for key,fnct in partner_add_map.items():
            record_address[key] = fnct(convert2utf(row))
        partner_name = record['name']
        if partner_name != "":

            if record['lang'] not in langs:
                langs.append(record['lang'])
            #partner already exists
            count_address = count_address + 1
            record_address['id'] = 'add' + str(count_address)
            if list_partners.has_key(record['name']):
                record_address['type'] = 'other'
                partner_dict[row['CID,A,10']] = list_partners[record['name']]

            else:
                #record it
                list_partners[record['name']] = row['CID,A,10']
                partner_dict[row['CID,A,10']] = record['id']
                dict_partner[record['ref']] = record_address['id']
                if not record['domiciliation_bool'] == '1':
                    record['domiciliation_bool'] = ''
                writer_partner.writerow(record)

            #create bank account if necessary
            if row.has_key('CBANKNO,A,20') and row['CBANKNO,A,20']:
                for key,fnct in partner_bank_map.items():
                    record_bank[key] = fnct(convert2utf(row))
                record_bank['partner_id:id'] = _get_partner_id(partner_dict[row['CID,A,10']])
                writer_bank.writerow(record_bank)

            #create address in all cases ('default' address if partner didn't exist before, 'other' otherwise)
            address = ''
            if record_address['country_id:id']:
                    address = 'base.'+record_address['country_id:id'].lower()

            record_address['partner_id:id'] = _get_partner_id(partner_dict[row['CID,A,10']])
            record_address['country_id:id'] = address
            writer_address.writerow(record_address)

    return True

# -= D. Contacts Data =-

contacts_map = {
    'id': lambda x:'' ,
    'first_name': lambda x: x['PFIRSTNAME,A,30'],
    'name': lambda x: x['PNAME,A,30'],
    'title': lambda x: {
        '0':'', #keep empty
        '1':'Mss', #should be the id of res.partner.title where name == 'Miss'
        '2':'Ms.', #should be the id of res.partner.title where name == 'Madam'
        '3':'M.', #should be the id of res.partner.title where name == 'Sir'
        '':'', #keep empty
        #~ #/!\ if an id cannot be found, the value should be ''
     }[x['PMF,A,1']],
    'mobile': lambda x: x['PGSM,A,25'],
#    'lang_id': lambda x: {
#        'E': 'English',#should be the id of res.lang where name == 'English'
#        'D': 'German',#should be the id of res.lang where name == 'German'
#        'F': 'French',#should be the id of res.lang where name == 'French'
#        'N': 'Dutch',#should be the id of res.lang where name == 'Dutch'
#        '': ''#for empty data.....
#
#        #~ #/!\ if an id cannot be found, the value should be ''
#     }[x['PLANGUAGE,A,2']],
#~ #have to be linked to the default adress of the partner with code == x['PCID,A,10']
    }
job_map = {
       #'id' : lambda x : '',
       'address_id:id' : lambda x:'',
       'contact_id:id' : lambda x:'',
       'function_id:id': lambda x:'account_bob_import.res_partner_function_bob',
       #'function_label' : lambda x:'' ...should be check...for cci users
}
def import_contact(reader_contact, writer_contact, contacts_map, writer_job, job_map):
    record = {}
    record_job = {}
    for key, column_name in contacts_map.items():
        record[key] = key
    for key, column_name in job_map.items():
        record_job[key] = key
    writer_contact.writerow(record)
    writer_job.writerow(record_job)
    count_contact = 0
    for row in reader_contact:
        record = {}
        record_job = {}
        for key,fnct in contacts_map.items():
            record[key] = fnct(convert2utf(row))
        for key,fnct in job_map.items():
            record_job[key] = fnct(convert2utf(row))
        count_contact = count_contact + 1
        record['id'] = "cont" + str(count_contact)
        record_job['contact_id:id'] = record['id']
        if dict_partner.has_key(row['PCID,A,10']):
            record_job['address_id:id'] = dict_partner[row['PCID,A,10']]
        else:
            record_job['address_id:id'] = 'account_bob_import.res_partner_address_bob'
        writer_contact.writerow(record)
        writer_job.writerow(record_job)
    return True

# -= E. Periods and FY =-

fyear_map = {
    'id':  lambda x: 'FY'+x['YEAR,I,4'],
    'date_stop': lambda x: x['YEAR,I,4']+'-12-31', #last day of x['YEAR,I,4']
    'date_start': lambda x: x['YEAR,I,4']+'-01-01',#first day of x['YEAR,I,4']
    'code': lambda x: 'FY'+x['YEAR,I,4'],
    'name': lambda x: 'Fiscal Year '+x['YEAR,I,4'],
    'state': lambda x: 'draft',
}

def import_fyear(reader_fyear, writer_fyear, fyear_map):
    record = {}
    for key, column_name in fyear_map.items():
        record[key] = key
    writer_fyear.writerow(record)
    fyear_rows = []
    fyear_rows_ref = []
    #parse the period csv file to know what are the fiscal years that need to be created
    for row in reader_fyear:
        if row['YEAR,I,4'] not in fyear_rows_ref:
            fyear_rows_ref.append(row['YEAR,I,4'])
            fyear_rows.append(row)

    #create the fiscal years
    for fyear in fyear_rows:
        record = {}
        for key,fnct in fyear_map.items():
            record[key] = fnct(convert2utf(fyear))
        writer_fyear.writerow(record)
    return True

periods_map = {
    'id': lambda x: 'period_'+x['YEAR,I,4']+"/"+x['MONTH,I,4'],
    'date_stop': lambda x: get_last_day(mkDateTime(x['YEAR,I,4']+"-"+x['MONTH,I,4']+"-01")).strftime("%Y-%m-%d"),#last day of x['MONTH,I,4']
    'date_start': lambda x:get_first_day(mkDateTime(x['YEAR,I,4']+"-"+x['MONTH,I,4']+"-01")).strftime("%Y-%m-%d"), #first day of x['MONTH,I,4']

    'name': lambda x: x['LABEL,A,8'],
    'state': lambda x: 'draft',
    'fiscalyear_id:id': lambda x: 'FY'+x['YEAR,I,4'],
}

def import_period(reader_period, writer_period, period_map):
    record = {}
    for key, column_name in period_map.items():
        record[key] = key
    writer_period.writerow(record)
    period_rows = []
    for row in reader_period:
        #only create periods if x['MONTH,I,4'] != 0
        if row['MONTH,I,4'] != "0":
            record = {}
            for key,fnct in period_map.items():
                record[key] = fnct(convert2utf(row))
            writer_period.writerow(record)
    return True

# -= F. Reconcile =-

arecon_map = {
    'id' : lambda x: 'a'+x['HID,A,10'].strip()+'_'+x['HMATCHNO,I,4'],
    'type': lambda x: 'bob imported',
    'name': lambda x: 'a'+x['HID,A,10'].strip()+'_'+x['HMATCHNO,I,4'],
}

crecon_map = {
    'id' : lambda x: 'c'+x['HID,A,10'].strip()+'_'+x['HMATCHNO,I,4'],
    'type': lambda x: 'bob imported',
    'name': lambda x: 'c'+x['HID,A,10'].strip()+'_'+x['HMATCHNO,I,4'],
}

def import_areconcile(reader, writer, map):
    #write the header of creconcile
    record = {}
    for key, column_name in map.items():
        record[key] = key
    writer.writerow(record)
    dict = {}
    last_id = ""
    for row in reader:

        dict[row['HID,A,10']+row['HDBK,A,4']+row['HFYEAR,A,5']+row['HMONTH,I,4']+'/'+row['HDOCNO,I,4']+'/'+row['HORDERNO,I,4']] = row['HMTACHNO_ID:ID']
        record = {}
        for key,fnct in map.items():
            record[key] = fnct(convert2utf(row))
        if last_id != record['id']:
            writer.writerow(record)
        last_id = record['id']
    return dict

def import_creconcile(reader, writer, map):
    #write the header of creconcile
    record = {}
    for key, column_name in map.items():
        record[key] = key
    writer.writerow(record)
    dict = {}
    last_id = ""
    for row in reader:
        dict[row['HTYPE,A,1']+row['HID,A,10']+row['HFYEAR,A,5']+row['HDBK,A,4']+'/'+row['HDOCNO,I,4']+'/'+row['HORDERNO,I,4']] = row['HMATCHNO_ID:ID']
        record = {}
        for key,fnct in map.items():
            record[key] = fnct(convert2utf(row))
        if last_id != record['id']:
            writer.writerow(record)
        last_id = record['id']
    return dict

# -= G. Move and Move_line =-

move_map = {
    'id': lambda x: 'move_'+x['HDBK,A,4']+'/'+x['HFYEAR,A,5']+'/'+x['HDOCNO,I,4'],
    'journal_id:id': lambda x: 'journal_'+x['HDBK,A,4'],
    'state': lambda x: 'draft',
    'period_id:id': lambda x: 'period_'+x['HFYEAR,A,5']+"/"+x['HMONTH,I,4'],
    'ref': lambda x: '',
}

def _check_debit(x):
    if (float(x) > 0):
        return float(x)
    return 0

def _check_credit(x):
    if (float(x) < 0):
        return -(float(x))
    return 0

def _get_ammount_currency(x):
    if x['HORDERNO,I,4'] != '1':
        return _check_debit(x['HAMOUNT,$,8']) + _check_credit(x['HAMOUNT,$,8'])
    return 0

def _check_debit_vat(x, ref):
    if ref.startswith('-'):
        return 0
    if (float(x) < 0):
        return -(float(x))
    return float(x)

def _check_credit_vat(x, ref):
    if ref.startswith('-'):
        if (float(x) < 0):
            return -(float(x))
        return float(x)
    return 0

#def _get_ammount_currency_vat(x):
#    if x['HORDERNO,I,4'] != '1':
#        return _check_debit_vat(x['HTAX,$,8'],x['HAMOUNT,$,8']) - _check_credit_vat(x['HTAX,$,8'],x['HAMOUNT,$,8'])
#    return 0

def _pick_vat_code(x, vat_dict, is_vat=False):
    if is_vat:
        if x['HDBTYPE,A,3'][2]=='C':
            #the move is a refund
            return vat_dict[x['HVATCODE,A,10']]['ref_vat']
        return vat_dict[x['HVATCODE,A,10']]['vat']

    if x['HDBTYPE,A,3'][2]=='C':
        #the move is a refund
        return vat_dict[x['HVATCODE,A,10']]['ref_inv']
    return vat_dict[x['HVATCODE,A,10']]['inv']

def _pick_vat_account(x, vat_dict):
    if x['HDBTYPE,A,3'][2]=='C':
        #the move is a refund
        return vat_dict[x['HVATCODE,A,10']]['ref_account'] and 'account_'+vat_dict[x['HVATCODE,A,10']]['ref_account'] or 'account_'+x['HID,A,10']
    return vat_dict[x['HVATCODE,A,10']]['inv_account'] and 'account_'+vat_dict[x['HVATCODE,A,10']]['inv_account'] or 'account_'+x['HID,A,10']

def _create_vat_move(x, vat_dict, count):
    res = []
    count = 0
    for vat_code in _pick_vat_code(x,vat_dict,True):
        count += 1
        if count == 1:
            res.append(_create_vat_move_core(x, vat_code, vat_dict, count))
        else:
            res.append(_create_vat_move_vat(x, vat_code, vat_dict, count, 'HTAX,$,8'))
    return res

def _create_vat_move_vat(x, vat_code, vat_dict, count,base_or_vat):
    return {
        'id': 'move_line_'+x['HDBK,A,4']+'/'+x['HFYEAR,A,5']+'/'+x['HDOCNO,I,4']+'/'+x['HORDERNO,I,4']+'/'+str(count),
        'currency_id': x['HCURRENCY,A,3'],
        'date_maturity': x['HDUEDATE,D,4'],
        'partner_id:id': _get_partner_id(partner_dict[x['HCUSSUP,A,10']]),
        'journal_id:id': 'journal_'+x['HDBK,A,4'],
        'tax_code_id:id': vat_code[1],
        'tax_amount': str(abs(float(x[base_or_vat])) * _get_float(vat_code[0])),
        'state': 'draft',
        'debit': '0',
        'credit': '0',
        'ref':  x['HDOCNO,I,4'],
        'account_id:id': _pick_vat_account(x, vat_dict),
        'period_id:id': 'period_'+x['HFYEAR,A,5']+"/"+x['HMONTH,I,4'],
        'date': x['HDOCDATE,D,4'],
        'move_id:id': 'move_'+x['HDBK,A,4']+'/'+x['HFYEAR,A,5']+'/'+x['HDOCNO,I,4'],
        'name': x['HREM,A,40'] or '/',
#        'amount_currency': str(_get_ammount_currency_vat(x)),
    }

def _create_vat_move_core(x, vat_code, vat_dict, count):
    return {
        'id': 'move_line_'+x['HDBK,A,4']+'/'+x['HFYEAR,A,5']+'/'+x['HDOCNO,I,4']+'/'+x['HORDERNO,I,4']+'/'+str(count),
        'currency_id': x['HCURRENCY,A,3'],
        'date_maturity': x['HDUEDATE,D,4'],
        'partner_id:id': _get_partner_id(partner_dict[x['HCUSSUP,A,10']]),
        'journal_id:id': 'journal_'+x['HDBK,A,4'],
        'tax_code_id:id': vat_code[1],
        'tax_amount': str(abs(float(x['HTAX,$,8'])) * _get_float(vat_code[0])),
        'state': 'draft',

        'debit': str(_check_debit_vat(x['HTAX,$,8'],x['HAMOUNT,$,8'])),
        'credit': str(_check_credit_vat(x['HTAX,$,8'],x['HAMOUNT,$,8'])),
        'ref':  x['HDOCNO,I,4'],
        'account_id:id': _pick_vat_account(x, vat_dict),
        'period_id:id': 'period_'+x['HFYEAR,A,5']+"/"+x['HMONTH,I,4'],
        'date': x['HDOCDATE,D,4'],
        'move_id:id': 'move_'+x['HDBK,A,4']+'/'+x['HFYEAR,A,5']+'/'+x['HDOCNO,I,4'],

        'name': x['HREM,A,40'] or '/',
#        'amount_currency': str(_get_ammount_currency_vat(x)),
    }

#check if the movement is a VAT movement: return TRUE if the account code begins with '450' or '451'
def _is_vat_movement(x):
    return x['HID,A,10'].startswith(('450','451','411'))

def _get_float(char):
    if char == '-':
        return -1
    return 1

move_line_map = {
#TODO check currency import
#TODO (bugfix): create one currency BEF with value: 1 EUR = 40.3399 BEF

    'id': lambda x: 'move_line_'+x['HDBK,A,4']+'/'+x['HFYEAR,A,5']+'/'+x['HDOCNO,I,4']+'/'+x['HORDERNO,I,4'],
    'currency_id': lambda x: x['HCURRENCY,A,3'],
    'date_maturity': lambda x: x['HDUEDATE,D,4'],
    'partner_id:id': lambda x: _get_partner_id(partner_dict[x['HCUSSUP,A,10']]),
    'journal_id:id': lambda x: 'journal_'+x['HDBK,A,4'],
    'tax_code_id:id': lambda x:'',
    'tax_amount': lambda x:'',

    'state': lambda x: 'draft',

        #qd vente: <0 c'est credit et >0 c'est debit
        #qd achat: <0 c'est le credit et >0 c'est debit
    'debit': lambda x: str(_check_debit(x['HAMOUNT,$,8'])),
    'credit': lambda x: str(_check_credit(x['HAMOUNT,$,8'])),
    'ref': lambda x: x['HDOCNO,I,4'],
    'account_id:id': lambda x: 'account_'+x['HID,A,10'],
    'period_id:id': lambda x: 'period_'+x['HFYEAR,A,5']+"/"+x['HMONTH,I,4'],
    'date': lambda x: x['HDOCDATE,D,4'],
    'move_id:id': lambda x: 'move_'+x['HDBK,A,4']+'/'+x['HFYEAR,A,5']+'/'+x['HDOCNO,I,4'],
    'reconcile_id:id': lambda x: '',
    'name': lambda x: x['HREM,A,40'] or '/',
#    'amount_currency': lambda x: str(_get_ammount_currency(x)),
}

def import_moves_and_lines(reader_move, writer_move, writer, move_map, map, dict_ahisto, dict_chisto, vat_dict):
    #write the header of account.move
    record = {}
    for key, column_name in move_map.items():
        record[key] = key
    writer_move.writerow(record)
    #write the header of account.move.line
    record = {}
    for key, column_name in map.items():
        record[key] = key
    writer.writerow(record)

    move_rows_ref = {}
    count =0
    period_rows = []
    tvacount = 0
    #parse the move.csv file to know what are the account_move that need to be created
    for row in reader_move:
        count += 1
        if (count%1000) == 0:
             logger.notifyChannel(count)

        if row['HCURRENCY,A,3'] not in currencies:
            currencies.append(row['HCURRENCY,A,3'])

        #only create move and move_line if x['HMONTH,I,4'] != 0
        #and if row['HAMOUNT,$,8']!="" is different from 0 (or False)
        if row['HMONTH,I,4'] != "0" and row['HAMOUNT,$,8']!="" and not float(row['HAMOUNT,$,8']) == 0.0:
            temp = 'move_line_'+row['HDBK,A,4']+'/'+row['HFYEAR,A,5']+'/'+row['HDOCNO,I,4']+'/'+row['HORDERNO,I,4']
            if not move_rows_ref.has_key(temp):
                #writing of the account.move
                move_rows_ref[temp] = 'ok'

                record = {}
                for key,fnct in move_map.items():
                    record[key] = fnct(convert2utf(row))
                writer_move.writerow(record)

            #writing of the account.move.line
            if _is_vat_movement(row):
                #vat movement cannot be imported and have to be generated from the move line
                continue

            record = {}
            for key,fnct in map.items():
                record[key] = fnct(convert2utf(row))

            if dict_ahisto.has_key(row['HID,A,10']+row['HDBK,A,4']+row['HFYEAR,A,5']+row['HMONTH,I,4']+'/'+row['HDOCNO,I,4']+'/'+row['HORDERNO,I,4']):
                record['reconcile_id:id'] = dict_ahisto[row['HID,A,10']+row['HDBK,A,4']+row['HFYEAR,A,5']+row['HMONTH,I,4']+'/'+row['HDOCNO,I,4']+'/'+row['HORDERNO,I,4']]

            #for the case of sales or sales refund
            elif row['HDBTYPE,A,3'] == 'SAL' or row['HDBTYPE,A,3'] == 'SAC':
                if dict_chisto.has_key('C'+row['HCUSSUP,A,10']+row['HFYEAR,A,5']+row['HDBK,A,4']+'/'+row['HDOCNO,I,4']+'/'+row['HORDERNO,I,4']):
                    record['reconcile_id:id'] = dict_chisto['C'+row['HCUSSUP,A,10']+row['HFYEAR,A,5']+row['HDBK,A,4']+'/'+row['HDOCNO,I,4']+'/'+row['HORDERNO,I,4']]

            #for the case of purchases or purchases refund
            elif row['HDBTYPE,A,3'] == 'PUR' or row['HDBTYPE,A,3'] == 'PUC':
                if dict_chisto.has_key('S'+row['HCUSSUP,A,10']+row['HFYEAR,A,5']+row['HDBK,A,4']+'/'+row['HDOCNO,I,4']+'/'+row['HORDERNO,I,4']):
                    record['reconcile_id:id'] = dict_chisto['S'+row['HCUSSUP,A,10']+row['HFYEAR,A,5']+row['HDBK,A,4']+'/'+row['HDOCNO,I,4']+'/'+row['HORDERNO,I,4']]

            else:
                #for the case of other operations. We have to search for the reconciliation with a customer and a supplier and we have to add 1 to the orderno
                tmp = str(int(row['HORDERNO,I,4'])+1)
                if dict_chisto.has_key('C'+row['HCUSSUP,A,10']+row['HFYEAR,A,5']+row['HDBK,A,4']+'/'+row['HDOCNO,I,4']+'/'+tmp):
                    record['reconcile_id:id'] = dict_chisto['C'+row['HCUSSUP,A,10']+row['HFYEAR,A,5']+row['HDBK,A,4']+'/'+row['HDOCNO,I,4']+'/'+tmp]
                elif row['HDBTYPE,A,3'] == 'PUR' or row['HDBTYPE,A,3'] == 'PUC':
                    if dict_chisto.has_key('S'+row['HCUSSUP,A,10']+row['HFYEAR,A,5']+row['HDBK,A,4']+'/'+row['HDOCNO,I,4']+'/'+tmp):
                        record['reconcile_id:id'] = dict_chisto['S'+row['HCUSSUP,A,10']+row['HFYEAR,A,5']+row['HDBK,A,4']+'/'+row['HDOCNO,I,4']+'/'+tmp]

            #if this move line is taxed
            if row['HVATCODE,A,10']:
                #create the base movement
                tvacount += 1
                tmp_cnt = 0
                for vat_code in _pick_vat_code(row,vat_dict,False):
                    tmp_cnt += 1
                    if tmp_cnt == 1:
                        record['tax_amount']= str(abs(float(row['HBASE,$,8'])) * _get_float(vat_code[0]))
                        record['tax_code_id:id'] = vat_code[1]
                    else:
                        writer.writerow(convert2utf(_create_vat_move_vat(row, vat_code, vat_dict, count,'HBASE,$,8')))

                #generate the vat movement
                vat_move_list = _create_vat_move(row, vat_dict, tvacount)
                for vat_move in vat_move_list:
                    writer.writerow(convert2utf(vat_move))

            writer.writerow(record)

    return True

# -=====================-
# -= 2. Importing DATA =-
# -=====================-

#specific part for CCI

reader_partner_matching = csv.DictReader(file('_conv_bob_id.csv','rb'))
bob_conv_matching = {}
bob_conv_matching[''] = ''
for row in reader_partner_matching:
    bob_conv_matching[row['bob']] = row['partner']
def _get_partner_id(char):
    if bob_conv_matching.has_key(char):
        return bob_conv_matching[char]
    return 'res_partner_destroyed'
partner_dict['GRAMME'] = ''

#end of specific part

langs = []
currencies = []

def run():
    reader_account = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/accoun.csv','rb'))
    writer_account = csv.DictWriter(file(config['addons_path']+'/account_bob_import/account.account.csv', 'wb'), account_map.keys())
    import_account(reader_account, writer_account, account_map)

    reader_journal = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/dbk.csv','rb'))
    writer_journal = csv.DictWriter(file(config['addons_path']+'/account_bob_import/account.journal.csv', 'wb'), journals_map.keys())
    import_journal(reader_journal, writer_journal, journals_map)

    reader_partner = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/compan.csv','rb'))
    writer_partner = csv.DictWriter(file(config['addons_path']+'/account_bob_import/res.partner.csv', 'wb'), partners_map.keys())
    writer_address = csv.DictWriter(file(config['addons_path']+'/account_bob_import/res.partner.address.csv','wb'), partner_add_map.keys())
    writer_bank = csv.DictWriter(file(config['addons_path']+'/account_bob_import/res.partner.bank.csv','wb'), partner_bank_map.keys())
    import_partner(reader_partner, writer_partner, partners_map, writer_address, partner_add_map, writer_bank, partner_bank_map)

    reader_contact = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/contacts.csv','rb'))
    writer_contact = csv.DictWriter(file(config['addons_path']+'/account_bob_import/res.partner.contact.csv','wb'),contacts_map.keys())
    writer_job = csv.DictWriter(file(config['addons_path']+'/account_bob_import/res.partner.job.csv','wb'),job_map.keys())
    import_contact(reader_contact, writer_contact, contacts_map, writer_job, job_map)

    reader_fyear = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/period.csv','rb'))
    writer_fyear = csv.DictWriter(file(config['addons_path']+'/account_bob_import/account.fiscalyear.csv', 'wb'), fyear_map.keys())
    import_fyear(reader_fyear, writer_fyear, fyear_map)

    reader_period = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/period.csv','rb'))
    writer_period = csv.DictWriter(file(config['addons_path']+'/account_bob_import/account.period.csv', 'wb'), periods_map.keys())
    import_period(reader_period, writer_period, periods_map)

    #import the account_tax from vat.csv
    #   constructing table account_tax => account_tax_code (for move and move_line)
    reader_vat_code = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/vatcas.csv','rb'))
    reader_vat = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/vat.csv','rb'))
    vat_dict = construct_vat_dict(reader_vat_code, reader_vat, {})

    reader_ahisto = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/ahisto_matchings.csv','rb'))
    writer_reconcile = csv.DictWriter(file(config['addons_path']+'/account_bob_import/account.move.reconcile-1.csv', 'wb'), arecon_map.keys())
    dict_ahisto = import_areconcile(reader_ahisto, writer_reconcile, arecon_map)

    reader_chisto = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/chisto_matchings.csv','rb'))
    writer_reconcile2 = csv.DictWriter(file(config['addons_path']+'/account_bob_import/account.move.reconcile-2.csv', 'wb'), crecon_map.keys())
    dict_chisto = import_creconcile(reader_chisto, writer_reconcile2, crecon_map)

    reader_move = csv.DictReader(file(config['addons_path']+'/account_bob_import/original_csv/ahisto.csv','rb'))
    writer_move = csv.DictWriter(file(config['addons_path']+'/account_bob_import/account.move.csv', 'wb'), move_map.keys())
    writer_move_line = csv.DictWriter(file(config['addons_path']+'/account_bob_import/account.move.line.csv', 'wb'), move_line_map.keys())
    import_moves_and_lines(reader_move, writer_move, writer_move_line, move_map, move_line_map, dict_ahisto, dict_chisto, vat_dict)

if __name__ == '__main__':
    run()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:


import mock
import uuid
from contextlib import contextmanager

import webtest
from pyramid.config import Configurator

from cliquet.events import (ResourceChanged, AfterResourceChanged,
                            ResourceRead, AfterResourceRead, ACTIONS)
from cliquet.storage.exceptions import BackendError
from cliquet.tests.testapp import main as testapp
from cliquet.tests.support import unittest, BaseWebTest, get_request_class
from cliquet import statsd

@contextmanager
def notif_broken(app, event_cls):
    old = app.registry.notify

    def buggy(event):
        if not isinstance(event, event_cls):
            return old(event)
        raise Exception("boom")

    app.registry.notify = buggy
    yield
    app.registry.notify = old

class BaseEventTest(BaseWebTest):

    subscribed = tuple()

    def setUp(self):
        super(BaseEventTest, self).setUp()
        self.events = []
        self.body = {'data': {'name': 'de Paris'}}

    def tearDown(self):
        self.events = []
        super(BaseEventTest, self).tearDown()

    def listener(self, event):
        self.events.append(event)

    def make_app(self, settings=None):
        settings = self.get_app_settings(settings)
        self.config = Configurator(settings=settings)
        for event_cls in self.subscribed:
            self.config.add_subscriber(self.listener, event_cls)
        self.config.commit()
        app = testapp(config=self.config)
        app = webtest.TestApp(app)
        app.RequestClass = get_request_class(self.api_prefix)
        return app

class ResourceReadTest(BaseEventTest, unittest.TestCase):

    subscribed = (ResourceRead,)

    def test_get_sends_read_event(self):
        resp = self.app.post_json(self.collection_url, self.body,
                                  headers=self.headers, status=201)
        record_id = resp.json['data']['id']
        record_url = self.get_item_url(record_id)
        self.app.get(record_url, headers=self.headers)
        self.assertEqual(len(self.events), 1)
        self.assertEqual(self.events[0].payload['action'], ACTIONS.READ.value)
        self.assertEqual(len(self.events[0].read_records), 1)

    def test_collection_get_sends_read_event(self):
        self.app.get(self.collection_url, headers=self.headers)
        self.assertEqual(len(self.events), 1)
        self.assertEqual(self.events[0].payload['action'], ACTIONS.READ.value)
        self.assertEqual(len(self.events[0].read_records), 0)

    def test_post_sends_read_if_id_already_exists(self):
        resp = self.app.post_json(self.collection_url, self.body,
                                  headers=self.headers, status=201)
        record = resp.json['data']
        body = dict(self.body)
        body['data']['id'] = record['id']

        # a second post with the same record id
        self.app.post_json(self.collection_url, body, headers=self.headers,
                           status=200)
        self.assertEqual(len(self.events), 1)
        self.assertEqual(self.events[0].payload['action'], ACTIONS.READ.value)

class ResourceChangedTest(BaseEventTest, unittest.TestCase):

    subscribed = (ResourceChanged,)

    def test_post_sends_create_action(self):
        self.app.post_json(self.collection_url, self.body,
                           headers=self.headers, status=201)
        self.assertEqual(len(self.events), 1)
        self.assertEqual(self.events[0].payload['action'],
                         ACTIONS.CREATE.value)

    def test_put_sends_create_action(self):
        body = dict(self.body)
        body['data']['id'] = record_id = str(uuid.uuid4())
        record_url = self.get_item_url(record_id)
        self.app.put_json(record_url, body,
                          headers=self.headers, status=201)
        self.assertEqual(len(self.events), 1)
        self.assertEqual(self.events[0].payload['action'],
                         ACTIONS.CREATE.value)

    def test_not_triggered_on_failed_put(self):
        record_id = str(uuid.uuid4())
        record_url = self.get_item_url(record_id)
        self.app.put_json(record_url, self.body, headers=self.headers)
        headers = self.headers.copy()
        headers['If-Match'] = '"12345"'
        self.app.put_json(record_url, self.body, headers=headers, status=412)
        self.assertEqual(len(self.events), 1)
        self.assertEqual(self.events[0].payload['action'],
                         ACTIONS.CREATE.value)

    def test_patch_sends_update_action(self):
        resp = self.app.post_json(self.collection_url, self.body,
                                  headers=self.headers, status=201)
        record = resp.json['data']
        record_url = self.get_item_url(record['id'])

        self.app.patch_json(record_url, self.body, headers=self.headers,
                            status=200)
        self.assertEqual(len(self.events), 2)
        self.assertEqual(self.events[0].payload['action'],
                         ACTIONS.CREATE.value)
        self.assertEqual(self.events[1].payload['action'],
                         ACTIONS.UPDATE.value)

    def test_put_sends_update_action_if_record_exists(self):
        body = dict(self.body)
        body['data']['id'] = record_id = str(uuid.uuid4())
        record_url = self.get_item_url(record_id)
        self.app.put_json(record_url, body,
                          headers=self.headers, status=201)

        body['data']['more'] = 'stuff'
        self.app.put_json(record_url, body,
                          headers=self.headers, status=200)

        self.assertEqual(len(self.events), 2)
        self.assertEqual(self.events[0].payload['action'],
                         ACTIONS.CREATE.value)
        self.assertEqual(self.events[1].payload['action'],
                         ACTIONS.UPDATE.value)

    def test_delete_sends_delete_action(self):
        resp = self.app.post_json(self.collection_url, self.body,
                                  headers=self.headers, status=201)
        record = resp.json['data']
        record_url = self.get_item_url(record['id'])

        self.app.delete(record_url, headers=self.headers, status=200)
        self.assertEqual(len(self.events), 2)
        self.assertEqual(self.events[0].payload['action'],
                         ACTIONS.CREATE.value)
        self.assertEqual(self.events[1].payload['action'],
                         ACTIONS.DELETE.value)

    def test_collection_delete_sends_delete_action(self):
        self.app.post_json(self.collection_url, self.body,
                           headers=self.headers, status=201)
        self.app.post_json(self.collection_url, self.body,
                           headers=self.headers, status=201)

        self.app.delete(self.collection_url, headers=self.headers, status=200)

        self.assertEqual(len(self.events), 3)
        self.assertEqual(self.events[0].payload['action'],
                         ACTIONS.CREATE.value)
        self.assertEqual(self.events[1].payload['action'],
                         ACTIONS.CREATE.value)
        self.assertEqual(self.events[2].payload['action'],
                         ACTIONS.DELETE.value)

    def test_request_fails_if_notify_fails(self):
        with notif_broken(self.app.app, ResourceChanged):
            self.app.post_json(self.collection_url, self.body,
                               headers=self.headers, status=500)
        self.assertEqual(len(self.events), 0)

    def test_triggered_on_protected_resource(self):
        app = self.make_app(settings={
            'psilo_write_principals': 'system.Authenticated'
        })
        app.post_json('/psilos', self.body,
                      headers=self.headers, status=201)
        self.assertEqual(len(self.events), 1)
        self.assertEqual(self.events[0].payload['action'],
                         ACTIONS.CREATE.value)

    def test_permissions_are_stripped_from_event_on_protected_resource(self):
        app = self.make_app(settings={
            'psilo_write_principals': 'system.Authenticated'
        })
        resp = app.post_json('/psilos', self.body,
                             headers=self.headers, status=201)
        record = resp.json['data']
        record_url = '/psilos/' + record['id']
        app.patch_json(record_url, {"data": {"name": "De barcelona"}},
                       headers=self.headers)
        impacted_records = self.events[-1].impacted_records
        self.assertNotIn('__permissions__', impacted_records[0]['new'])
        self.assertNotIn('__permissions__', impacted_records[0]['old'])

class AfterResourceChangedTest(BaseEventTest, unittest.TestCase):

    subscribed = (AfterResourceChanged,)

    def test_request_succeeds_if_notify_fails(self):
        with notif_broken(self.app.app, AfterResourceChanged):
            self.app.post_json(self.collection_url, self.body,
                               headers=self.headers)

        self.assertEqual(len(self.events), 0)

class AfterResourceReadTest(BaseEventTest, unittest.TestCase):

    subscribed = (AfterResourceRead,)

    def test_request_succeeds_if_notify_fails(self):
        with notif_broken(self.app.app, AfterResourceChanged):
            self.app.post_json(self.collection_url, self.body,
                               headers=self.headers)

        self.assertEqual(len(self.events), 0)

class ImpactedRecordsTest(BaseEventTest, unittest.TestCase):

    subscribed = (ResourceChanged,)

    def test_create_has_new_record_and_no_old_in_payload(self):
        resp = self.app.post_json(self.collection_url, self.body,
                                  headers=self.headers)
        record = resp.json['data']
        impacted_records = self.events[-1].impacted_records
        self.assertEqual(len(impacted_records), 1)
        self.assertNotIn('old', impacted_records[0])
        self.assertEqual(impacted_records[0]['new'], record)

    def test_collection_delete_has_old_record_and_no_new_in_payload(self):
        resp = self.app.post_json(self.collection_url, self.body,
                                  headers=self.headers)
        record1 = resp.json['data']
        resp = self.app.post_json(self.collection_url, self.body,
                                  headers=self.headers)
        record2 = resp.json['data']

        self.app.delete(self.collection_url, headers=self.headers, status=200)

        impacted_records = self.events[-1].impacted_records
        self.assertEqual(len(impacted_records), 2)
        self.assertNotIn('new', impacted_records[0])
        self.assertNotIn('new', impacted_records[1])
        self.assertEqual(impacted_records[0]['old']['deleted'], True)
        self.assertEqual(impacted_records[1]['old']['deleted'], True)
        deleted_ids = {impacted_records[0]['old']['id'],
                       impacted_records[1]['old']['id']}
        self.assertEqual(deleted_ids, {record1['id'], record2['id']})

    def test_update_has_old_and_new_record(self):
        resp = self.app.post_json(self.collection_url, self.body,
                                  headers=self.headers, status=201)
        record = resp.json['data']
        record_url = self.get_item_url(record['id'])
        self.app.patch_json(record_url, {'data': {'name': 'en boite'}},
                            headers=self.headers)
        impacted_records = self.events[-1].impacted_records
        self.assertEqual(len(impacted_records), 1)
        self.assertEqual(impacted_records[0]['new']['id'], record['id'])
        self.assertEqual(impacted_records[0]['new']['id'],
                         impacted_records[0]['old']['id'])
        self.assertEqual(impacted_records[0]['old']['name'], 'de Paris')
        self.assertEqual(impacted_records[0]['new']['name'], 'en boite')

    def test_delete_has_old_record_and_no_new_in_payload(self):
        resp = self.app.post_json(self.collection_url, self.body,
                                  headers=self.headers, status=201)
        record = resp.json['data']
        record_url = self.get_item_url(record['id'])
        self.app.delete(record_url, headers=self.headers, status=200)

        impacted_records = self.events[-1].impacted_records
        self.assertEqual(len(impacted_records), 1)
        self.assertNotIn('new', impacted_records[0])
        self.assertEqual(impacted_records[0]['old']['id'], record['id'])
        self.assertEqual(impacted_records[0]['old']['deleted'], True)

class BatchEventsTest(BaseEventTest, unittest.TestCase):

    subscribed = (ResourceChanged, ResourceRead)

    def test_impacted_records_are_merged(self):
        record_id = str(uuid.uuid4())
        record_url = self.get_item_url(record_id)
        body = {
            "defaults": {
                "method": "PUT",
                "path": record_url
            },
            "requests": [
                {"body": {'data': {'name': 'foo'}}},
                {"body": {'data': {'name': 'bar'}}},
                {"body": {'data': {'name': 'baz'}}},
                {"method": "DELETE"}
            ]
        }
        self.app.post_json("/batch", body, headers=self.headers)
        self.assertEqual(len(self.events), 3)

        create_event = self.events[0]
        self.assertEqual(create_event.payload['action'], 'create')
        self.assertEqual(len(create_event.impacted_records), 1)
        self.assertNotIn('old', create_event.impacted_records[0])
        update_event = self.events[1]
        self.assertEqual(update_event.payload['action'], 'update')
        impacted = update_event.impacted_records
        self.assertEqual(len(impacted), 2)
        self.assertEqual(impacted[0]['old']['name'], 'foo')
        self.assertEqual(impacted[0]['new']['name'], 'bar')
        self.assertEqual(impacted[1]['old']['name'], 'bar')
        self.assertEqual(impacted[1]['new']['name'], 'baz')
        delete_event = self.events[2]
        self.assertEqual(delete_event.payload['action'], 'delete')
        self.assertEqual(len(delete_event.impacted_records), 1)
        self.assertNotIn('new', delete_event.impacted_records[0])

    def test_one_event_is_sent_per_resource(self):
        body = {
            "defaults": {
                "method": "POST",
                "body": self.body,
            },
            "requests": [
                {"path": '/mushrooms'},
                {"path": '/mushrooms'},
                {"path": '/psilos'},
            ]
        }
        self.app.post_json("/batch", body, headers=self.headers)
        self.assertEqual(len(self.events), 2)

    def test_one_event_is_sent_per_action(self):
        body = {
            "defaults": {
                "path": '/mushrooms',
            },
            "requests": [
                {"method": "POST", "body": self.body},
                {"method": "DELETE"},
                {"method": "GET"},
            ]
        }
        self.app.post_json("/batch", body, headers=self.headers)
        self.assertEqual(len(self.events), 3)

    def test_events_are_not_sent_if_subrequest_fails(self):
        patch = mock.patch.object(self.storage,
                                  'delete_all',
                                  side_effect=BackendError('boom'))
        patch.start()
        self.addCleanup(patch.stop)
        request_create = {
            "method": "POST",
            "body": self.body,
        }
        request_delete_all = {
            "method": "DELETE",
            "body": self.body,
        }
        body = {
            "defaults": {
                "path": self.collection_url
            },
            "requests": [request_create, request_delete_all]
        }
        self.app.post_json("/batch", body, headers=self.headers,
                           status=503)
        self.assertEqual(len(self.events), 0)

def load_from_config(config, prefix):
    class ClassListener(object):
        def __call__(self, event):
            pass
    return ClassListener()

@unittest.skipIf(not statsd.statsd_module, "statsd is not installed.")
class StatsDTest(BaseWebTest, unittest.TestCase):
    def get_app_settings(self, *args, **kwargs):
        settings = super(StatsDTest, self).get_app_settings(*args, **kwargs)
        if not statsd.statsd_module:
            return settings

        settings['statsd_url'] = 'udp://localhost:8125'
        this_module = 'cliquet.tests.resource.test_events'
        settings['event_listeners'] = 'test'
        settings['event_listeners.test.use'] = this_module
        return settings

    def test_statds_tracks_listeners_execution_duration(self):
        statsd_client = self.app.app.registry.statsd._client
        with mock.patch.object(statsd_client, 'timing') as mocked:
            self.app.post_json(self.collection_url,
                               {"data": {"name": "pouet"}},
                               headers=self.headers)
            timers = set(c[0][0] for c in mocked.call_args_list)
            self.assertIn('listeners.test', timers)


#!/usr/local/bin/python3.7
import cv2
import numpy as np

img = cv2.imread("../test_pic.jpg")
kernel = np.ones((2, 2), np.uint8)

imgCanny = cv2.Canny(img, 100, 200)
imgErosion = cv2.erode(imgCanny, kernel, iterations=1)

cv2.imshow("Edge detection", imgCanny)
cv2.imshow("Erosion Fix", imgErosion)
cv2.waitKey(0)


import sys
import collections
#sys.setrecursionlimit(100001)

#  bfs로 동작하는 코드: 정확성은 맞지만 시간초과
def bfs_find_root():
    while queue:
        root = queue.popleft()
        for child in tree[root]:
            if not visited[child]:
                output[child] = root
                queue.append(child)
                visited[child] = True

'''
#  dfs로 동작하는 코드
def dfs_find_root(root):
    for child in tree[root]:
        if output[child] == 0:
            output[child] = root
            dfs_find_root(child)
'''
n = int(sys.stdin.readline())
tree = [[] for _ in range(n+1)]
output = [0 for _ in range(n+1)]
visited = [False for i in range(n+1)]
queue = collections.deque()
queue.append(1)
output[1] = 1
for _ in range(n-1):
    a, b = map(int, sys.stdin.readline().split())
    tree[a].append(b)
    tree[b].append(a)
bfs_find_root()
#dfs_find_root(1)
for i in range(2, n+1):
    print(output[i])

'''
        1
    4       6
  2   7   1x    3
4x    4x       x6   5

2번 노드부터 부모 노드 순서
4 6 1 3 1 4

import sys
sys.setrecursionlimit(100000)
n = int(input())
tree = [[] for _ in range(n+1)]
for _ in range(n-1):
    a, b = map(int, input().split())
    tree[a].append(b)
    tree[b].append(a)
parents = [0 for _ in range(n+1)]
parents[1] =1

def dfs(curr, tree, parents):
    for node in tree[curr]:
        if parents[node] == 0:
            parents[node] = curr
            dfs(node, tree, parents)
dfs(1, tree, parents)
for i in range(2, n+1):
    print(parents[i])
'''


import sys
sys.path.append("../SimpleNN")
"""SingleNN potential."""
from fp_calculator import set_sym, calculate_fp
from NN import MultiLayerNet

import torch
from torch.autograd import grad
from Batch import batch_pad
import time
import numpy as np

import pickle
from ase.data import chemical_symbols, atomic_numbers
from ase.calculators.calculator import (Calculator, all_changes,
                                        PropertyNotImplementedError)
import os

class SingleNN(Calculator):

    implemented_properties = ['energy', 'energies', 'forces']

    def __init__(self, model_path,cal_list = None,**kwargs):
        Calculator.__init__(self, **kwargs)
        self.model_path = model_path
        self.cal_list = cal_list

    def initialize(self, atoms):
        self.numbers = atoms.get_atomic_numbers()
        self.energies = np.empty(len(atoms))
        self.forces = np.empty((len(atoms), 3))

    def calculate(self, atoms=None, properties=['energy'],system_changes=all_changes):
        cal_list = self.cal_list
        if os.path.exists(self.model_path+'/best_model'):
            model = torch.load(self.model_path+'/best_model')
            ensemble_training = False
        else:
            ensemble_training = True
            models = []
            ensemble = 0
            end = False
            while end is False:
                if os.path.exists(self.model_path+f'/best_model-{ensemble}'):
                    models += [torch.load(self.model_path+f'/best_model-{ensemble}')]
                    ensemble += 1
                else:
                    end = True

        sym_params = pickle.load(open(self.model_path+"/sym_params.sav", "rb" ))
        [Gs, cutoff, g2_etas, g2_Rses, g4_etas, g4_zetas, g4_lambdas, elements, weights, element_energy]=sym_params
        params_set = set_sym(elements, Gs, cutoff,
            g2_etas=g2_etas, g2_Rses=g2_Rses,
            g4_etas=g4_etas, g4_zetas = g4_zetas,
            g4_lambdas= g4_lambdas, weights=weights)
        if cal_list is None:
            N_atoms = len(atoms)
        else:
            N_atoms = len(cal_list)
        nelem = len(elements)
        N_sym = params_set[elements[0]]['num']
        data = calculate_fp(atoms, elements, params_set, cal_list = cal_list)
        fps = data['x']
        dfpdXs = data['dx']

        fp = torch.zeros((N_atoms,N_sym))
        dfpdX = torch.zeros((N_atoms, N_sym, N_atoms, 3))
        elements_num = torch.tensor([atomic_numbers[ele] for ele in elements])
        atom_idx = data['atom_idx'] - 1

        a_num = elements_num[atom_idx]
        atom_numbers = a_num.repeat_interleave(nelem).view(len(a_num),nelem)
        # change to float for pytorch to be able to run without error
        if cal_list is not None:
            e_mask = (atom_numbers == elements_num).float()[cal_list]
            atom_idx = atom_idx[cal_list]
        else:
            e_mask = (atom_numbers == elements_num).float()
        fp_track = [0]*nelem
        if element_energy is not None:
            element_energy = torch.sum(element_energy * e_mask)

        for i,idx in enumerate(atom_idx):
            ele = elements[idx]
            fp[i,:] = torch.tensor(fps[ele][fp_track[idx],:]).float()
            if cal_list is None:
                dfpdX[i,:,:,:] = torch.tensor(dfpdXs[ele][fp_track[idx],:,:,:]).float()
            fp_track[idx] += 1
        fp.requires_grad = True

        if ensemble_training:
            scaling = models[0].scaling
        else:
            scaling = model.scaling

        gmin = scaling['gmin']
        gmax = scaling['gmax']
        emin = scaling['emin']
        emax = scaling['emax']
        eps = 1e-5
        sfp = (fp - gmin) / (gmax - gmin+eps)

        if ensemble_training:
            all_energy = []
            all_forces = []
            for model in models:
                Atomic_Es = model(sfp)
                E_predict = torch.sum(torch.sum(Atomic_Es * e_mask,
                    dim = 1)*(emax-emin)+emin,dim=0)
                dEdfp = grad(E_predict,
                    fp,
                    grad_outputs=torch.ones_like(E_predict),
                    create_graph = True,
                    retain_graph = True)[0].view(1,fp.shape[0]*fp.shape[1])
                dfpdX = dfpdX.view(fp.shape[0]*fp.shape[1],fp.shape[0]*3)
                F_predict = -torch.mm(dEdfp,dfpdX).view(fp.shape[0],3)
                forces = F_predict.data.numpy()
                if element_energy is not None:
                    energy = (E_predict + element_energy).data.numpy()
                else:
                    energy = E_predict.data.numpy()
                all_energy += [energy]
                all_forces += [forces]
            all_energy = np.array(all_energy)
            all_forces = np.array(all_forces)
            ensemble_energy = np.mean(all_energy)
            energy_std = np.std(all_energy)
            ensemble_forces = np.mean(all_forces, axis=0)
            forces_std = np.std(all_forces, axis=0)
            self.energy = ensemble_energy
            self.forces = ensemble_forces
            self.results['energy'] = self.energy
            self.results['free_energy'] = self.energy
            self.results['forces'] = self.forces
            self.results['energy_std'] = energy_std
            self.results['forces_std'] = forces_std
        else:
            Atomic_Es = model(sfp)
            E_predict = torch.sum(torch.sum(Atomic_Es * e_mask,
                dim = 1)*(emax-emin)+emin,dim=0)
            dEdfp = grad(E_predict,
                fp,
                grad_outputs=torch.ones_like(E_predict),
                create_graph = True,
                retain_graph = True)[0].view(1,fp.shape[0]*fp.shape[1])
            dfpdX = dfpdX.view(fp.shape[0]*fp.shape[1],fp.shape[0]*3)
            F_predict = -torch.mm(dEdfp,dfpdX).view(fp.shape[0],3)
            self.forces = F_predict.data.numpy()
            if element_energy is not None:
                self.energy = (E_predict + element_energy).data.numpy()
            else:
                self.energy = E_predict.data.numpy()
            self.results['energy'] = self.energy
            self.results['free_energy'] = self.energy
            self.results['forces'] = self.forces

class SingleNNTrainer(object):
    def __init__(self, model_path, scaling, N_sym, n_nodes, activations, nelem, optim_params, device=torch.device('cpu')):

        self.model = MultiLayerNet(N_sym, n_nodes, activations, nelem, scaling=scaling)
        self.model_path = model_path
        self.opt_method = optim_params['opt_method']
        if self.opt_method == 'lbfgs':
            self.history_size = optim_params['history_size']
            self.lr = optim_params['lr']
            self.max_iter = optim_params['max_iter']
            self.line_search_fn = optim_params['line_search_fn']
            self.optimizer = torch.optim.LBFGS(self.model.parameters(), lr=self.lr,
                max_iter=self.max_iter, history_size=self.history_size,
                line_search_fn=self.line_search_fn)
        else:
            print('Optimization method not implemented!')
            raise
        self.device = device
        # self.convergence = hyperparams['convergence']
        # self.n_nodes = n_nodes
        # self.activations = hyperparams['activations']

    def train(self, train_dict, val_dict, E_coeff, F_coeff, epoch, val_interval, n_val_stop, convergence, is_force, logfile):
        device = self.device
        opt_method = self.opt_method
        optimizer = self.optimizer
        model = self.model.to(device)
        model_path = self.model_path

        t0 = time.time()
        SSE = torch.nn.MSELoss(reduction='sum')
        SAE = torch.nn.L1Loss(reduction='sum')
        scaling = self.model.scaling
        gmin = scaling['gmin'].to(device)
        gmax = scaling['gmax'].to(device)
        emin = scaling['emin'].to(device)
        emax = scaling['emax'].to(device)
        n_val = 0
        E_cov = convergence['E_cov']
        F_cov = convergence['F_cov']
        t_ids = np.array(list(train_dict.keys()))
        batch_info = batch_pad(train_dict,t_ids)
        b_fp = batch_info['b_fp'].to(device)

        if is_force:
            b_dfpdX = batch_info['b_dfpdX'].view(b_fp.shape[0],
                                                 b_fp.shape[1]*b_fp.shape[2],
                                                 b_fp.shape[1]*3,
                                                 ).to(device)
        b_e_mask = batch_info['b_e_mask'].to(device)
        b_fp.requires_grad = True
        eps = 1e-5
        sb_fp = ((b_fp - gmin) / (gmax - gmin + eps))
        N_atoms = batch_info['N_atoms'].view(-1).to(device)
        b_e = batch_info['b_e'].view(-1).to(device)
        b_f = batch_info['b_f'].to(device)

        sb_e = ((b_e - emin) / (emax - emin))
        sb_f = (b_f / (emax - emin))
        t1 = time.time()
        logfile.write(f'Batching takes {t1-t0}.\n')

        v_ids = np.array(list(val_dict.keys()))
        v_batch_info = batch_pad(val_dict,v_ids)
        v_b_fp = v_batch_info['b_fp'].to(device)
        if is_force:
            v_b_dfpdX = v_batch_info['b_dfpdX'].view(v_b_fp.shape[0],
                                                     v_b_fp.shape[1]*v_b_fp.shape[2],
                                                     v_b_fp.shape[1]*3,
                                                     ).to(device)
        v_b_e_mask = v_batch_info['b_e_mask'].to(device)
        v_b_fp.requires_grad = True
        v_sb_fp = ((v_b_fp - gmin) / (gmax - gmin + eps))
        v_N_atoms = v_batch_info['N_atoms'].view(-1).to(device)
        v_b_e = v_batch_info['b_e'].view(-1).to(device)

        v_b_f = v_batch_info['b_f'].to(device)

        v_sb_e = ((v_b_e - emin) / (emax - emin))
        v_sb_f = (v_b_f / (emax - emin))

        if opt_method == 'lbfgs':
            for i in range(epoch):
                def closure():
                    global E_MAE, F_MAE
                    optimizer.zero_grad()
                    Atomic_Es = model(sb_fp)
                    E_predict = torch.sum(Atomic_Es * b_e_mask, dim = [1,2])
                    if is_force:
                        F_predict = self.get_forces(E_predict, b_fp, b_dfpdX)
                        metrics = self.get_metrics(sb_e, sb_f, N_atoms, t_ids,
                                               E_predict, F_predict, SSE, SAE, scaling, b_e_mask)
                        [E_loss, F_loss, E_MAE, F_MAE, E_RMSE, F_RMSE] = metrics
                        loss = E_coeff * E_loss + F_coeff * F_loss
                    else:
                        metrics =  self.get_metrics(sb_e, None, N_atoms, t_ids,
                                               E_predict, None, SSE, SAE, scaling, b_e_mask)
                        [E_loss, F_loss, E_MAE, F_MAE, E_RMSE, F_RMSE] = metrics
                        loss = E_coeff * E_loss

                    loss.backward(retain_graph=True)
                    return loss

                optimizer.step(closure)

                if i % val_interval == 0:
                    n_val += 1
                    Atomic_Es = model(sb_fp)
                    E_predict = torch.sum(Atomic_Es * b_e_mask, dim = [1,2])
                    if is_force:
                        F_predict = self.get_forces(E_predict, b_fp, b_dfpdX)
                        metrics =  self.get_metrics(sb_e, sb_f, N_atoms, t_ids,
                                               E_predict, F_predict, SSE, SAE, scaling, b_e_mask)
                        [E_loss, F_loss, E_MAE, F_MAE, E_RMSE, F_RMSE] = metrics
                        loss = E_coeff * E_loss + F_coeff * F_loss
                    else:
                        metrics = self.get_metrics(sb_e, None, N_atoms, t_ids,
                                               E_predict, None, SSE, SAE, scaling, b_e_mask)
                        [E_loss, F_loss, E_MAE, F_MAE, E_RMSE, F_RMSE] = metrics
                        loss = E_coeff * E_loss
                    logfile.write(f'{i}, E_RMSE/atom = {E_RMSE}, F_RMSE = {F_RMSE}, loss={loss}\n')
                    logfile.write(f'{i}, E_MAE/atom = {E_MAE}, F_MAE = {F_MAE}\n')

                    v_Atomic_Es = model(v_sb_fp)
                    v_E_predict = torch.sum(v_Atomic_Es * v_b_e_mask, dim = [1,2])
                    if is_force:
                        v_F_predict = self.get_forces(v_E_predict, v_b_fp, v_b_dfpdX)
                        v_metrics =  self.get_metrics(v_sb_e, v_sb_f, v_N_atoms, v_ids,
                                               v_E_predict, v_F_predict, SSE, SAE, scaling, v_b_e_mask)
                        [v_E_loss, v_F_loss, v_E_MAE, v_F_MAE, v_E_RMSE, v_F_RMSE] = v_metrics
                        v_loss = E_coeff * v_E_loss + F_coeff * v_F_loss
                    else:
                        v_metrics =  self.get_metrics(v_sb_e, None, v_N_atoms, v_ids,
                                                 v_E_predict, None, SSE, SAE, scaling, v_b_e_mask)
                        [v_E_loss, v_F_loss, v_E_MAE, v_F_MAE, v_E_RMSE, v_F_RMSE] = v_metrics
                        v_loss = E_coeff * v_E_loss

                    try:
                        if v_loss < best_v_loss:
                            best_loss = loss
                            best_E_MAE = E_MAE
                            best_F_MAE = F_MAE
                            best_v_loss = v_loss
                            best_v_E_MAE = v_E_MAE
                            best_v_F_MAE = v_F_MAE
                            torch.save(model,model_path)
                            n_val = 1
                    except NameError:
                        best_loss = loss
                        best_E_MAE = E_MAE
                        best_F_MAE = F_MAE
                        best_v_loss = v_loss
                        best_v_E_MAE = v_E_MAE
                        best_v_F_MAE = v_F_MAE
                        torch.save(model,model_path)
                        n_val = 1

                    logfile.write(f'val, E_RMSE/atom = {v_E_RMSE}, F_RMSE = {v_F_RMSE}\n')
                    logfile.write(f'val, E_MAE/atom = {v_E_MAE}, F_MAE = {v_F_MAE}\n')
                    logfile.flush()
                    if n_val > n_val_stop:
                        break

        t2 = time.time()
        logfile.write(f'Training takes {t2-t0}\n')
        logfile.close()
        return [best_loss, best_E_MAE, best_F_MAE, best_v_loss, best_v_E_MAE, best_v_F_MAE]

    def get_forces(self, E_predict, b_fp, b_dfpdX):
        b_dEdfp = grad(E_predict,
                        b_fp,
                        grad_outputs=torch.ones_like(E_predict),
                        create_graph = True,
                        retain_graph = True)[0].view(b_fp.shape[0],1,b_fp.shape[1]*b_fp.shape[2])
        F_predict = - torch.bmm(b_dEdfp,b_dfpdX).view(b_fp.shape[0],b_fp.shape[1],3)
        return F_predict

    def get_metrics(self, sb_e, sb_f, N_atoms, ids, E_predict, F_predict, SSE, SAE, scaling, b_e_mask):

        gmin = scaling['gmin']
        gmax = scaling['gmax']
        emin = scaling['emin']
        emax = scaling['emax']

        E_loss = SSE(sb_e, E_predict / N_atoms) / len(ids)
        E_MAE = SAE(sb_e, E_predict / N_atoms) / len(ids) * (emax - emin)
        E_RMSE = torch.sqrt(E_loss) * (emax - emin)
        if sb_f is None:
            F_loss = 0
            F_MAE = 0
            F_RMSE = 0
        else:
            F_loss = SSE(sb_f, F_predict) / (3 * torch.sum(N_atoms))
            F_MAE = SAE(sb_f, F_predict) / (3 * torch.sum(N_atoms)) * (emax - emin)
            F_RMSE = torch.sqrt(F_loss) * (emax - emin)
            F_max = torch.max(torch.abs(sb_f-F_predict))*(emax-emin)
            print('F_max = ',F_max.data.numpy(), 'eV/A')
        return [E_loss, F_loss, E_MAE, F_MAE, E_RMSE, F_RMSE]

    def evaluate(self, data_dict, E_coeff, F_coeff, is_force):
        device = self.device
        for key in data_dict.keys():
            data_dict[key] = data_dict[key].to(device)

        model_path = self.model_path
        SSE = torch.nn.MSELoss(reduction='sum')
        SAE = torch.nn.L1Loss(reduction='sum')
        model = torch.load(model_path)
        scaling = model.scaling
        gmin = scaling['gmin']
        gmax = scaling['gmax']
        emin = scaling['emin']
        emax = scaling['emax']

        ids = np.array(list(data_dict.keys()))
        batch_info = batch_pad(data_dict,ids)
        b_fp = batch_info['b_fp']

        if is_force:
            b_dfpdX = batch_info['b_dfpdX'].view(b_fp.shape[0],
                                                 b_fp.shape[1]*b_fp.shape[2],
                                                 b_fp.shape[1]*3)
        b_e_mask = batch_info['b_e_mask']
        b_fp.requires_grad = True
        sb_fp = (b_fp - gmin) / (gmax - gmin)
        N_atoms = batch_info['N_atoms'].view(-1)
        b_e = batch_info['b_e'].view(-1)
        b_f = batch_info['b_f']

        sb_e = (b_e - emin) / (emax - emin)
        sb_f = b_f / (emax - emin)

        Atomic_Es = model(sb_fp)
        E_predict = torch.sum(Atomic_Es * b_e_mask, dim = [1,2])
        if is_force:
            F_predict = get_forces(E_predict, b_fp, b_dfpdX)
            metrics =  get_metrics(sb_e, sb_f, N_atoms, ids,
                                   E_predict, F_predict, SSE, SAE, scaling, b_e_mask)
            [E_loss, F_loss, E_MAE, F_MAE, E_RMSE, F_RMSE] = metrics
            loss = E_coeff * E_loss + F_coeff * F_loss
        else:
            metrics =  get_metrics(sb_e, None, N_atoms, ids,
                                   E_predict, None, SSE, SAE, scaling, b_e_mask)
            [E_loss, F_loss, E_MAE, F_MAE, E_RMSE, F_RMSE] = metrics
            loss = E_coeff * E_loss
        return [loss, E_MAE, F_MAE]


"""
    This script has the information for the initialization of the model.
-------------------------------------------------------------------------------
created on:
    Thu 3 Mar 2022
-------------------------------------------------------------------------------
last change:
    Wed 18 May 2022
-------------------------------------------------------------------------------
notes:
-------------------------------------------------------------------------------
contributors:
    Jose:
        name:       Jose Betancourt
        email:      jose.betancourtvalencia@yale.edu
-------------------------------------------------------------------------------
"""
import Params
import Agents
import Groups
import Network
import TimeIteration
import numpy as np

#------------------------------------------------------------------------------
# AUXILIARY FUNCTIONS
#------------------------------------------------------------------------------
def generate_attributes():
    '''
    This function creates agent attributes according to the specified
    distributions.
    '''
    opinion = np.random.beta(Params.sigma, Params.sigma)
    return opinion

#------------------------------------------------------------------------------
# GENERATION
#------------------------------------------------------------------------------
class Population(object):
    def __init__(self, agents=None, network=None):
        '''
        This function initializes the population object.

        '''
        # Create agent list
        agents = []
        Na = Params.N_agents
        for i in range(Na):
            opinion = generate_attributes()
            agent = Agents.Agent(ident=i, opinion=opinion)
            agents.append(agent)
        self.agents = agents
        # Generate group information tensor
        groups = []
        Nl = Params.N_layers
        for l in range(Nl):
            Ng = int(Na/Params.avg_members[l])
            layer = []
            for g in range(Ng):
                group = Groups.Group(layer=l, ident=g)
                layer.append(group)
            groups.append(layer)
        self.groups = groups
        # Assign agents to groups
        for agent in agents:
            agent.groups = []
            for l in range(Nl):
                Ng = int(Na/Params.avg_members[l])
                g = np.random.choice(np.arange(Ng))
                agent.groups.append(groups[l][g])
                groups[l][g].members.append(agent)
        networks = []
        for l in range(Nl):
            layer_net = np.zeros([Na, Na], dtype=float)
            for group in groups[l]:
                for agent_i in group.members:
                    for agent_j in group.members:
                            # Family ties
                            if l == 0:
                                layer_net[agent_i.ident, agent_j.ident] = Params.init_weight[l]
                            # Work ties
                            elif l == 1:
                                layer_net[agent_i.ident, agent_j.ident] = \
                                   np.random.choice([0.,Params.init_weight[l]],p=[1-Params.p_link[l], Params.p_link[l]])
                            # Friend ties
                            else:
                                if np.random.rand() < Params.p_main_gang:
                                    layer_net[agent_i.ident, agent_j.ident] = \
                                       np.random.choice([0.,Params.init_weight[l]],p=[1-Params.p_link[l], Params.p_link[l]])
                                elif agent_i.ident < agent_j.ident:
                                    others = [k for k in agents if k.groups[l]!=group]
                                    new_friend = np.random.choice(others)
                                    layer_net[agent_i.ident, new_friend.ident] = Params.init_weight[l]
            networks.append(layer_net)
       # Generate network object
        network = Network.Network(np.array(networks))
        self.network = network

#------------------------------------------------------------------------------
# SIMULATION
#------------------------------------------------------------------------------
def run_simulation(ind, agents, network, record):
    '''
    This function runs the simulation with the initialized agents.
    '''
    my_simulation = TimeIteration.Simulation()
    while my_simulation.time < Params.T:
        my_simulation.iterate(ind, agents, network, record)


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

"""

animation of global earthquakes locatiopns from 2000-2019
plotted annually

"""
import numpy as np
import matplotlib as plt
from mlp_toolkit.basemat import Basemap as Basemap

#===================================================
#                   files and parameters
#===================================================
file_eq = 'globalEqs.txt'

#===================================================
#                   load data
#===================================================
#
aYr    = np.genfromtxt( file_eq, skip_header = 1,
                       usecols = (0), delimiter = '-', dtype = int)
print (np.unique(aYr))

mLoc   = np.genfromtxt( file_eq, skip_header = 1,
                       usecols = (2,1), delimiter = '-', dtype = float).T

#===================================================
#                   plot eq map using basemap
#===================================================
for it in np.unique(aYr):
    sel_eq = it == aYr
    print( ' no of eq in %i: %i' %(it, sel_eq.sum()))

    plt.figure(1)
    plt.title(str(it))

    m = Basemap()
    m.drawcoastlines()

    a_x, a_y = m(mLoc[0], mLoc[1])

    plt.plot (a_x, a_y, 'ro', ms = 5, mew = 1.5, mfc = 'none')

    plt.pause(.5)
    #plt.clf()

Astro119/In Class/Wk 3/global earthquakes.py


# -*- coding: utf-8 -*-
"""
Created on Mon Oct  3 17:53:18 2022

@author: yiann
"""

import pandas  as pd
import pytz

# eisagwgh tou arxeiou ston kwdika
data_file='Solar_1min_2021.txt'
df=pd.read_csv(data_file, index_col=[0], usecols=[0,6], sep=',', header=None,
    parse_dates=True, na_values='"NAN"')
df.columns=['GH']
df.dropna() # Deletes missing values
df = df * 1000 / 8.63 # Converts mV to W/m^2

# dokimazw timezones apo to pytz
grc = pytz.timezone('Europe/Athens') # includes DST, den mou kanei
grc_w = pytz.timezone('Etc/GMT-2') # auto pou psaxnw???
utc=pytz.timezone('UCT')

df.index = df.index.tz_localize(grc_w).tz_convert(utc)

''' ------------------------ TA KATAFERAAAAAAAA? ---------------------------'''