def saveResourceText(FileName_, Resource_): """ Сохранить ресурс в файле в текстовом формате. @param FileName_: Полное имя ресурсного файла. @param Resource_: Словарно-списковая структура спецификации. @return: Возвращает результат выполнения операции True/False. """ try: f = None # Если необходимые папки не созданы, то создать их dir_name = os.path.dirname(FileName_) try: os.makedirs(dir_name) except: pass f = open(FileName_, 'w') text = textfunc.StructToTxt(Resource_) f.write(text) f.close() log.info(u'Файл <%s> сохранен в текстовом формате.' % FileName_) return True except: if f: f.close() log.error(u'Ошибка сохраненения файла <%s> в текстовом формате.' % FileName_) return False
def setScanOptions(self, **options): """ Установить опции сканирования @param options: Значения опций. @return: True - все прошло удачно / False - ошибка. """ try: global SCAN_OPTIONS_ORDER for option_name in SCAN_OPTIONS_ORDER: if option_name in options: option_val = options[option_name] try: setattr(self.scan_device_obj, option_name, option_val) log.info( u'Установка опции сканирования <%s>. Значение <%s>' % (option_name, option_val)) # Запомнить выставленные значения опций # Может быть так что устанавливаем опции в устройстве # а они по не понятной причине не выставляются:-( self.options[option_name] = option_val except: log.warning( u'Ошибка установки опции сканирования <%s>. Значение <%s>' % (option_name, option_val)) return True except: log.fatal(u'Ошибка установки опций сканирования') return False
def copyDir(src_directory, dst_directory, bReWrite=False, bAddDir=True): """ Функция папку src_directory в папку dst_directory со всеми внутренними поддиректориями и файлами. @param src_directory: Папка/директория, которая копируется. @param dst_directory: Папка/директория, в которую копируется src_directory. @param bReWrite: Указание перезаписи директории, если она уже существует. @param bAddDir: Указание производить дополнение папки, в случае ко когда копируемые файлы/папки существуют. @return: Функция возвращает результат выполнения операции True/False. """ try: to_dir = os.path.join(dst_directory, os.path.basename(src_directory)) if os.path.exists(to_dir) and bReWrite: log.info(u'Удаление папки <%s>' % to_dir) shutil.rmtree(to_dir, 1) if os.path.exists(to_dir) and bAddDir: return addCopyDir(src_directory, to_dir) else: log.info(u'Копировние папки <%s> в <%s>' % (src_directory, to_dir)) shutil.copytree(src_directory, to_dir) return True except: log.fatal(u'Ошибка копирования папки из <%s> в <%s>' % (src_directory, dst_directory)) return False
def delWorksheet(self, XMLFileName_=None, SheetName_=None): """ Удалить безвозвратно лист. @param XMLFileName_: Имя XML файла книги. Если не определено, то имеется ввиду активная книга. @param SheetName_: Имя листа в указанной книге. Если не указано, то имеется ввиду первый лист. @return: True-лист удален, False-лист по какойто причине не удален. """ XMLFileName_ = self._unificXMLFileName(XMLFileName_) SheetName_ = self._unicode2str(SheetName_) # Определить книгу workbook_data = self._findWorkbookData(XMLFileName_) if workbook_data is None: log.warning('Workbook <%s> not found' % XMLFileName_) return False # Найти и удалить лист result = False for i, data in enumerate(workbook_data['children']): if data['name'] == 'Worksheet': if SheetName_ is None: # Если имя листа не определено, то просто удалить первый попавшийся лист del workbook_data['children'][i] result = True break else: # Если имя листа определено, то проверить на соответствие имен листов if data['Name'] == SheetName_: log.info('Delete <%s> worksheet' % SheetName_) del workbook_data['children'][i] result = True break return result
def glue_pdf_files(out_pdf_filename, *pdf_filenames): """ Процедура склеивания/объединения PDf файлов в один. @param out_pdf_filename: Полное наименование результирующего PDF файла. @param pdf_filenames: Список имен файлов-источников. Объединение происходит в порядке указанном списком pdf_filenames. Если какой либо файл отсутствует, то объединение происходит без него. @return: True - объединение прошло успешно / False - ошибка по какой-либо причине. """ try: merger = PyPDF2.PdfFileMerger() filenames = [ filename for filename in pdf_filenames if filename.lower().endswith('.pdf') and os.path.exists(filename) ] for filename in filenames: pdf_file = open(filename, 'rb') log.debug(u'Объединение PDF файла <%s> => <%s>' % (filename, out_pdf_filename)) reader = PyPDF2.PdfFileReader(pdf_file) merger.append(reader) out_pdf_filename = os.path.abspath(out_pdf_filename) if os.path.exists(out_pdf_filename): log.info(u'Удаление файла <%s>' % out_pdf_filename) try: os.remove(out_pdf_filename) except: log.fatal(u'Ошибка удаления файла <%s>' % out_pdf_filename) log.debug(u'Запись результирующего файла <%s>' % out_pdf_filename) merger.write(out_pdf_filename) return True except: log.fatal(u'Ошибка склеивания/объединения PDF файлов') return False
def _existTagBand(self): """ Присутствует в шаблоне колонка тегов бендов? Если не существует, то далее считаем, что весь шаблон - это заголовок отчета [header]. @return: True - колонка тегов бендов есть в шаблоне / False - нет. """ log.info(u'Колонка тегов бендов: %s' % str(self._tag_band_col)) return self._tag_band_col is not None
def icCopyFilesByMask(fromMask, toMask, Rewrite_=True, bDelImage=False): """ Копирует файлы по маске. Пример: "C:\WRK\p*.dbf" -> "Y:\WRK\pkt*.dbf" @type fromMask: C{string} @param fromMask: Маска, задающая файлы, которые надо копировать. @type toMask: C{string} @param toMask:Маска задает, куда надо копировать файлы. @type bDelImage: C{bool} @param bDelImage: Признак удаления образа файла. Если True - аналог переноса файлов с переименованием. """ fileList = [] fromMask = util.icUpper(fromMask.replace('\\', '/')) toMask = toMask.replace('\\', '/') dirName = '/'.join(fromMask.split('/')[:-1]) # Регулярное выражение для сравнения rm = re.compile( fromMask.replace('.', '\\.').replace('*', '.*').replace('$', '\\$')) ml1 = fromMask.split('*') ml2 = toMask.split('*') if os.path.isdir(dirName): # Определяем список файлов dir_list = os.listdir(dirName) for indx, fl in enumerate(dir_list): path, fileName = os.path.split(fl) # Определяем расширение файла try: ext = fileName.split('.')[1] except: ext = None fn = util.icUpper(dirName + '/' + fl) # Если очередное имя не является именем директории и удовлетворяет # шаблону, то соответствующий файл копируется в нужное место. if not os.path.isdir(fn) and truth(rm.match(fn)): # Определяем новое имя файла copyName = fn for i, s in enumerate(ml2): if i < len(ml1): if ml1[i] and s: copyName = copyName.replace(ml1[i], s) else: break log.info(u'Copy file %s -> %s' % (fn, copyName)) if icCopyFile(fn, copyName, Rewrite_) and bDelImage: Remove(fn) log.info(u'Delete file <%s>' % fn) else: log.warning(u'Dir <%> didn\'t find' % dirName)
def runExtScan(self): """ Запуск внешнего инструмента сканирования. @return: True/False. """ if self.ext_scan_cmd: log.info(u'Запуск комманды <%s>' % self.ext_scan_cmd) os.system(self.ext_scan_cmd) return True return False
def OpenOffice(self, ODSFileName_): """ Открыть. @param ODSFileName_: Имя ods файла, содержащего сгенерированный отчет. """ if ODSFileName_ and os.path.exists(ODSFileName_): cmd = 'libreoffice %s&' % ODSFileName_ log.info('Command <%s>' % cmd) os.system(cmd) else: log.warning(u'Открытие. Файл <%s> не найден' % ODSFileName_)
def PrintOffice(self, ODSFileName_): """ Печать отчета с помощью CALC. @param ODSFileName_: Имя ods файла, содержащего сгенерированный отчет. """ if ODSFileName_ and os.path.exists(ODSFileName_): cmd = 'libreoffice -p %s&' % ODSFileName_ log.info(u'Command <%s>' % cmd) os.system(cmd) else: log.warning(u'Печать. Файл <%s> не найден.' % ODSFileName_)
def Update(self, RepTemplateFileName_=None): """ Обновить шаблон отчета в системе генератора отчетов. @param RepTemplateFileName_: Имя файла шаблона отчета. Если None, то должен производиться запрос на выбор этого файла. @return: Имя файла файла шаблона или None в случае ошибки. """ if RepTemplateFileName_ is None: filename = dlg.getFileDlg( self._ParentForm, u'Выберите шаблон отчета:', u'Microsoft Excel 2003 XML (*.xml)|*.xml|Электронные таблицы ODF (*.ods)|*.ods', self.getReportDir()) else: filename = os.path.abspath(os.path.normpath(RepTemplateFileName_)) if os.path.isfile(filename): # Конвертация log.debug(u'Начало конвертации <%s>' % filename) tmpl_filename = None template = None if os.path.exists( os.path.splitext(filename)[0] + DEFAULT_TEMPLATE_EXT): tmpl_filename = os.path.splitext( filename)[0] + DEFAULT_TEMPLATE_EXT template = icreptemplate.icODSReportTemplate() elif os.path.exists( os.path.splitext(filename)[0] + ODS_TEMPLATE_EXT): tmpl_filename = os.path.splitext( filename)[0] + ODS_TEMPLATE_EXT template = icreptemplate.icODSReportTemplate() elif os.path.exists( os.path.splitext(filename)[0] + XLS_TEMPLATE_EXT): tmpl_filename = os.path.splitext( filename)[0] + XLS_TEMPLATE_EXT template = icreptemplate.icXLSReportTemplate() elif os.path.exists( os.path.splitext(filename)[0] + XML_TEMPLATE_EXT): tmpl_filename = os.path.splitext( filename)[0] + XML_TEMPLATE_EXT template = icreptemplate.icExcelXMLReportTemplate() else: log.warning(u'Not find report template for <%s>' % filename) if template: rep_template = template.read(tmpl_filename) new_filename = os.path.splitext( filename)[0] + DEFAULT_REPORT_TEMPLATE_EXT res.saveResourcePickle(new_filename, rep_template) log.info(u'Конец конвертации') return new_filename else: log.warning(u'Не найден файл источника шаблона <%s>' % filename) return None
def open(self, TemplateFile_): """ Открыть файл шаблона отчета. @param TemplateFile_: Файл шаблона отчета. """ try: ods_filename = os.path.splitext(TemplateFile_)[0] + '.ods' cmd = 'unoconv --format=ods %s' % TemplateFile_ log.info(u'Выполнение комманды <%s>' % cmd) os.system(cmd) return icODSReportTemplate.open(self, ods_filename) except: log.fatal(u'Ошибка открытия файла шаблона <%s>' % TemplateFile_) return None
def setOptions(self, **options): """ Установить опции сканирования в диалоговом окне. @param options: Опции. @return: True/False. """ for option_name, option_value in options.items(): if hasattr(self, option_name): try: setattr(self, option_name, option_value) log.info(u'Установлена опция <%s>. Значение <%s>' % (option_name, option_value)) except: log.warning(u'Ошибка установки опции <%s>. Значние <%s>' % (option_name, option_value)) # После установки атрибутов отобразить их в диалоговом окне self.showOptions()
def remove_file(filename): """ Удалить файл. @param filename: Имя файла. @return: True/False. """ if not os.path.exists(filename): log.warning(u'Удаление. Файл <%s> не найден' % filename) return False try: os.remove(filename) log.info(u'Файл <%s> удален' % filename) return True except: log.fatal(u'Ошибка удаления файла <%s>' % filename) return False
def _genXMLReport(self, Rep_): """ Генерация отчета и сохранение его в XML файл. @param Rep_: Полное описание шаблона отчета. @return: Возвращает имя xml файла или None в случае ошибки. """ if Rep_ is None: Rep_ = self._Rep data_rep = self.GenerateReport(Rep_) if data_rep: rep_file = icrepfile.icExcelXMLReportFile() rep_file_name = os.path.join( self.getReportDir(), '%s_report_result.xml' % str(data_rep['name'])) rep_file.write(rep_file_name, data_rep) log.info(u'Сохранение отчета в файл <%s>' % rep_file_name) return rep_file_name return None
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
def delAllFilesFilter(directory, *file_filter): """ Удаление всех файлов из папки с фильтрацией по маске файла. Удаление рекурсивное по поддиректориям. @param directory: Папка-источник. @param file_filter: Список масок файлов которые нужно удалить. Например '*_pkl.tab'. """ try: # Сначала обработка в поддиректориях subdirs = getSubDirs(directory) if subdirs: for sub_dir in subdirs: delAllFilesFilter(sub_dir, *file_filter) for file_mask in file_filter: del_files = getFilesByMask(os.path.join(directory, file_mask)) for del_file in del_files: os.remove(del_file) log.info(u'Удаление файла <%s>' % del_file) return True except: log.fatal(u'Ошибка удаления файлов %s из папки <%s>' % (str(file_filter), directory)) return None
def PreviewOffice(self, ODSFileName_): """ Открыть отчет в режиме предварительного просмотра. @param ODSFileName_: Имя ods файла, содержащего сгенерированный отчет. """ if not os.path.exists(ODSFileName_): log.warning(u'Предварительный просмотр. Файл <%s> не найден' % ODSFileName_) return pdf_filename = os.path.splitext(ODSFileName_)[0] + '.pdf' if os.path.exists(pdf_filename): try: os.remove(pdf_filename) except: log.error(u'Delete file <%s>' % pdf_filename) cmd = 'unoconv --format=pdf %s' % ODSFileName_ log.info(u'UNOCONV. Command <%s>' % cmd) os.system(cmd) cmd = 'evince %s&' % pdf_filename log.info(u'EVINCE. Command <%s>' % cmd) os.system(cmd)
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
def copyToScanDir(self, scan_filename=None, scan_dir=None, doRemove=True): """ Копировать файл - результат сканирования в папку. @param scan_filename: Результирующий файл сканирования. Если не указан, то берется файл по умолчанию. @param scan_dir: Папка сканирования. @param doRemove: Произвести перенос файла? @return: True/False """ if scan_filename is None: scan_filename = config.DEFAULT_SCAN_FILENAME if scan_dir is None: scan_dir = self.scan_dir if not os.path.exists(scan_filename): log.warning(u'Не существует файл скана <%s>' % scan_filename) return False if scan_dir: if not os.path.exists(scan_dir): try: os.makedirs(scan_dir) log.info(u'Создана папка сканирования <%s>' % scan_dir) except OSError: log.fatal(u'Ошибка создания папки сканирования <%s>' % scan_dir) return False new_filename = os.path.join(scan_dir, os.path.basename(scan_filename)) if scan_filename != new_filename: shutil.copyfile(scan_filename, new_filename) log.info(u'Копирование файла <%s> в директорию <%s>' % (scan_filename, scan_dir)) if doRemove: try: os.remove(scan_filename) log.info(u'Удален файл <%s>' % scan_filename) except: log.fatal(u'Ошибка удаления файла <%s>' % scan_filename) return True else: log.warning(u'Не определена папка сканирования') return False
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
def main(argv): """ Основная запускающая функция. @param argv: Список параметров коммандной строки. """ # Разбираем аргументы командной строки try: options, args = getopt.getopt(argv, 'h?vdl', [ 'help', 'version', 'debug', 'log', 'scanner=', 'source=', 'mode=', 'multi_scan', 'preview', 'page_size=', 'area=', 'scan_dir=', 'file_name=', 'file_type=', 'ext_cmd=', 'pack_mode', 'pack_pages=', 'glue', 'max_sheets=' ]) except getopt.error as msg: log.error(str(msg), bForcePrint=True) log.info(__doc__) sys.exit(2) # Инициализоция системы журналирования log.init(config) txt_version = '.'.join([str(ver) for ver in __version__]) cmd_options = dict() for option, arg in options: if option in ('-h', '--help', '-?'): print(__doc__) sys.exit(0) elif option in ('-v', '--version'): print('icScanner version: %s' % txt_version) sys.exit(0) elif option in ('-d', '--debug'): config.set_glob_var('DEBUG_MODE', True) elif option in ('-l', '--log'): config.set_glob_var('LOG_MODE', True) elif option in ('--scanner', ): cmd_options['scanner'] = arg elif option in ('--source', ): cmd_options['scan_source'] = arg elif option in ('--mode', ): cmd_options['scan_mode'] = arg elif option in ('--multi_scan', ): cmd_options['is_multi_scan'] = True elif option in ('--preview', ): cmd_options['is_preview'] = True elif option in ('--page_size', ): if arg in ('A4', 'a4'): cmd_options['page_size'] = scan_manager.A4_PORTRAIT_PAGE_SIZE elif arg in ('A3', 'a3'): cmd_options['page_size'] = scan_manager.A3_PORTRAIT_PAGE_SIZE else: log.warning(u'Не обрабатываемый размер страницы <%s>' % arg) elif option in ('--area', ): try: area = tuple([float(x.strip()) for x in arg.split(',')]) cmd_options['scan_area'] = area except: log.fatal( u'Ошибка парсинга параметра области сканирования <%s>' % arg) elif option in ('--scan_dir', ): cmd_options['scan_dir'] = arg elif option in ('--file_name', ): cmd_options['scan_filename'] = arg elif option in ('--file_type', ): cmd_options['scan_filetype'] = arg.lower() elif option in ('--depth', ): cmd_options['depth'] = int(arg) elif option in ('--ext_cmd', ): cmd_options['ext_scan_cmd'] = arg elif option in ('--pack_mode', ): cmd_options['pack_mode'] = True elif option in ('--pack_pages', ): cmd_options['pack_pages'] = arg elif option in ('--max_sheets', ): config.set_glob_var('DEFAULT_SCANNER_MAX_SHEETS', int(arg)) else: log.warning(u'Не обрабатываемый параметр коммандной строки <%s>' % option) # По умолчанию пакетный режим отключен+ # v if not cmd_options.get('pack_mode', False): # Внимание! Приложение создается для # управления диалоговыми окнами app = wx.PySimpleApp() # ВНИМАНИЕ! Выставить русскую локаль # Это необходимо для корректного отображения календарей, # форматов дат, времени, данных и т.п. locale = wx.Locale() locale.Init(wx.LANGUAGE_RUSSIAN) scanner_dlg.do_scan_dlg(options=cmd_options, title=u'Сканирование. icScanner ' + txt_version) app.MainLoop() else: # В пакетном режиме не используем диалоговое окно # Но в случае режима склеивания документа по частям диалоговые окна используются filenames = cmd_options.get('scan_filename', u'').split(';') pack_page_list = cmd_options.get('pack_pages', u'').split(';') n_pages = [ int(pack_page.split('/')[0]) if '/' in pack_page else int(pack_page) for pack_page in pack_page_list ] duplexes = [ bool(int(pack_page.split('/')[1])) if '/' in pack_page else False for pack_page in pack_page_list ] scan_filenames = [(filename, n_pages[i] if i < len(n_pages) else 1, duplexes[i] if i < len(duplexes) else False) for i, filename in enumerate(filenames)] scan_admin = scanner_dlg.icScanAdministrator() # Установить дополнительные опции из коммандной строки scan_admin.setExtOptions(scan_dir=cmd_options['scan_dir']) scan_admin.runScanPack(*scan_filenames)
def runScan(self): """ Запусть процесс сканирования, согласно выставленным параметрам. @return: True/False """ if self.scan_manager is None: log.warning(u'Не определен менеджер сканирования') return False if self.scanner is None: log.warning(u'Не определено устройство сканирования') return False self.scan_manager.init() self.scan_manager.open(self.scanner) # Выставить все опции options = dict() if self.scan_source: options['source'] = self.scan_source if self.scan_mode: options['mode'] = self.scan_mode if self.depth: options['depth'] = self.depth if self.page_size: options['page_width'] = self.page_size[0] options['page_height'] = self.page_size[1] else: # ВНИМАНИЕ! Если не определить размер страницы # край скана будет обрезаться # По умолчанию A4 портретной ориентации options['page_width'] = 210.0 options['page_height'] = 297.0 if self.scan_area: options['tl_x'] = self.scan_area[0] options['tl_y'] = self.scan_area[1] if self.scan_area and self.page_size: options['br_x'] = self.page_size[0] - self.scan_area[2] options['br_y'] = self.page_size[1] - self.scan_area[3] else: # ВНИМАНИЕ! Если не определить размер страницы # край скана будет обрезаться # По умолчанию A4 портретной ориентации options['br_x'] = 210.0 options['br_y'] = 297.0 self.scan_manager.setScanOptions(**options) # Определение имени файла сканирования scan_filename = os.path.join( ic_file.getHomeDir(), config.PROFILE_DIRNAME, self.scan_filename + '.' + self.scan_filetype ) if self.scan_filename else config.DEFAULT_SCAN_FILENAME if os.path.exists(scan_filename): # Удалить старый файл сканирования try: os.remove(scan_filename) log.info(u'Удален файл <%s>' % scan_filename) except OSError: log.fatal(u'Ошибка удаления файла <%s>' % scan_filename) log.debug(u'Сканирование в файл <%s>' % scan_filename) try: if not self.is_multi_scan: result = self.scan_manager.singleScan(scan_filename) else: result = self.scan_manager.multiScan(scan_filename) if not result: # Какая-то ошибка сканирования dlg.getErrBox( u'ОШИБКА', u'Ошибка сканирования. Проверте листы в лотке сканера') return False if self.scan_dir: self.copyToScanDir(scan_filename, self.scan_dir) if self.is_preview: self.previewScanFile(scan_filename) return True except: log.fatal(u'Ошибка сканирования') return False
def runScanPack(self, *scan_filenames): """ Запусть процесс сканирования в режиме пакетной обработки, согласно выставленным параметрам. @param scan_filenames: Имена файлов скана с указанием количества листов и признака 2-стороннего сканирования. Например: (scan001, 3, True), (scan002, 1, False), (scn003, 2, True), ... @return: True/False """ if self.scan_manager is None: log.warning(u'Не определен менеджер сканирования') return False if self.scanner is None: log.warning(u'Не определено устройство сканирования') return False self.scan_manager.init() self.scan_manager.open(self.scanner) # Выставить все опции options = dict() if self.scan_source: options['source'] = self.scan_source if self.scan_mode: options['mode'] = self.scan_mode if self.depth: options['depth'] = self.depth if self.page_size: options['page_width'] = self.page_size[0] options['page_height'] = self.page_size[1] else: # ВНИМАНИЕ! Если не определить размер страницы # край скана будет обрезаться # По умолчанию A4 портретной ориентации options['page_width'] = 210.0 options['page_height'] = 297.0 if self.scan_area: options['tl_x'] = self.scan_area[0] options['tl_y'] = self.scan_area[1] if self.scan_area and self.page_size: options['br_x'] = self.page_size[0] - self.scan_area[2] options['br_y'] = self.page_size[1] - self.scan_area[3] else: # ВНИМАНИЕ! Если не определить размер страницы # край скана будет обрезаться # По умолчанию A4 портретной ориентации options['br_x'] = 210.0 options['br_y'] = 297.0 self.scan_manager.setScanOptions(**options) scans = [(os.path.join(ic_file.getHomeDir(), config.PROFILE_DIRNAME, scan_filename + '.' + self.scan_filetype) if scan_filename else config.DEFAULT_SCAN_FILENAME, int(n_pages), bool(is_duplex)) for scan_filename, n_pages, is_duplex in scan_filenames] for scan_filename, n_pages, is_duplex in scans: full_scan_filename = os.path.join( os.environ.get('HOME', '/home/user'), config.PROFILE_DIRNAME, scan_filename) if os.path.exists(full_scan_filename): # Удалить старый файл сканирования try: os.remove(full_scan_filename) log.info(u'Удален ранее отсканированный файл <%s>' % full_scan_filename) except OSError: log.fatal(u'Ошибка удаления файла <%s>' % full_scan_filename) try: scan_filenames = self.scan_manager.scan_pack(scan_filenames=scans) # Перенос отсканированных файлов в результирующую папку if self.scan_dir and os.path.exists(self.scan_dir): for scan_filename in scan_filenames: if scan_filename and os.path.exists(scan_filename): log.debug( u'Перенос файла <%s> в результирующую папку <%s>' % (scan_filename, self.scan_dir)) self.copyToScanDir(scan_filename, self.scan_dir) else: log.warning( u'Не определен результирующий файл сканирования') else: log.warning(u'Не определена результирующая папка сканирования') return True except: log.fatal(u'Ошибка сканирования в режиме пакетной обработки') return False
def ic_eval(expr, logType=-1, evalSpace=None, msg='', globSpace=None, compileKey=None): """ Функция выполняет предобработку вычисляемого выражения, вычисляет с использование стандартной, функции eval(...), а также обрабатывает исключения. В качестве локального пространства имен используется словарь evalSpace. В качестве глобального пространства имен берется словарь возвращаемый функцией globals(). Если передается уникальный идентификатор выполняемого выражения, то функция откомпилирует выражение и сохранит в буфере для последующего использования. @type expr: C{string} @param expr: Вычисляемое выражение. @type logType: C{int} @param logType: Тип лога (0 - консоль, 1- файл, 2- окно лога) @param evalSpace: Пространство имен, необходимых для вычисления выражения @type evalSpace: C{dictionary} @type msg: C{string} @param msg: Сообщение, которое дополнительно выводится при ошибке. Если параметр None, то сообщение об ошибке не выводится. @param globalSpace: Глобальное пространство имен. @type globalSpace: C{dictionary} @type compileKey: C{int} @param compileKey: Идентификатор компилированного выражения. """ # В режиме отладки если определены точки остонова в выражении, # вызовы перенаправляются в служебный модуль, в котором соответствующие # вычисляемые выражения представляются функциями с уникальным именами. Такой # прием позволяет воспользоваться стандартым отладчиком pdb.py. if (evalSpace and compileKey and '__runtime_mode' in evalSpace and evalSpace['__runtime_mode'] == IC_RUNTIME_MODE_DEBUG): try: subsys = resource.icGetResPath().split('/')[-1] MyExec('import %s.debug as debugModul' % subsys) newSpace = eval('debugModul.f_%s(_esp)' % compileKey, globSpace, evalSpace) evalSpace.update(newSpace) if '_resultEval' in evalSpace: ret = evalSpace['_resultEval'] log.info(u'EXECUTE ATTRIBUTE DEBUG FUNCTION f%s(_esp)' % compileKey) return True, ret except: pass if globSpace is None: globSpace = globals() # Отсекаем по типу выражения if not isinstance(expr, str): return 0, None # Отсекаем пустые строки if expr == '': return 1, '' bSuccess, ret = 1, None evalSpace['_resultEval'] = None ##################################################### # Если выражение уже откомпилировано, то пытаемся # его выполнить bCompile = False if compileKey: try: s = GetCompileString(compileKey, 'eval') if s: ret = eval(s, globSpace, evalSpace) bCompile = True else: s = GetCompileString(compileKey, 'exec') if s: exec(s, globSpace, evalSpace) bCompile = True if '_resultEval' in evalSpace: ret = evalSpace['_resultEval'] except: if msg is not None: ret = u'EXEC[EVAL] EXCEPTION IN %s: exec(%s)' % (msg, expr) log.fatal(ret) return 0, ret ############################################# # Компилируем и выполняем, если выражение еще # не компилировалось if not bCompile: try: if expr[0] == '@': expr = expr[1:] expr = expr.replace('\r\n', '\n').replace('\r', '\n') s = compile(expr, '<string>', 'eval') ret = eval(s, globSpace, evalSpace) if compileKey: SetCompileString(compileKey, 'eval', s) except: try: expr += '\n' s = compile(expr, '<string>', 'exec') exec(s, globSpace, evalSpace) if compileKey: SetCompileString(compileKey, 'exec', s) if '_resultEval' in evalSpace: ret = evalSpace['_resultEval'] except: bSuccess = 0 if msg is not None: ret = u'EXEC EXCEPTION IN %s: exec(%s)' % (msg, expr) log.fatal(ret) return bSuccess, ret
def readAndEvalFile(filename, dictRpl={}, bRefresh=False): """ Функция читает файл и выполняет его. @type filename: C{string} @param filename: Имя ресурсного файла. @type dictRpl: C{dictionary} @param dictRpl: Словарь замен. @type bRefresh: C{bool} @param bRefresh: Признак того, что файл надо перечитать даже если он буферезирован. """ obj = None try: # Проверяем есть ли в буфферном файле такой объект, если есть, то его и возвращаем if not bRefresh and filename in Buff_readAndEvalFile: return Buff_readAndEvalFile[filename] nm = os.path.basename(filename) pt = nm.find('.') if pt >= 0: filepcl = os.path.dirname( filename) + '/' + nm[:pt] + '_pkl' + nm[pt:] else: filepcl = os.path.dirname(filename) + '/' + nm + '_pkl' # Проверяем нужно ли компилировать данную структуру по следующим признакам: # наличие скомпилированного файла, по времени последней модификации. try: if (os.path.isfile(filepcl) and not os.path.isfile(filename)) or \ (os.path.getmtime(filename) < os.path.getmtime(filepcl)): # Пытаеся прочитать сохраненную структуру если # время последней модификации текстового представления меньше, # времени последней модификации транслированного варианта. fpcl = None try: fpcl = open(filepcl) obj = cPickle.load(fpcl) fpcl.close() # Сохраняем объект в буфере Buff_readAndEvalFile[filename] = obj log.info(u'Load from <%s>' % filepcl) return obj except: if fpcl: fpcl.close() except: pass # Пытаемся прочитать cPickle, если не удается считаем, что в файле # хранится текст. Читаем его, выполняем, полученный объект сохраняем # на диске для последующего использования if os.path.isfile(filename): try: fpcl = open(filename) obj = cPickle.load(fpcl) fpcl.close() # Сохраняем объект в буфере Buff_readAndEvalFile[filename] = obj log.info(u'Load file <cPickle Format> <%s>' % filename) return obj except: log.fatal(u'<Non cPickle Format. Try to compile text>') # Открываем текстовое представление, если его нет, то создаем его f = open(filename) txt = f.read().replace('\r\n', '\n') f.close() for key in dictRpl: txt = txt.replace(key, dictRpl[key]) # Выполняем obj = eval(txt) # Сохраняем объект в буфере Buff_readAndEvalFile[filename] = obj # Сохраняем транслированный вариант fpcl = open(filepcl, 'w') log.info(u'create: <%s>' % filepcl) cPickle.dump(obj, fpcl, PICKLE_PROTOCOL) fpcl.close() except IOError: log.fatal(u'Ошибка открытия файла <%s>' % filename) obj = None except: log.fatal(u'Ошибка при трансляции файла <%s>' % filename) obj = None return obj
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