import logging
import random

import numpy as np
import pypinyin
import tensorflow as tf

from augmentations.augments import Augmentation
from utils.speech_featurizers import SpeechFeaturizer
from utils.text_featurizers import TextFeaturizer

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
import time

class AM_DataLoader():

    def __init__(self, config_dict, training=True):

        self.speech_config = config_dict['speech_config']
        self.phone_config = config_dict['inp_config']
        self.text_config = config_dict['tar_config']
        self.running_config=config_dict['running_config']
        self.augment_config = config_dict['augments_config']
        self.streaming = self.speech_config['streaming']
        self.chunk = self.speech_config['sample_rate'] * self.speech_config['streaming_bucket']
        self.batch = config_dict['running_config']['batch_size']
        self.speech_featurizer = SpeechFeaturizer(self.speech_config)
        self.phone_featurizer = TextFeaturizer(self.phone_config)
        self.text_featurizer = TextFeaturizer(self.text_config)
        self.make_file_list( training)
        self.augment = Augmentation(self.augment_config)
        self.init_text_to_vocab()
        self.epochs = 1
        self.steps = 0


    def return_data_types(self):

        return (tf.float32, tf.int32, tf.int32, tf.int32,tf.int32)

    def return_data_shape(self):

        return (
            tf.TensorShape([self.batch, None, 1]),

            tf.TensorShape([self.batch, ]),
            tf.TensorShape([self.batch, None]),
            tf.TensorShape([self.batch, ]),
            tf.TensorShape([self.batch, None]),
        )

    def get_per_epoch_steps(self):
        return len(self.train_list) // self.batch

    def eval_per_epoch_steps(self):
        return len(self.test_list) // self.batch

    def init_text_to_vocab(self):
        pypinyin.load_phrases_dict({'调大': [['tiáo'], ['dà']],
                                    '调小': [['tiáo'], ['xiǎo']],
                                    '调亮': [['tiáo'], ['liàng']],
                                    '调暗': [['tiáo'], ['àn']],
                                    '肖': [['xiāo']],
                                    '英雄传': [['yīng'], ['xióng'], ['zhuàn']],
                                    '新传': [['xīn'], ['zhuàn']],
                                    '外传': [['wài'], ['zhuàn']],
                                    '正传': [['zhèng'], ['zhuàn']], '水浒传': [['shuǐ'], ['hǔ'], ['zhuàn']]
                                    })

        def text_to_vocab_func(txt):
            pins = pypinyin.pinyin(txt)
            pins = [i[0] for i in pins]
            phones = []
            for pin in pins:
                if pin in self.phone_featurizer.vocab_array:
                    phones += [pin]
                else:
                    phones += list(pin)
            # print(phones)
            return phones

        self.text_to_vocab = text_to_vocab_func

    def make_file_list(self, training=True):
        train_list=self.speech_config['train_list']
        test_list=self.speech_config['eval_list']
        if training:
            with open(train_list, encoding='utf-8') as f:
                train_list = f.readlines()
            train_list = [i.strip() for i in train_list if i != '']

            self.train_list = train_list

            np.random.shuffle(self.train_list)
            with open(test_list, encoding='utf-8') as f:
                data = f.readlines()
            data = [i.strip() for i in data if i != '']
            self.test_list = data
            self.train_offset = 0
            self.test_offset = 0
            logging.info('load train list {} test list {}'.format(len(self.train_list), len(self.test_list)))
        else:
            with open(test_list, encoding='utf-8') as f:
                data = f.readlines()
            data = [i.strip() for i in data if i != '']
            self.test_list = data
            self.test_offset = 0

    def only_chinese(self, word):
        txt = ''
        for ch in word:
            if '\u4e00' <= ch <= '\u9fff':
                txt += ch
            else:
                continue

        return txt

    def eval_data_generator(self):

        sample = []
        speech_features = []
        input_length = []
        phones = []
        phones_length = []
        txts = []
        max_input = 0
        batch = self.batch

        for i in range(batch * 10):

            line = self.test_list[self.test_offset]
            self.test_offset += 1
            if self.test_offset > len(self.test_list) - 1:
                self.test_offset = 0
            wp, txt = line.strip().split('\t')
            try:
                data = self.speech_featurizer.load_wav(wp)
            except:
                logging.info('{} load data failed,skip'.format(wp))
                continue
            if len(data) < 400:
                continue
            elif len(data) > self.speech_featurizer.sample_rate * self.speech_config['wav_max_duration']:
                logging.info(
                    '{} duration out of wav_max_duration({}),skip'.format(wp, self.speech_config['wav_max_duration']))
                continue
            if self.speech_config['only_chinese']:
                txt = self.only_chinese(txt)

            if not self.streaming:
                speech_feature = data / np.abs(data).max()
                speech_feature = np.expand_dims(speech_feature, -1)
                in_len = len(speech_feature) // (
                        self.speech_config['reduction_factor'] * (self.speech_featurizer.sample_rate / 1000) *
                        self.speech_config['stride_ms'])
            else:
                speech_feature = data
                speech_feature = np.expand_dims(speech_feature, -1)
                reduce = self.speech_config['reduction_factor'] * (self.speech_featurizer.sample_rate / 1000) * \
                         self.speech_config['stride_ms']
                in_len = len(speech_feature) // self.chunk
                if len(speech_feature) % self.chunk != 0:
                    in_len += 1
                chunk_times = self.chunk // reduce
                if self.chunk % reduce != 0:
                    chunk_times += 1
                in_len *= chunk_times

            py = self.text_to_vocab(txt)
            if self.check_valid(py, self.phone_featurizer.vocab_array) is not True:
                logging.info(' {} txt phone {} not all in tokens,continue'.format(txt, self.check_valid(py,
                                                                                                        self.phone_featurizer.vocab_array)))
                continue
            if self.check_valid(txt, self.text_featurizer.vocab_array) is not True:
                logging.info(' {} txt phone {} not all in tokens,continue'.format(txt, self.check_valid(py,
                                                                                                        self.text_featurizer.vocab_array)))
                continue

            txt = list(txt)
            phone_feature = self.phone_featurizer.extract(py)
            text_feature = self.text_featurizer.extract(txt)+[self.text_featurizer.endid()]

            if in_len < len(phone_feature):
                logging.info('{} feature length < phone length,continue'.format(wp))
                continue
            max_input = max(max_input, len(speech_feature))

            speech_features.append(speech_feature)
            input_length.append(in_len)
            phones.append(np.array(phone_feature))
            txts.append(np.array(text_feature))
            phones_length.append(len(phone_feature))
            sample.append(line)
            if len(sample) == batch:
                break


        if self.streaming:
            max_input = max_input // self.chunk * self.chunk + self.chunk
        speech_features = self.speech_featurizer.pad_signal(speech_features, max_input)



        if self.streaming:
            reduce = self.speech_config['reduction_factor'] * (self.speech_featurizer.sample_rate / 1000) * \
                     self.speech_config['stride_ms']
            max_input = max_input // self.chunk * self.chunk + self.chunk
            max_in_len = max_input // self.chunk
            chunk_times = self.chunk // reduce
            if self.chunk % reduce != 0:
                chunk_times += 1
            max_in_len *= chunk_times
            input_length = np.clip(input_length, 0, max_in_len)
        speech_features = self.speech_featurizer.pad_signal(speech_features, max_input)

        phones = tf.keras.preprocessing.sequence.pad_sequences(phones, maxlen=max([len(i) for i in phones]),
                                                               padding='post', value=self.phone_featurizer.pad)
        txts = tf.keras.preprocessing.sequence.pad_sequences(txts, maxlen=max([len(i) for i in txts]), padding='post',
                                                             value=self.text_featurizer.pad)
        x = np.array(speech_features, 'float32')
        phones = np.array(phones, 'int32')
        txts = np.array(txts, 'int32')

        input_length = np.array(input_length, 'int32')
        phones_length = np.array(phones_length, 'int32')

        return x, input_length, phones, phones_length, txts

    def check_valid(self, txt, vocab_list):
        if len(txt) == 0:
            return False
        for n in txt:
            if n in vocab_list:
                pass
            else:
                return n
        return True


    def generate(self, train=True):

        sample = []
        speech_features = []
        input_length = []
        phones = []
        phones_length = []
        txts = []

        max_input = 0

        if train:
            batch = self.batch * 3 // 4 if self.augment.available() else self.batch
        else:
            batch = self.batch

        for i in range(batch * 10):
            if train:
                line = self.train_list[self.train_offset]
                self.train_offset += 1
                if self.train_offset > len(self.train_list) - 1:
                    self.train_offset = 0
                    np.random.shuffle(self.train_list)
                    self.epochs += 1
            else:
                line = self.test_list[self.test_offset]
                self.test_offset += 1
                if self.test_offset > len(self.test_list) - 1:
                    self.test_offset = 0
            wp, txt = line.strip().split('\t')
            try:
                data = self.speech_featurizer.load_wav(wp)
            except:
                logging.info('{} load data failed,skip'.format(wp))
                continue
            if len(data) < 400:
                continue
            elif len(data) > self.speech_featurizer.sample_rate * self.speech_config['wav_max_duration']:
                logging.info(
                    '{} duration out of wav_max_duration({}),skip'.format(wp, self.speech_config['wav_max_duration']))
                continue
            if self.speech_config['only_chinese']:
                txt = self.only_chinese(txt)

            if not self.streaming:
                speech_feature = data / np.abs(data).max()
                speech_feature = np.expand_dims(speech_feature, -1)
                in_len = len(speech_feature) // (
                        self.speech_config['reduction_factor'] * (self.speech_featurizer.sample_rate / 1000) *
                        self.speech_config['stride_ms'])
            else:
                speech_feature = data
                speech_feature = np.expand_dims(speech_feature, -1)
                reduce = self.speech_config['reduction_factor'] * (self.speech_featurizer.sample_rate / 1000) * \
                         self.speech_config['stride_ms']
                in_len = len(speech_feature) // self.chunk
                if len(speech_feature) % self.chunk != 0:
                    in_len += 1
                chunk_times = self.chunk // reduce
                if self.chunk % reduce != 0:
                    chunk_times += 1
                in_len *= chunk_times

            py = self.text_to_vocab(txt)
            if self.check_valid(py, self.phone_featurizer.vocab_array) is not True:
                logging.info(' {} txt phone {} not all in tokens,continue'.format(txt, self.check_valid(py,
                                                                                                        self.phone_featurizer.vocab_array)))
                continue
            if self.check_valid(txt, self.text_featurizer.vocab_array) is not True:
                logging.info(' {} txt  {} not all in tokens,continue'.format(txt, self.check_valid(txt,
                                                                                                        self.text_featurizer.vocab_array)))
                continue

            txt = list(txt)
            phone_feature = self.phone_featurizer.extract(py)
            text_feature = self.text_featurizer.extract(txt)+[self.text_featurizer.endid()]

            if in_len < len(phone_feature):
                logging.info('{} feature length < phone length,continue'.format(wp))
                continue
            max_input = max(max_input, len(speech_feature))

            speech_features.append(speech_feature)
            input_length.append(in_len)
            phones.append(np.array(phone_feature))
            txts.append(np.array(text_feature))
            phones_length.append(len(phone_feature))
            sample.append(line)
            if len(sample) == batch:
                break
        if train and self.augment.available():

            sample = random.sample(sample, self.batch // 4)
            for i in sample:
                wp, txt = i.strip().split('\t')
                try:
                    data = self.speech_featurizer.load_wav(wp)
                except:
                    continue
                if len(data) < 400:
                    logging.info('{} wav too short < 25ms,skip'.format(wp))
                    continue
                elif len(data) > self.speech_featurizer.sample_rate * self.speech_config['wav_max_duration']:
                    continue
                data = self.augment.process(data)
                if self.speech_config['only_chinese']:
                    txt = self.only_chinese(txt)

                if not self.streaming:
                    speech_feature = data / np.abs(data).max()
                    speech_feature = np.expand_dims(speech_feature, -1)
                    in_len = len(speech_feature) // (
                            self.speech_config['reduction_factor'] * (self.speech_featurizer.sample_rate / 1000) *
                            self.speech_config['stride_ms'])
                else:
                    speech_feature = data
                    speech_feature = np.expand_dims(speech_feature, -1)
                    reduce = self.speech_config['reduction_factor'] * (self.speech_featurizer.sample_rate / 1000) * \
                             self.speech_config['stride_ms']
                    in_len = len(speech_feature) // self.chunk
                    if len(speech_feature) % self.chunk != 0:
                        in_len += 1
                    chunk_times = self.chunk // reduce
                    if self.chunk % reduce != 0:
                        chunk_times += 1
                    in_len *= chunk_times

                py = self.text_to_vocab(txt)
                if self.check_valid(py, self.phone_featurizer.vocab_array) is not True:
                    logging.info(' {} txt phone {} not all in tokens,continue'.format(txt, self.check_valid(py,
                                                                                                             self.phone_featurizer.vocab_array)))
                    continue
                if self.check_valid(txt, self.text_featurizer.vocab_array) is not True:
                    logging.info(' {} txt  {} not all in tokens,continue'.format(txt, self.check_valid(txt,
                                                                                                             self.text_featurizer.vocab_array)))
                    continue

                txt = list(txt)
                phone_feature = self.phone_featurizer.extract(py)
                text_feature = self.text_featurizer.extract(txt)+[self.text_featurizer.endid()]

                if in_len < len(phone_feature):
                    logging.info('{} feature length < phone length,continue'.format(wp))
                    continue

                max_input = max(max_input, len(speech_feature))

                speech_features.append(speech_feature)
                input_length.append(in_len)
                phones.append(np.array(phone_feature))
                txts.append(np.array(text_feature))
                phones_length.append(len(phone_feature))


        if self.streaming:
            reduce = self.speech_config['reduction_factor'] * (self.speech_featurizer.sample_rate / 1000) * \
                     self.speech_config['stride_ms']
            max_input = max_input // self.chunk * self.chunk + self.chunk
            max_in_len = max_input // self.chunk
            chunk_times = self.chunk // reduce
            if self.chunk % reduce != 0:
                chunk_times += 1
            max_in_len *= chunk_times
            input_length = np.clip(input_length, 0, max_in_len)
        speech_features = self.speech_featurizer.pad_signal(speech_features, max_input)


        phones=tf.keras.preprocessing.sequence.pad_sequences(phones,maxlen=max([len(i) for i in phones]),padding='post',value=self.phone_featurizer.pad)
        txts=tf.keras.preprocessing.sequence.pad_sequences(txts,maxlen=max([len(i) for i in txts]),padding='post',value=self.text_featurizer.pad)
        x = np.array(speech_features, 'float32')
        phones = np.array(phones, 'int32')
        txts = np.array(txts, 'int32')

        input_length = np.array(input_length, 'int32')
        phones_length = np.array(phones_length, 'int32')

        return x, input_length, phones, phones_length,txts

    def generator(self, train=True):
        while 1:
            s=time.time()
            x, input_length, phones, phones_length,txts = self.generate(train)
            e=time.time()
            logging.info('load data cost time: {}'.format(e-s))
            if x.shape[0] == 0:
                logging.info('load data length zero,continue')
                continue


            yield x, input_length, phones, phones_length,txts

[((243, 350), 'logging.basicConfig', 'logging.basicConfig', ([], {'level': 'logging.INFO', 'format': '"""%(asctime)s - %(name)s - %(levelname)s - %(message)s"""'}), "(level=logging.INFO, format=\n    '%(asctime)s - %(name)s - %(levelname)s - %(message)s')\n", (262, 350), False, 'import logging\n'), ((971, 1007), 'utils.speech_featurizers.SpeechFeaturizer', 'SpeechFeaturizer', (['self.speech_config'], {}), '(self.speech_config)\n', (987, 1007), False, 'from utils.speech_featurizers import SpeechFeaturizer\n'), ((1040, 1073), 'utils.text_featurizers.TextFeaturizer', 'TextFeaturizer', (['self.phone_config'], {}), '(self.phone_config)\n', (1054, 1073), False, 'from utils.text_featurizers import TextFeaturizer\n'), ((1105, 1137), 'utils.text_featurizers.TextFeaturizer', 'TextFeaturizer', (['self.text_config'], {}), '(self.text_config)\n', (1119, 1137), False, 'from utils.text_featurizers import TextFeaturizer\n'), ((1200, 1233), 'augmentations.augments.Augmentation', 'Augmentation', (['self.augment_config'], {}), '(self.augment_config)\n', (1212, 1233), False, 'from augmentations.augments import Augmentation\n'), ((1931, 2269), 'pypinyin.load_phrases_dict', 'pypinyin.load_phrases_dict', (["{'调大': [['tiáo'], ['dà']], '调小': [['tiáo'], ['xiǎo']], '调亮': [['tiáo'], [\n    'liàng']], '调暗': [['tiáo'], ['àn']], '肖': [['xiāo']], '英雄传': [['yīng'],\n    ['xióng'], ['zhuàn']], '新传': [['xīn'], ['zhuàn']], '外传': [['wài'], [\n    'zhuàn']], '正传': [['zhèng'], ['zhuàn']], '水浒传': [['shuǐ'], ['hǔ'], [\n    'zhuàn']]}"], {}), "({'调大': [['tiáo'], ['dà']], '调小': [['tiáo'], [\n    'xiǎo']], '调亮': [['tiáo'], ['liàng']], '调暗': [['tiáo'], ['àn']], '肖': [\n    ['xiāo']], '英雄传': [['yīng'], ['xióng'], ['zhuàn']], '新传': [['xīn'], [\n    'zhuàn']], '外传': [['wài'], ['zhuàn']], '正传': [['zhèng'], ['zhuàn']],\n    '水浒传': [['shuǐ'], ['hǔ'], ['zhuàn']]})\n", (1957, 2269), False, 'import pypinyin\n'), ((9102, 9138), 'numpy.array', 'np.array', (['speech_features', '"""float32"""'], {}), "(speech_features, 'float32')\n", (9110, 9138), True, 'import numpy as np\n'), ((9156, 9181), 'numpy.array', 'np.array', (['phones', '"""int32"""'], {}), "(phones, 'int32')\n", (9164, 9181), True, 'import numpy as np\n'), ((9197, 9220), 'numpy.array', 'np.array', (['txts', '"""int32"""'], {}), "(txts, 'int32')\n", (9205, 9220), True, 'import numpy as np\n'), ((9245, 9276), 'numpy.array', 'np.array', (['input_length', '"""int32"""'], {}), "(input_length, 'int32')\n", (9253, 9276), True, 'import numpy as np\n'), ((9301, 9333), 'numpy.array', 'np.array', (['phones_length', '"""int32"""'], {}), "(phones_length, 'int32')\n", (9309, 9333), True, 'import numpy as np\n'), ((17946, 17982), 'numpy.array', 'np.array', (['speech_features', '"""float32"""'], {}), "(speech_features, 'float32')\n", (17954, 17982), True, 'import numpy as np\n'), ((18000, 18025), 'numpy.array', 'np.array', (['phones', '"""int32"""'], {}), "(phones, 'int32')\n", (18008, 18025), True, 'import numpy as np\n'), ((18041, 18064), 'numpy.array', 'np.array', (['txts', '"""int32"""'], {}), "(txts, 'int32')\n", (18049, 18064), True, 'import numpy as np\n'), ((18089, 18120), 'numpy.array', 'np.array', (['input_length', '"""int32"""'], {}), "(input_length, 'int32')\n", (18097, 18120), True, 'import numpy as np\n'), ((18145, 18177), 'numpy.array', 'np.array', (['phones_length', '"""int32"""'], {}), "(phones_length, 'int32')\n", (18153, 18177), True, 'import numpy as np\n'), ((1482, 1519), 'tensorflow.TensorShape', 'tf.TensorShape', (['[self.batch, None, 1]'], {}), '([self.batch, None, 1])\n', (1496, 1519), True, 'import tensorflow as tf\n'), ((1534, 1562), 'tensorflow.TensorShape', 'tf.TensorShape', (['[self.batch]'], {}), '([self.batch])\n', (1548, 1562), True, 'import tensorflow as tf\n'), ((1578, 1612), 'tensorflow.TensorShape', 'tf.TensorShape', (['[self.batch, None]'], {}), '([self.batch, None])\n', (1592, 1612), True, 'import tensorflow as tf\n'), ((1626, 1654), 'tensorflow.TensorShape', 'tf.TensorShape', (['[self.batch]'], {}), '([self.batch])\n', (1640, 1654), True, 'import tensorflow as tf\n'), ((1670, 1704), 'tensorflow.TensorShape', 'tf.TensorShape', (['[self.batch, None]'], {}), '([self.batch, None])\n', (1684, 1704), True, 'import tensorflow as tf\n'), ((2633, 2653), 'pypinyin.pinyin', 'pypinyin.pinyin', (['txt'], {}), '(txt)\n', (2648, 2653), False, 'import pypinyin\n'), ((3402, 3436), 'numpy.random.shuffle', 'np.random.shuffle', (['self.train_list'], {}), '(self.train_list)\n', (3419, 3436), True, 'import numpy as np\n'), ((8531, 8567), 'numpy.clip', 'np.clip', (['input_length', '(0)', 'max_in_len'], {}), '(input_length, 0, max_in_len)\n', (8538, 8567), True, 'import numpy as np\n'), ((13770, 13808), 'random.sample', 'random.sample', (['sample', '(self.batch // 4)'], {}), '(sample, self.batch // 4)\n', (13783, 13808), False, 'import random\n'), ((17508, 17544), 'numpy.clip', 'np.clip', (['input_length', '(0)', 'max_in_len'], {}), '(input_length, 0, max_in_len)\n', (17515, 17544), True, 'import numpy as np\n'), ((18307, 18318), 'time.time', 'time.time', ([], {}), '()\n', (18316, 18318), False, 'import time\n'), ((18412, 18423), 'time.time', 'time.time', ([], {}), '()\n', (18421, 18423), False, 'import time\n'), ((5480, 5514), 'numpy.expand_dims', 'np.expand_dims', (['speech_feature', '(-1)'], {}), '(speech_feature, -1)\n', (5494, 5514), True, 'import numpy as np\n'), ((5822, 5856), 'numpy.expand_dims', 'np.expand_dims', (['speech_feature', '(-1)'], {}), '(speech_feature, -1)\n', (5836, 5856), True, 'import numpy as np\n'), ((7621, 7644), 'numpy.array', 'np.array', (['phone_feature'], {}), '(phone_feature)\n', (7629, 7644), True, 'import numpy as np\n'), ((7670, 7692), 'numpy.array', 'np.array', (['text_feature'], {}), '(text_feature)\n', (7678, 7692), True, 'import numpy as np\n'), ((11347, 11381), 'numpy.expand_dims', 'np.expand_dims', (['speech_feature', '(-1)'], {}), '(speech_feature, -1)\n', (11361, 11381), True, 'import numpy as np\n'), ((11689, 11723), 'numpy.expand_dims', 'np.expand_dims', (['speech_feature', '(-1)'], {}), '(speech_feature, -1)\n', (11703, 11723), True, 'import numpy as np\n'), ((13484, 13507), 'numpy.array', 'np.array', (['phone_feature'], {}), '(phone_feature)\n', (13492, 13507), True, 'import numpy as np\n'), ((13533, 13555), 'numpy.array', 'np.array', (['text_feature'], {}), '(text_feature)\n', (13541, 13555), True, 'import numpy as np\n'), ((18536, 18582), 'logging.info', 'logging.info', (['"""load data length zero,continue"""'], {}), "('load data length zero,continue')\n", (18548, 18582), False, 'import logging\n'), ((10262, 10296), 'numpy.random.shuffle', 'np.random.shuffle', (['self.train_list'], {}), '(self.train_list)\n', (10279, 10296), True, 'import numpy as np\n'), ((14597, 14631), 'numpy.expand_dims', 'np.expand_dims', (['speech_feature', '(-1)'], {}), '(speech_feature, -1)\n', (14611, 14631), True, 'import numpy as np\n'), ((14963, 14997), 'numpy.expand_dims', 'np.expand_dims', (['speech_feature', '(-1)'], {}), '(speech_feature, -1)\n', (14977, 14997), True, 'import numpy as np\n'), ((16873, 16896), 'numpy.array', 'np.array', (['phone_feature'], {}), '(phone_feature)\n', (16881, 16896), True, 'import numpy as np\n'), ((16926, 16948), 'numpy.array', 'np.array', (['text_feature'], {}), '(text_feature)\n', (16934, 16948), True, 'import numpy as np\n'), ((5428, 5440), 'numpy.abs', 'np.abs', (['data'], {}), '(data)\n', (5434, 5440), True, 'import numpy as np\n'), ((11295, 11307), 'numpy.abs', 'np.abs', (['data'], {}), '(data)\n', (11301, 11307), True, 'import numpy as np\n'), ((14541, 14553), 'numpy.abs', 'np.abs', (['data'], {}), '(data)\n', (14547, 14553), True, 'import numpy as np\n')]