Exemple #1
0
def getReportResourceFilename(report_filename='', report_dir=''):
    """
    Получить полное имя файла шаблона отчета.
    @param report_filename: Имя файла отчета в кратком виде.
    @param report_dir: Папка отчетов.
    @return: Полное имя файла отчета.
    """
    # Проверить расширение
    rprt_filename = report_filename
    if not rprt_filename.endswith(DEFAULT_REPORT_FILE_EXT):
        rprt_filename = os.path.splitext(
            rprt_filename)[0] + DEFAULT_REPORT_FILE_EXT

    # Проверить актуальность шаблона
    full_src_filename = getPathFilename(report_filename, report_dir)
    full_rprt_filename = getPathFilename(rprt_filename, report_dir)
    if isNewReportTemplateFile(full_src_filename, full_rprt_filename):
        # Если исходный шаблон изменен позже чем рабочий файл шаблона <rprt>
        # то необходимо сделать изменения
        updateReportTemplateFile(full_src_filename, full_rprt_filename)

    if os.path.exists(rprt_filename):
        # Проверить может быть задано абсолютное имя файла
        filename = rprt_filename
    else:
        # Задано скорее всего относительное имя файла
        # относительно папки отчетов
        filename = full_rprt_filename
        if not os.path.exists(filename):
            # Нет такого файла
            log.warning(u'Файл шаблона отчета <%s> не найден' %
                        textfunc.toUnicode(filename))
            filename = createReportResourceFile(filename)
    log.debug(u'Полное имя файла шаблона <%s>' % textfunc.toUnicode(filename))
    return filename
Exemple #2
0
    def NewByOffice(self, dst_path=None):
        """
        Создание нового отчета средствами LibreOffice Calc.
        @param dst_path: Результирующая папка, в которую будет помещен новый файл.
        """
        try:
            src_filename = DEFAULT_REP_TMPL_FILE
            new_filename = dlg.getTextInputDlg(
                self._ParentForm, u'Создание нового файла',
                u'Введите имя файла шаблона отчета')
            if os.path.splitext(new_filename)[1] != '.ods':
                new_filename += '.ods'

            if dst_path is None:
                # Необходимо определить результирующий путь
                dst_path = dlg.getDirDlg(self._ParentForm, u'Папка хранения')
                if not dst_path:
                    dst_path = os.getcwd()

            dst_filename = os.path.join(dst_path, new_filename)
            if os.path.exists(dst_filename):
                if dlg.getAskBox(u'Заменить существующий файл?'):
                    shutil.copyfile(src_filename, dst_filename)
            else:
                shutil.copyfile(src_filename, dst_filename)

            cmd = OFFICE_OPEN_CMD_FORMAT % dst_filename
            log.debug(u'Command <%s>' % textfunc.toUnicode(cmd))
            os.system(cmd)

            return True
        except:
            # Вывести сообщение об ошибке в лог
            log.fatal(u'New report template by LibreOffice Calc')
Exemple #3
0
    def save(self, report_data=None, is_virtual_excel=True):
        """
        Сохранить результаты генерации в файл
        @param report_data: Сгенерированный отчет.
        @param is_virtual_excel: Сохранение произвести с помощью VirtualExcel?
            True - да, False - Сохранение производится конвертацией с помощью UNOCONV.
            ВНИМАНИЕ! При конвертации с помощью UNOCONV ячейки не образмериваются.
                Размеры ячеек остаются по умолчанию.
                UNOCONV транслирует не все стили и атрибуты ячеек.
        @return: Имя сохраненного файла или None, если сохранения не произошло.
        """
        if report_data:
            rep_file = icrepfile.icExcelXMLReportFile()
            save_dir = self.getProfileDir()
            if not save_dir:
                save_dir = icrepgensystem.DEFAULT_REPORT_DIR
            # print(u'DBG:', save_dir, report_data, type(report_data))
            xml_rep_file_name = os.path.join(
                save_dir, '%s_report_result.xml' % report_data['name'])
            rep_file_name = os.path.join(
                save_dir, '%s_report_result.ods' % report_data['name'])

            rep_file.write(xml_rep_file_name, report_data)

            if is_virtual_excel:
                log.info(u'Конвертация отчета <%s> в файл <%s>' %
                         (textfunc.toUnicode(xml_rep_file_name),
                          textfunc.toUnicode(rep_file_name)))
                v_excel = icexcel.icVExcel()
                v_excel.Load(xml_rep_file_name)
                v_excel.SaveAs(rep_file_name)
            else:
                # ВНИМАНИЕ! UNOCONV транслирует не все стили и атрибуты ячеек
                # Поэтому сначала используется Virtual Excel
                cmd = 'unoconv --format=ods %s' % xml_rep_file_name
                log.info(
                    u'UNOCONV. Конвертация отчета <%s> в файл <%s>. (%s)' %
                    (textfunc.toUnicode(xml_rep_file_name),
                     textfunc.toUnicode(rep_file_name),
                     textfunc.toUnicode(cmd)))
                os.system(cmd)

            return rep_file_name
        return None
Exemple #4
0
    def generate(self,
                 report=None,
                 db_url=None,
                 sql=None,
                 stylelib=None,
                 vars=None,
                 *args,
                 **kwargs):
        """
        Запустить генератор отчета.
        @param report: Шаблон отчета.
        @param db_url: Connection string в виде url. Например
            postgresql+psycopg2://postgres:[email protected]:5432/realization.
        @param sql: Запрос SQL.
        @param stylelib: Библиотека стилей.
        @param vars: Словарь переменных отчета.
        @return: Возвращает сгенерированный отчет или None в случае ошибки.
        """
        try:
            if report is not None:
                self._Rep = report

            if stylelib:
                self._Rep['style_lib'] = stylelib

            if vars:
                self._Rep['variables'] = vars

            # 1. Получить таблицу запроса
            _kwargs = copy.deepcopy(kwargs)
            _kwargs.update(
                dict(db_url=db_url, sql=sql, stylelib=stylelib,
                     variables=vars))
            query_tbl = self.getQueryTbl(self._Rep, **_kwargs)
            if self._isEmptyQueryTbl(query_tbl):
                dlg.getMsgBox(
                    u'Внимание', u'Нет данных, соответствующих запросу: %s' %
                    self._Rep['query'], self._ParentForm)
                return None

            # 2. Запустить генерацию
            rep = icrepgen.icReportGenerator()
            data_rep = rep.generate(self._Rep,
                                    query_tbl,
                                    NameSpace_=vars,
                                    *args,
                                    **kwargs)

            return data_rep
        except:
            # Вывести сообщение об ошибке в лог
            log.fatal(u'Ошибка генерации отчета <%s>.' %
                      textfunc.toUnicode(self._Rep['name']))
        return None
Exemple #5
0
    def write(self, RepFileName_, RepData_):
        """
        Сохранить заполненный отчет в файле.
        @param RepFileName_: Имя файла отчета XML.
        @param RepData_: Данные отчета.
        @return: Функция возвращает имя созданного xml файла, 
            или None в случае ошибки.
        """
        xml_file = None
        try:
            # Начать запись
            xml_file = open(RepFileName_, 'w')
            xml_gen = icXMLSSGenerator(xml_file)
            xml_gen.startDocument()
            xml_gen.startBook()

            # Параметры страницы
            # xml_gen.savePageSetup(RepName_,Rep_)

            # Стили
            xml_gen.scanStyles(RepData_['sheet'])
            xml_gen.saveStyles()

            # Данные
            xml_gen.startSheet(RepData_['name'], RepData_)
            xml_gen.saveColumns(RepData_['sheet'])
            for i_row in xrange(len(RepData_['sheet'])):
                xml_gen.startRow(RepData_['sheet'][i_row])
                # Сбросить индекс ячейки
                xml_gen.cell_idx = 1
                for i_col in range(len(RepData_['sheet'][i_row])):
                    cell = RepData_['sheet'][i_row][i_col]
                    xml_gen.saveCell(i_row + 1, i_col + 1, cell,
                                     RepData_['sheet'])
                xml_gen.endRow()

            xml_gen.endSheet(RepData_)

            # Закончить запись
            xml_gen.endBook()
            xml_gen.endDocument()
            xml_file.close()

            return RepFileName_
        except:
            if xml_file:
                xml_file.close()
            log.error(u'Ошибка сохранения отчета <%s>.' %
                      textfunc.toUnicode(RepFileName_))
            raise
        return None
Exemple #6
0
def createReportResourceFile(template_filename):
    """
    Создать ресурсный файл шаблона по имени запрашиваемого.
    @param template_filename: Имя запрашиваемого файла шаблона.
    @return: Скорректированное имя созданного файла шаблона или None в случае ошибки.
    """
    # Коррекция имени файла с учетом русских букв в имени файла
    dir_name = os.path.dirname(template_filename)
    base_filename = os.path.basename(template_filename).replace(' ', '_')
    base_filename = textfunc.rus2lat(base_filename) if textfunc.isRUSText(
        base_filename) else base_filename
    norm_tmpl_filename = os.path.join(dir_name, base_filename)

    log.info(u'Создание нового файла шаблона <%s>' % norm_tmpl_filename)
    # Последовательно проверяем какой файл можно взять за основу для шаблона
    for ext in report_generator.SRC_REPORT_EXT:
        src_filename = os.path.splitext(template_filename)[0] + ext
        unicode_src_filename = textfunc.toUnicode(src_filename)
        if os.path.exists(src_filename):
            # Да такой файл есть и он может выступать
            # в качестве источника для шаблона
            log.info(u'Найден источник шаблона отчета <%s>' %
                     unicode_src_filename)
            try:
                rep_generator = report_generator.createReportGeneratorSystem(
                    ext)
                return rep_generator.Update(src_filename)
            except:
                log.fatal(u'Ошибка конвертации шаблона отчета <%s> -> <%s>' %
                          (unicode_src_filename, norm_tmpl_filename))
            return None

    log.warning(
        u'Не найдены источники шаблонов отчета в папке <%s> для <%s>' %
        (dir_name, textfunc.toUnicode(os.path.basename(template_filename))))
    return None
Exemple #7
0
 def _genHeader(self, Header_):
     """
     Сгенерировать заголовок отчета и перенести ее в выходной отчет.
     @param Header_: Бэнд заголовка.
     @return: Возвращает результат выполнения операции True/False.
     """
     try:
         log.debug(u'Генерация заголовка')
         # Добавлять будем в конец отчета,
         # поэтому опреелить максимальную строчку
         max_row = len(self._Rep['sheet'])
         i_row = 0
         cur_height = 0
         # Перебрать все ячейки бэнда
         for row in range(Header_['row'],
                          Header_['row'] + Header_['row_size']):
             for col in range(Header_['col'],
                              Header_['col'] + Header_['col_size']):
                 if self._TemplateSheet[row][col]:
                     self._genCell(self._TemplateSheet, row, col, self._Rep,
                                   max_row + i_row, col, self._CurRec)
                     cur_height = self._TemplateSheet[row][col]['height']
             i_row += 1
             # Увеличить текущую координату Y
             self._cur_top += cur_height
         # Прописать область
         self._Rep['header'] = {
             'row': max_row,
             'col': Header_['col'],
             'row_size': i_row,
             'col_size': Header_['col_size'],
         }
         # Очистить сумы суммирующих ячеек
         self._TemplateSheet = self._clearSum(self._TemplateSheet, 0,
                                              len(self._TemplateSheet))
         return True
     except:
         # Вывести сообщение об ошибке в лог
         log.fatal(u'Ошибка генерации заголовка отчета <%s>.' %
                   textfunc.toUnicode(self._RepName))
         return False
Exemple #8
0
    def parse(self, TemplateData_, template_name=None):
        """
        Разобрать/преобразовать прочитанную структуру.
        @param TemplateData_: Словарь описания шаблона.
        @param template_name: Имя шаблона(листа), если None то первый лист.
        """
        try:
            # Создать первоначальный шаблон
            rep = copy.deepcopy(icrepgen.IC_REP_TMPL)

            # 0. Определение основных структур
            workbook = TemplateData_['children'][0]
            # Стили (в виде словаря)
            styles = dict([(style['ID'], style) for style in [element for element in workbook['children']
                                                              if element['name'] == 'Styles'][0]['children']])
            worksheets = [element for element in workbook['children'] if element['name'] == 'Worksheet']

            # I. Определить все бэнды в шаблоне
            # Если имя шаблона не определено, тогда взять первый лист
            if template_name is None:
                template_name = worksheets[0]['Name']
                self._rep_worksheet = worksheets[0]
            else:
                # Установить активной страницу выбранного шаблона отчета
                self._rep_worksheet = [sheet for sheet in worksheets if sheet['Name'] == template_name][0]
            # Прописать имя отчета
            rep['name'] = template_name
            
            # Взять таблицу
            rep_template_tabs = [rep_obj for rep_obj in self._rep_worksheet['children'] if rep_obj['name'] == 'Table']
            self._setDefaultCellSize(rep_template_tabs[0])
            # Привести таблицу к нормальному виду
            rep_template_tab = self._normTable(rep_template_tabs[0])

            # Взять описания колонок
            rep_template_cols = [element for element in rep_template_tab['children'] if element['name'] == 'Column']
            rep_template_cols = self._defineSpan(rep_template_cols)
            # Взять описания строк
            rep_template_rows = [element for element in rep_template_tab['children'] if element['name'] == 'Row']
            rep_template_rows = self._defineSpan(rep_template_rows)

            # Количество колонок без колонки тегов бендов
            col_count = self._getColumnCount(rep_template_rows)
            log.debug(u'Количество колонок: %d' % col_count)

            # II. Определить все ячейки листа
            used_rows = range(len(rep_template_rows))
            used_cols = range(col_count)

            self.__cur_band = None  # Тег текущего бенда
            # Перебор по строкам
            for cur_row in used_rows:
                if not self._isTitleBand(rep_template_rows, cur_row):
                    # Не колонтитулы, добавить ячейки в общий лист
                    rep['sheet'].append([])
                    for cur_col in used_cols:
                        cell_attr = self._getCellAttr(rep_template_rows, rep_template_cols, styles, cur_row, cur_col)
                        if not self._isTag(cell_attr['value']):
                            rep['sheet'][-1].append(cell_attr)
                        else:
                            rep['sheet'][-1].append(None)

            # III. Определить бэнды в шаблоне
            # Перебрать все ячейки первой колонки
            self.__cur_band = None  # Тег текущего бенда
            title_row = 0   # Счетчик строк колонтитулов/заголовочных бендов
    
            # Перебор всех строк в шаблоне
            for cur_row in range(len(rep_template_rows)):
                tag = self._getTagBandRow(rep_template_rows, cur_row)
                # Если это ячейка с определенным тегом, значит новый бенд
                if tag:
                    # Определить текущий бэнд
                    self.__cur_band = tag
                    if tag in TITLE_TAGS:
                        # Обработка строк заголовочных тегов
                        parse_func = self._TitleTagParse.setdefault(tag, None)
                        try:
                            parse_func(self, rep, rep_template_rows[cur_row]['children'])
                        except:
                            log.fatal(u'Ошибка парсинга функции <%s>' % textfunc.toUnicode(parse_func))
                        title_row += 1
                    else:
                        # Определить бэнд внутри объекта
                        rep = self._defBand(self.__cur_band, cur_row, col_count, title_row, rep)
                else:
                    log.error(u'Не корректный тег строки [%d]' % cur_row)

            # Прочитать в шаблон параметры страницы
            rep['page_setup'] = copy.deepcopy(icrepgen.IC_REP_PAGESETUP)
            sheet_options = [rep_obj for rep_obj in self._rep_worksheet['children']
                             if rep_obj['name'] == 'WorksheetOptions']

            page_setup = [rep_obj for rep_obj in sheet_options[0]['children'] if rep_obj['name'] == 'PageSetup'][0]
            rep['page_setup'].update(self._getPageSetup(page_setup))
            print_setup = [rep_obj for rep_obj in sheet_options[0]['children'] if rep_obj['name'] == 'Print']
            if print_setup:
                rep['page_setup'].update(self._getPrintSetup(print_setup[0]))

            # Проверить заполнение генератора отчета
            if not rep['generator']:
                tmpl_filename = self.getTemplateFilename()
                rep['generator'] = os.path.splitext(tmpl_filename)[1].upper() if tmpl_filename else '.ODS'

            return rep
        except:
            log.fatal(u'Ошибка парсинга шаблона отчета <%s>' % textfunc.toUnicode(template_name))
        return None
Exemple #9
0
    def _genTxt(self, cell, record=None, CellRow_=None, CellCol_=None):
        """
        Генерация текста.
        @param cell: Ячейка.
        @param record: Словарь, описывающий текущую запись таблицы запроса.
            Формат: { <имя поля> : <значение поля>, ...}
        @param CellRow_: Номер строки ячейки в результирующем отчете.
        @param CellCol_: Номер колонки ячейки в результирующем отчете.
        @return: Возвращает сгенерированное значение.
        """
        try:
            # Проверка на преобразование типов
            cell_val = cell['value']
            if cell_val is not None and type(cell_val) not in (str, unicode):
                cell_val = str(cell_val)
            if cell_val not in self._cellFmt:
                parsed_fmt = self.funcStrParse(cell_val)
                self._cellFmt[cell_val] = parsed_fmt
            else:
                parsed_fmt = self._cellFmt[cell_val]

            func_str = []  # Выходной список значений
            i_sum = 0
            # Перебрать строки функционала
            for cur_func in parsed_fmt['func']:

                # Функция
                if re.search(REP_FUNC_PATT, cur_func):
                    value = str(self._execFuncGen(cur_func[2:-2], locals()))

                # Ламбда-выражение
                elif re.search(REP_LAMBDA_PATT, cur_func):
                    # ВНИМАНИЕ: Лямбда-выражение в шаблоне должно иметь
                    #     1 аргумент это словарь записи.
                    #     Например:
                    #         [~rec: rec['name']=='Петров'~]
                    lambda_func = eval('lambda ' + cur_func[2:-2])
                    value = str(lambda_func(record))

                # Переменная
                elif re.search(REP_VAR_PATT, cur_func):
                    var_name = cur_func[2:-2]
                    if var_name in self._NameSpace:
                        log.debug(u'Обработка переменной <%s>' % var_name)
                    else:
                        log.warning(
                            u'Переменная <%s> не найдена в пространстве имен' %
                            var_name)
                    value = str(self._NameSpace.setdefault(var_name, u''))

                # Блок кода
                elif re.search(REP_EXEC_PATT, cur_func):
                    # ВНИМАНИЕ: В блоке кода доступны объекты cell и record.
                    #     Если надо вывести информацию, то ее надо выводить в
                    #     переменную value.
                    #     Например:
                    #         [=if record['name']=='Петров':
                    #             value='-'=]
                    value = ''
                    exec_func = cur_func[2:-2].strip()
                    try:
                        exec exec_func
                    except:
                        log.fatal(u'Ошибка выполнения блока кода <%s>' %
                                  textfunc.toUnicode(exec_func))

                # Системная функция
                elif re.search(REP_SYS_PATT, cur_func):
                    # Функция суммирования
                    if cur_func[2:6].lower() == 'sum(':
                        value = str(cell['sum'][i_sum]['value'])
                        i_sum += 1  # Перейти к следующей сумме
                    # Функция вычисления среднего значения
                    elif cur_func[2:6].lower() == 'avg(':
                        if 'ic_sys_num_rec' not in record:
                            record['ic_sys_num_rec'] = 0
                        value = str(cell['sum'][i_sum]['value'] /
                                    (record['ic_sys_num_rec'] + 1))
                        i_sum += 1  # Перейти к следующей сумме
                    elif cur_func[2:-2].lower() == 'n':
                        if 'ic_sys_num_rec' not in record:
                            record['ic_sys_num_rec'] = 0
                        sys_num_rec = record['ic_sys_num_rec']
                        value = str(sys_num_rec + 1)
                    else:
                        # Вывести сообщение об ошибке в лог
                        log.warning(
                            u'Неизвестная системная функция <%s> шаблона <%s>.'
                            % (textfunc.toUnicode(cur_func), self._RepName))
                        value = ''

                # Стиль
                elif re.search(REP_STYLE_PATT, cur_func):
                    value = ''
                    style_name = cur_func[2:-2]
                    self._setStyleAttr(style_name)

                # Под-отчеты
                elif re.search(REP_SUBREPORT_PATT, cur_func):
                    value = ''
                    subreport_name = cur_func[2:-2]
                    self._genSubReport(subreport_name, CellRow_)

                # Поле
                elif re.search(REP_FIELD_PATT, cur_func):
                    field_name = str((cur_func[2:-2]))
                    try:
                        value = record[field_name]
                    except KeyError:
                        log.warning(u'В строке (%s) поле <%s> не найдено' %
                                    (textfunc.toUnicode(record),
                                     textfunc.toUnicode(field_name)))
                        value = ''

                # ВНИМАНИЕ! В значении ячейки тоже могут быть управляющие коды
                value = self._genTxt({'value': value}, record)
                func_str.append(value)

            # Заполнение формата
            return self._valueFormat(parsed_fmt['fmt'], func_str)
        except:
            # Вывести сообщение об ошибке в лог
            log.fatal(u'Ошибка генерации текста ячейки <%s> шаблона <%s>.' %
                      (textfunc.toUnicode(cell['value']), self._RepName))
            return None
Exemple #10
0
    def generate(self,
                 RepTemplate_,
                 QueryTable_,
                 NameSpace_=None,
                 CoordFill_=None):
        """
        Генерация отчета.
        @param RepTemplate_: Структура шаблона отчета (см. спецификации).
        @param QueryTable_: Таблица запроса. 
            Словарь следующей структуры:
                {
                    '__name__':имя таблицы запроса,
                    '__fields__':[имена полей],
                    '__data__':[список списков значений],
                    '__sub__':{словарь данных подотчетов},
                }.
        @param NameSpace_: Пространство имен шаблона.
            Обычный словарь:
                {
                    'имя переменной': значение переменной, 
                }.
            ВНИМАНИЕ! Этот словарь может передаваться в таблице запроса
                ключ __variables__.
        @param CoordFill_: Координатное заполнение значений ячеек.
            Формат:
                {
                    (Row,Col): 'Значение',
                }.
            ВНИМАНИЕ! Этот словарь может передаваться в таблице запроса
                ключ __coord_fill__.
        @return: Заполненную структуру отчета.
        """
        try:
            # Покоординатная замена значений ячеек
            self._CoordFill = CoordFill_
            if QueryTable_ and '__coord_fill__' in QueryTable_:
                if self._CoordFill is None:
                    self._CoordFill = dict()
                self._CoordFill.update(QueryTable_['__coord_fill__'])

            # Инициализация списка групп
            self._RepGrp = list()

            # I. Определить все бэнды в шаблоне и ячейки сумм
            if isinstance(RepTemplate_, dict):
                self._Template = RepTemplate_
            else:
                # Вывести сообщение об ошибке в лог
                log.warning(u'Ошибка типа шаблона отчета <%s>.' %
                            type(RepTemplate_))
                return None

            # Инициализация имени отчета
            if 'name' in QueryTable_ and QueryTable_['name']:
                # Если таблица запроса именована, то значит это имя готового отчета
                self._RepName = str(QueryTable_['name'])
            elif 'name' in self._Template:
                self._RepName = self._Template['name']

            # Заполнить пространство имен
            self._NameSpace = NameSpace_
            if self._NameSpace is None:
                self._NameSpace = dict()
            self._NameSpace.update(self._Template['variables'])
            if QueryTable_ and '__variables__' in QueryTable_:
                self._NameSpace.update(QueryTable_['__variables__'])
            log.debug(u'Переменные отчета: %s' % self._NameSpace.keys())

            # Библиотека стилей
            self._StyleLib = None
            if 'style_lib' in self._Template:
                self._StyleLib = self._Template['style_lib']

            self._TemplateSheet = self._Template['sheet']
            self._TemplateSheet = self._initSumCells(self._TemplateSheet)

            # II. Инициализация таблицы запроса
            self._QueryTbl = QueryTable_
            # Определить количество записей в таблице запроса
            self._QueryTblRecCount = 0
            if self._QueryTbl and '__data__' in self._QueryTbl:
                self._QueryTblRecCount = len(self._QueryTbl['__data__'])

            # Проинициализировать бенды групп
            for grp in self._Template['groups']:
                grp['old_rec'] = None

            time_start = time.time()
            log.info(u'Отчет <%s>. Запуск генерации' %
                     textfunc.toUnicode(self._RepName))

            # III. Вывод данных в отчет
            # Создать отчет
            self._Rep = copy.deepcopy(IC_REP_TMPL)
            self._Rep['name'] = self._RepName

            # Инициализация необходимых переменных
            field_idx = {}  # Индексы полей
            i = 0
            i_rec = 0
            # Перебор полей таблицы запроса
            if self._QueryTbl and '__fields__' in self._QueryTbl:
                for cur_field in self._QueryTbl['__fields__']:
                    field_idx[cur_field] = i
                    i += 1

            # Если записи в таблице запроса есть, то ...
            if self._QueryTblRecCount:
                # Проинициализировать текущую строку для использования
                # ее в заголовке отчета
                rec = self._QueryTbl['__data__'][i_rec]
                # Заполнить словарь текущей записи
                for field_name in field_idx.keys():
                    val = rec[field_idx[field_name]]
                    # Предгенерация значения данных ячейки
                    self._CurRec[field_name] = val
                # Прописать индекс текущей записи
                self._CurRec['ic_sys_num_rec'] = i_rec

            # Верхний колонтитул
            if self._Template['upper']:
                self._genUpper(self._Template['upper'])

            # Вывести в отчет заголовок
            self._genHeader(self._Template['header'])

            # Главный цикл
            # Перебор записей таблицы запроса
            while i_rec < self._QueryTblRecCount:
                # Обработка групп
                # Проверка смены группы в описании всех групп
                # и найти индекс самой общей смененной группы
                i_grp_out = -1  # индекс самой общей смененной группы
                # Флаг начала генерации (примечания групп не выводяться)
                start_gen = False
                for i_grp in range(len(self._Template['groups'])):
                    grp = self._Template['groups'][i_grp]
                    if grp['old_rec']:
                        # Проверить условие вывода примечания группы
                        if self._CurRec[grp['field']] != grp['old_rec'][
                                grp['field']]:
                            i_grp_out = i_grp
                            break
                    else:
                        i_grp_out = 0
                        start_gen = True
                        break
                if i_grp_out != -1:
                    # Вывести примечания
                    if start_gen is False:
                        for i_grp in range(
                                len(self._Template['groups']) - 1,
                                i_grp_out - 1, -1):
                            grp = self._Template['groups'][i_grp]
                            self._genGrpFooter(grp)
                    # Вывести заголовки
                    for i_grp in range(i_grp_out,
                                       len(self._Template['groups'])):
                        grp = self._Template['groups'][i_grp]
                        grp['old_rec'] = copy.deepcopy(self._CurRec)
                        self._genGrpHeader(grp)

                # Область данных
                self._genDetail(self._Template['detail'])

                # Увеличить суммы суммирующих ячеек
                self._sumIterate(self._TemplateSheet, self._CurRec)

                # Перейти на следующую запись
                i_rec += 1
                # Заполнить словарь текущей записи
                if i_rec < self._QueryTblRecCount:
                    rec = self._QueryTbl['__data__'][i_rec]
                    # Заполнить словарь текущей записи
                    for field_name in field_idx.keys():
                        val = rec[field_idx[field_name]]
                        # Предгенерация значения данных ячейки
                        self._CurRec[field_name] = val
                    # Прописать индекс текущей записи
                    self._CurRec['ic_sys_num_rec'] = i_rec

            # Вывести примечания после области данных
            for i_grp in range(len(self._Template['groups']) - 1, -1, -1):
                grp = self._Template['groups'][i_grp]
                if grp['old_rec']:
                    self._genGrpFooter(grp)
                else:
                    break
            # Вывести в отчет примечание отчета
            self._genFooter(self._Template['footer'])
            # Нижний колонтитул
            if self._Template['under']:
                self._genUnder(self._Template['under'])

            # Параметры страницы
            self._Rep['page_setup'] = self._Template['page_setup']

            # Прогресс бар
            log.info(
                u'Отчет <%s>. Окончание генерации. Время: %d сек.' %
                (textfunc.toUnicode(self._RepName), time.time() - time_start))

            return self._Rep
        except:
            # Вывести сообщение об ошибке в лог
            log.fatal(u'Ошибка генерации отчета.')
            return None
Exemple #11
0
    def _getSQLQueryTable(self, report, db_url=None, sql=None):
        """
        Получить таблицу запроса.
        @param report: Шаблон отчета.
        @param db_url: Connection string в виде url. Например
            postgresql+psycopg2://postgres:[email protected]:5432/realization.
        @param sql: Текст SQL запроса.
        @return: Функция возвращает словарь -
            ТАБЛИЦА ЗАПРОСА ПРЕДСТАВЛЯЕТСЯ В ВИДЕ СЛОВАРЯ 
            {'__fields__':имена полей таблицы,'__data__':данные таблицы}
        """
        result = None
        # Инициализация
        db_connection = None
        try:
            if not db_url:
                data_source = report['data_source']

                if not data_source:
                    # Учет случая когда источник данных не определен
                    log.warning(u'Не определен источник данных в отчете')
                    return {'__fields__': list(), '__data__': list()}

                signature = data_source[:4].upper()
                if signature != DB_URL_SIGNATURE:
                    log.warning('Not support DB type <%s>' % signature)
                    return result
                # БД задается с помощью стандартного DB URL
                db_url = data_source[4:].lower().strip()

            log.info(u'DB connection <%s>' % db_url)
            # Установить связь с БД
            db_connection = sqlalchemy.create_engine(db_url)
            # Освободить БД
            # db_connection.dispose()
            log.info(u'DB SQL <%s>' % textfunc.toUnicode(sql, 'utf-8'))
            sql_result = db_connection.execute(sql)
            rows = sql_result.fetchall()
            cols = rows[0].keys() if rows else []

            # Закрыть связь
            db_connection.dispose()
            db_connection = None

            # ТАБЛИЦА ЗАПРОСА ПРЕДСТАВЛЯЕТСЯ В ВИДЕ СЛОВАРЯ
            # {'__fields__':имена полей таблицы,'__data__':данные таблицы} !!!
            result = {'__fields__': cols, '__data__': list(rows)}
            return result
        except:
            if db_connection:
                # Закрыть связь
                db_connection.dispose()

            # Вывести сообщение об ошибке в лог
            log.fatal(u'Ошибка определения таблицы SQL запроса <%s>.' % sql)
            log.error(u'''ВНИМАНИЕ! Если возникает ошибка в модуле:
        ----------------------------------------------------------------------------------------------
        File "/usr/lib/python2.7/dist-packages/sqlalchemy/engine/default.py", line 324, in do_execute
            cursor.execute(statement, parameters)
        TypeError: 'dict' object does not support indexing
        ----------------------------------------------------------------------------------------------        
        Это означает что SQLAlchemy не может распарсить SQL выражение. 
        Необходимо вместо <%> использовать <%%> в SQL выражении. 
                    ''')

        return None