From 8075d0650daf6ecd2c7daa7c7f0e3ef2b15ca9c5 Mon Sep 17 00:00:00 2001 From: DocenkoG Date: Wed, 7 Feb 2018 00:36:48 +0300 Subject: [PATCH] =?UTF-8?q?requests,=20=D0=92=D1=81=D0=B5=20=D0=B2=20?= =?UTF-8?q?=D0=BE=D0=B4=D0=BD=D0=BE=D0=BC=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B5?= =?UTF-8?q?,=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B8=D0=BA=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + _readme.txt | 9 -- abn.cfg | 74 ------------ abn.py | 287 +++++++++++++++++++++++++++++++++++++++++----- abn_converter.py | 248 --------------------------------------- abn_downLoader.py | 90 --------------- abn_unittest.py | 123 -------------------- cfg_abn.cfg | 33 ++++++ logging.cfg | 6 +- price_tools.py | 175 ++++++++++++++++++++++++++++ 10 files changed, 477 insertions(+), 573 deletions(-) delete mode 100644 _readme.txt delete mode 100644 abn.cfg mode change 100644 => 100755 abn.py delete mode 100644 abn_converter.py delete mode 100644 abn_downLoader.py delete mode 100644 abn_unittest.py create mode 100644 cfg_abn.cfg create mode 100755 price_tools.py diff --git a/.gitignore b/.gitignore index 9ae7f0c..72d425f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,16 @@ *.xls *.xlsx +*.xlsb *.csv *.1 *.2 *.3 *.4 *.5 +*.zip +*.sublime-project +*.sublime-workspace +private* # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/_readme.txt b/_readme.txt deleted file mode 100644 index dd338b1..0000000 --- a/_readme.txt +++ /dev/null @@ -1,9 +0,0 @@ -Соответствие полей: -В МНП (поля в МНП = поля в прайсе поставщика), [ххх] означают, что нужно вставить текст ххх: -Код = Код -Бренд = Бренд -Наименование = Наименование -Описание = Наименование -Закупка = Цена клиента -Продажа = нет -В наличии = Свободный остаток \ No newline at end of file diff --git a/abn.cfg b/abn.cfg deleted file mode 100644 index c58f452..0000000 --- a/abn.cfg +++ /dev/null @@ -1,74 +0,0 @@ -[input] -Filename_in = new_abn.xlsx -sheetname = - -Filename_out = abn.csv -loglevel = DEBUG - # - # - # !!! !!! lowercase ! - # - # -[cols_in] # . - - = 1 - = - = -3 = -4 = - = 8 - = - = 2 - = - = 7 -1 = - = 4 - = - = - = - = - = -url = - = -1 = -2 = -3 = - = - = - - -[cols_out] # , - - = - = - = - = - = -1 = - = - = - = - = - = - = - = -url = - = - = -.. = -. = - - -[grp_properties] # , - . - = 8 - = -GrpFonti = 6 -SubGrpFonti = 6 -BrandFonti = 8 -HeaderFonti = 7 -RegularFonti = 9 -regularfontsize = 10 -HeaderFontSize = 7 -GrpFontSize = -SubGrpFontSize = -GrpFontBold = -SubGrpFontBold = -GrpBackgroundColor = -SubGrpBackgroundColor = diff --git a/abn.py b/abn.py old mode 100644 new mode 100755 index c9771f7..34d9e4d --- a/abn.py +++ b/abn.py @@ -3,16 +3,238 @@ import os.path import logging import logging.config -import io import sys import configparser import time -import abn_downloader -import abn_converter import shutil +import openpyxl # Для .xlsx +#import xlrd # для .xls +from price_tools import getCellXlsx, getCell, quoted, dump_cell, currencyType, openX, sheetByName +import csv +import requests, lxml.html -global log -global myname + + +def getXlsString(sh, i, in_columns_j): + impValues = {} + for item in in_columns_j.keys() : + j = in_columns_j[item] + if item in ('закупка','продажа','цена со скидкой','цена_') : + if getCell(row=i, col=j, isDigit='N', sheet=sh) == '' : # .find('Звоните') >=0 : + impValues[item] = '0.1' + else : + impValues[item] = getCell(row=i, col=j, isDigit='Y', sheet=sh) + #print(sh, i, sh.cell( row=i, column=j).value, sh.cell(row=i, column=j).number_format, currencyType(sh, i, j)) + elif item == 'валюта_по_формату': + impValues[item] = currencyType(row=i, col=j, sheet=sh) + else: + impValues[item] = getCell(row=i, col=j, isDigit='N', sheet=sh) + return impValues + + + +def getXlsxString(sh, i, in_columns_j): + impValues = {} + for item in in_columns_j.keys() : + j = in_columns_j[item] + if item in ('закупка','продажа','цена','цена1') : + if getCellXlsx(row=i, col=j, isDigit='N', sheet=sh).find('Звоните') >=0 : + impValues[item] = '0.1' + else : + impValues[item] = getCellXlsx(row=i, col=j, isDigit='Y', sheet=sh) + #print(sh, i, sh.cell( row=i, column=j).value, sh.cell(row=i, column=j).number_format, currencyType(sh, i, j)) + elif item == 'валюта_по_формату': + impValues[item] = currencyType(row=i, col=j, sheet=sh) + else: + impValues[item] = getCellXlsx(row=i, col=j, isDigit='N', sheet=sh) + return impValues + + + +def convert_excel2csv(cfg): + csvFName = cfg.get('basic','filename_out') + priceFName= cfg.get('basic','filename_in') + sheetName = cfg.get('basic','sheetname') + + log.debug('Reading file ' + priceFName ) + sheet = sheetByName(fileName = priceFName, sheetName = sheetName) + if not sheet : + log.error("Нет листа "+sheetName+" в файле "+ priceFName) + return False + log.debug("Sheet "+sheetName) + out_cols = cfg.options("cols_out") + in_cols = cfg.options("cols_in") + out_template = {} + for vName in out_cols : + out_template[vName] = cfg.get("cols_out", vName) + in_cols_j = {} + for vName in in_cols : + in_cols_j[vName] = cfg.getint("cols_in", vName) + #brands, discount = config_read(cfgFName, 'discount') + #for k in discount.keys(): + # discount[k] = (100 - int(discount[k]))/100 + #print(discount) + + outFile = open( csvFName, 'w', newline='', encoding='CP1251', errors='replace') + csvWriter = csv.DictWriter(outFile, fieldnames=out_cols ) + csvWriter.writeheader() + + ''' # Блок проверки свойств для распознавания групп XLSX + for i in range(2393, 2397): + i_last = i + ccc = sheet.cell( row=i, column=in_cols_j['группа'] ) + print(i, ccc.value) + print(ccc.font.name, ccc.font.sz, ccc.font.b, ccc.font.i, ccc.font.color.rgb, '------', ccc.fill.fgColor.rgb) + print('------') + ''' + ''' # Блок проверки свойств для распознавания групп XLS + for i in range(0, 75): + xfx = sheet.cell_xf_index(i, 0) + xf = book.xf_list[xfx] + bgci = xf.background.pattern_colour_index + fonti = xf.font_index + ccc = sheet.cell(i, 0) + if ccc.value == None : + print (i, colSGrp, 'Пусто!!!') + continue + # Атрибуты шрифта для настройки конфига + font = book.font_list[fonti] + print( '---------------------- Строка', i, '-----------------------', sheet.cell(i, 0).value) + print( 'background_colour_index=',bgci) + print( 'fonti=', fonti, ' xf.alignment.indent_level=', xf.alignment.indent_level) + print( 'bold=', font.bold) + print( 'weight=', font.weight) + print( 'height=', font.height) + print( 'italic=', font.italic) + print( 'colour_index=', font.colour_index ) + print( 'name=', font.name) + return + ''' + + recOut ={} +# for i in range(1, sheet.nrows) : # xls + for i in range(1, sheet.max_row +1) : # xlsx + i_last = i + try: + impValues = getXlsxString(sheet, i, in_cols_j) + #impValues = getXlsString(sheet, i, in_cols_j) + #print( impValues['закупка']) + if impValues['цена1']=='0': # (ccc.value == None) or (ccc2.value == None) : # Пустая строка + pass + #print( 'Пустая строка. i=',i, impValues ) + elif impValues['код_'] == '' or impValues['код_'] == 'Арт.' : # Пустая строка + print (i, 'Пусто!!!') + continue + else : # Обычная строка + for outColName in out_template.keys() : + shablon = out_template[outColName] + for key in impValues.keys(): + if shablon.find(key) >= 0 : + shablon = shablon.replace(key, impValues[key]) + if (outColName == 'закупка') and ('*' in shablon) : + vvvv = float( shablon[ :shablon.find('*') ] ) + #print(vvvv) + shablon = str( float(vvvv) * brand_koeft ) + recOut[outColName] = shablon + + csvWriter.writerow(recOut) + + except Exception as e: + print(e) + if str(e) == "'NoneType' object has no attribute 'rgb'": + pass + else: + log.debug('Exception: <' + str(e) + '> при обработке строки ' + str(i) +'.' ) + + log.info('Обработано ' +str(i_last)+ ' строк.') + outFile.close() + + + +def download( cfg ): + retCode = False + filename_new= cfg.get('download','filename_new') + filename_old= cfg.get('download','filename_old') + login = cfg.get('download','login' ) + password = cfg.get('download','password' ) + url_lk = cfg.get('download','url_lk' ) + url_file = cfg.get('download','url_file' ) + headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.0; rv:14.0) Gecko/20100101 Firefox/14.0.1', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Language':'ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3', + 'Accept-Encoding':'gzip, deflate', + 'Connection':'keep-alive', + 'DNT':'1' + } + + try: + s = requests.Session() + r = s.get(url_lk, headers = headers) + #print(r.text) # посмотреть имена полей формы + page = lxml.html.fromstring(r.text) + form = page.forms[0] + #print(form) + form.fields['login'] = login + form.fields['pass'] = password + r = s.post(url_lk+ form.action, data=form.form_values()) + #print('<<<',r.text,'>>>') + print(' ==================================================') + + log.debug('Авторизация на %s --- code=%d', url_lk, r.status_code) + r = s.get(url_file) + log.debug('Загрузка файла %16d bytes --- code=%d', len(r.content), r.status_code) + retCode = True + ''' + s = requests.Session() + r = s.get(url_lk, auth=(login,password)) # ,headers = headers (И без него сработало, но где-то может понадобиться) + page = lxml.html.fromstring(r.text) + # data = {'USER_LOGIN':login, 'USER_PASSWORD':password}) + log.debug('Авторизация на %s --- code=%d', url_lk, r.status_code) + r = s.get(url_file) + log.debug('Загрузка файла %24d bytes --- code=%d', len(r.content), r.status_code) + retCode = True + ''' + except Exception as e: + log.debug('Exception: <' + str(e) + '>') + + if os.path.exists( filename_new) and os.path.exists( filename_old): + os.remove( filename_old) + os.rename( filename_new, filename_old) + if os.path.exists( filename_new) : + os.rename( filename_new, filename_old) + f2 = open(filename_new, 'wb') #Теперь записываем файл + f2.write(r.content) + f2.close() + return retCode + + + +def config_read( cfgFName ): + cfg = configparser.ConfigParser(inline_comment_prefixes=('#')) + if os.path.exists('private.cfg'): + cfg.read('private.cfg', encoding='utf-8') + if os.path.exists(cfgFName): + cfg.read( cfgFName, encoding='utf-8') + else: + log.debug('Нет файла конфигурации '+cfgFName) + return cfg + + + +def is_file_fresh(fileName, qty_days): + qty_seconds = qty_days *24*60*60 + if os.path.exists( fileName): + price_datetime = os.path.getmtime(fileName) + else: + log.error('Не найден файл '+ fileName) + return False + + if price_datetime+qty_seconds < time.time() : + file_age = round((time.time()-price_datetime)/24/60/60) + log.error('Файл "'+fileName+'" устарел! Допустимый период '+ str(qty_days)+' дней, а ему ' + str(file_age) ) + return False + else: + return True @@ -20,32 +242,45 @@ def make_loger(): global log logging.config.fileConfig('logging.cfg') log = logging.getLogger('logFile') -# log.debug('test debug message from abn') -# log.info( 'test info message from abn') -# log.warn( 'test warn message from abn') -# log.error('test error message from abn') -# log.critical('test critical message') -def main( ): - global myname - global mydir - +def processing(cfgFName): + log.info('----------------------- Processing '+cfgFName ) + cfg = config_read(cfgFName) + filename_out = cfg.get('basic','filename_out') + filename_in = cfg.get('basic','filename_in') + filename_new = cfg.get('download','filename_new') + + if cfg.has_section('download'): + result = download(cfg) + if is_file_fresh( filename_new, int(cfg.get('basic','срок годности'))): + #os.system( 'brullov_converter_xlsx.xlsm') + #convert_csv2csv(cfg) + convert_excel2csv(cfg) + folderName = os.path.basename(os.getcwd()) + if os.name == 'nt' : + if os.path.exists(filename_out) : shutil.copy2(filename_out , 'c://AV_PROM/prices/' + folderName +'/'+filename_out) + if os.path.exists('python.log') : shutil.copy2('python.log', 'c://AV_PROM/prices/' + folderName +'/python.log') + if os.path.exists('python.log.1'): shutil.copy2('python.log.1','c://AV_PROM/prices/' + folderName +'/python.log.1') + + + +def main( dealerName): + """ Обработка прайсов выполняется согласно файлов конфигурации. + Для этого в текущей папке должны быть файлы конфигурации, описывающие + свойства файла и правила обработки. По одному конфигу на каждый + прайс или раздел прайса со своими правилами обработки + """ make_loger() - log.info('------------ '+myname +' ------------') + log.info(' '+dealerName ) + for cfgFName in os.listdir("."): + if cfgFName.startswith("cfg") and cfgFName.endswith(".cfg"): + processing(cfgFName) - if abn_downloader.download( myname ) : - abn_converter.convert2csv( myname ) - shutil.copy2( myname + '.csv', 'c://AV_PROM/prices/' + myname +'/'+ myname + '.csv') - shutil.copy2( 'python.log', 'c://AV_PROM/prices/' + myname +'/python.log') if __name__ == '__main__': - global myname - global mydir - myname = os.path.basename(os.path.splitext(sys.argv[0])[0]) + myName = os.path.basename(os.path.splitext(sys.argv[0])[0]) mydir = os.path.dirname (sys.argv[0]) - if ('' != mydir) : os.chdir(mydir) - main( ) - -#os.system(r'c:\prices\_scripts\remove_tmp_profiles.cmd') \ No newline at end of file + print(mydir, myName) + main( myName) diff --git a/abn_converter.py b/abn_converter.py deleted file mode 100644 index 76a6a8d..0000000 --- a/abn_converter.py +++ /dev/null @@ -1,248 +0,0 @@ -# -*- coding: UTF-8 -*- -import os -import os.path -import logging -import logging.config -import io -import sys -import configparser -import time -import openpyxl # Для .xlsx -# import xlrd # для .xls - - -def make_loger(): - global log - logging.config.fileConfig('logging.cfg') - log = logging.getLogger('logFile') - - - -def convert2csv( myname ): - global log - global SheetName - global FilenameIn - global FilenameOut - global out_columns_names - global out_columns_j - global in_columns_j - global colGrp - global colSGrp - global GrpFonti - global BrandFonti - global SubGrpFonti - global HeaderFonti - global HeaderFontSize - global RegularFontSize - global SubGrpBackgroundColor - global GrpBackgroundColor - global strHeader - global SubGrpFontSize - global GrpFontSize - make_loger() - log.debug('Begin ' + __name__ + 'convert2csv') - - # Прочитать конфигурацию из файла - ff = config_read( myname ) - log.debug('Открываю файл '+ FilenameIn) - book = openpyxl.load_workbook(filename = FilenameIn, read_only=False, keep_vba=False, data_only=False) -# book = xlrd.open_workbook( FilenameIn.encode('cp1251'), formatting_info=True) -# book = xlrd.open_workbook( os.path.join( mydir, FilenameIn.encode('cp1251')), formatting_info=True) - - log.debug('Устанавливаю страницу ' + SheetName ) -# sh = book.sheet_by_name( SheetName ) # xls - sh = book[SheetName] # xlsx - - - ssss = [] - line_qty = 0 - log.debug('На странице %d строк' % book[SheetName].max_row) - # цикл по строкам файла - for i in range(book[SheetName].min_row, book[SheetName].max_row+1) : - i_last = i - try: - ccc = sh.cell(row=i, column=colGrp) - if ccc.value == None : - print (i, colGrp, 'Пусто!!!') - continue - ''' # Атрибуты шрифта для настройки конфига - print( 'Строка', i, ccc.value,) - print( 'font=', ccc.font.name,) - print( 'bold=', ccc.font.bold,) - print( 'italic=', ccc.font.italic,) - print( 'size=', ccc.font.size) - #print( 'colour=', ccc.font.color.rgb) - print( 'background=',ccc.fill.fill_type) - print( 'backgroundColor1=', ccc.fill.start_color) - print( 'backgroundColor2=', ccc.fill.end_color) - print( 'Строка', i, 'столбец', colGrp, 'значение', ccc.value) - continue - ''' - -# if GrpFontSize <= ccc.font.size : # Группа -# grpName = quoted(sh.cell(row=i, column=colGrp).value) -# subGrpName = '' -# print('группа', grpName) - -# elif SubGrpFontSize == ccc.font.size : # Подгруппа -# subGrpName = quoted(sh.cell(row=i,column=colSGrp).value) - - if HeaderFontSize == ccc.font.size : # Заголовок таблицы - pass - - elif (None == sh.cell(row=i, column=in_columns_j['закупка']).value) : # Пустая строка - pass - print( 'Пустая строка:', sh.cell(row=i, column=in_columns_j['закупка']).value ) - - elif RegularFontSize == ccc.font.size : # Информационная строка - ccc = sh.cell(row=i, column=out_columns_j['код']) - code = ccc.value - sss = [] # формируемая строка для вывода в файл - for strname in out_columns_names : - if strname in out_columns_j : - # берем значение из соответствующей ячейки файла - j = out_columns_j[strname] - ccc = sh.cell(row=i, column=j) - cellType = ccc.data_type - cellValue = ccc.value -# print (cellType, cellValue) - if cellValue == None : - ss = '' - elif cellType in ('n') : # numeric - if int(cellValue) == cellValue: - ss = str(int(cellValue)) - else : - ss = str(cellValue) - elif strname in ('закупка','продажа','цена1', 'цена2') : - ss = '0' - elif cellType == 's' : - ss = quoted(cellValue ) - else: - ss = '' - else : - # вычисляемое поле - s1 = sh.cell(row=i, column=in_columns_j['бренд']).value - s2 = sh.cell(row=i, column=in_columns_j['код']).value - s3 = sh.cell(row=i, column=in_columns_j['примечание']).value - ss = quoted(s1 + ' ' + s2 + ' ' + s3) - pass - sss.append(ss) - -# sss.append(grpName) -# sss.append(subGrpName) - ssss.append(','.join(sss)) - else : - log.debug('Нераспознана строка: <' + sh.cell(row=i, column=out_columns_j['код']).value + '>' ) - except Exception as e: - log.debug('Exception: <' + str(e) + '> при обработке строки ' + str(i) +'<' + '>' ) - raise e - - - f2 = open( FilenameOut, 'w', encoding='cp1251') - f2.write(strHeader + ',\n') - data = ',\n'.join(ssss) +',' - dddd = data.encode(encoding='cp1251', errors='replace') - data = dddd.decode(encoding='cp1251') - f2.write(data) - f2.close() - - - -def config_read( myname ): - global log - global SheetName - global FilenameIn - global FilenameOut - global out_columns_names - global out_columns_j - global in_columns_j - global colGrp - global colSGrp - global GrpFonti - global SubGrpFonti - global BrandFonti - global HeaderFonti - global HeaderFontSize - global RegularFontSize - global SubGrpBackgroundColor - global GrpBackgroundColor - global strHeader - global SubGrpFontSize - global GrpFontSize - - cfgFName = myname + '.cfg' - log.debug('Begin config_read ' + cfgFName ) - - config = configparser.ConfigParser() - if os.path.exists(cfgFName): config.read( cfgFName) - else : log.debug('Не найден файл конфигурации.') - - # в разделе [cols_in] находится список интересующих нас колонок и номера столбцов исходного файла - in_columns_names = config.options('cols_in') - in_columns_j = {} - for vName in in_columns_names : - if ('' != config.get('cols_in', vName)) : - in_columns_j[vName] = config.getint('cols_in', vName) - - # По разделу [cols_out] формируем перечень выводимых колонок и строку заголовка результирующего CSV файла - temp_list = config.options('cols_out') - temp_list.sort() - - out_columns_names = [] - for vName in temp_list : - if ('' != config.get('cols_out', vName)) : - out_columns_names.append(vName) - - out_columns_j = {} - for vName in out_columns_names : - tName = config.get('cols_out', vName) - if tName in in_columns_j : - out_columns_j[vName] = in_columns_j[tName] - print('-----------------------------------') - for vName in out_columns_j : - print(vName, '\t', out_columns_j[vName]) - print('-----------------------------------') - strHeader = ','.join(out_columns_names) # +',бренд,группа,подгруппа' - print('HEAD =', strHeader) - - # считываем имена файлов и имя листа - FilenameIn = config.get('input','Filename_in' ) - SheetName = config.get('input','SheetName' ) - FilenameOut = config.get('input','Filename_out') - print('SHEET=', SheetName) - - # считываем признаки группы и подгруппы - if ('' != config.get('grp_properties', 'группа')) : - colGrp = config.getint('grp_properties', 'группа') - if ('' != config.get('grp_properties', 'подгруппа')) : - colSGrp = config.getint('grp_properties', 'подгруппа') - if ('' != config.get('grp_properties', 'GrpFonti')) : - GrpFonti = config.getint('grp_properties', 'GrpFonti') - if ('' != config.get('grp_properties', 'SubGrpFonti')) : - SubGrpFonti = config.getint('grp_properties','SubGrpFonti') - if ('' != config.get('grp_properties', 'BrandFonti')) : - BrandFonti = config.getint('grp_properties', 'BrandFonti') - if ('' != config.get('grp_properties', 'HeaderFonti')) : - HeaderFonti = config.getint('grp_properties','HeaderFonti') - if ('' != config.get('grp_properties', 'HeaderFontSize')) : - HeaderFontSize = config.getint('grp_properties','HeaderFontSize') - if ('' != config.get('grp_properties', 'RegularFontSize')) : - RegularFontSize = config.getint('grp_properties','RegularFontSize') - if ('' != config.get('grp_properties', 'SubGrpFontSize')): - SubGrpFontSize = config.getint('grp_properties','SubGrpFontSize') - if ('' != config.get('grp_properties', 'GrpFontSize')) : - GrpFontSize = config.getint('grp_properties', 'GrpFontSize') - if ('' != config.get('grp_properties', 'SubGrpBackgroundColor')) : - SubGrpBackgroundColor= config.getint('grp_properties','SubGrpBackgroundColor') - if ('' != config.get('grp_properties', 'GrpBackgroundColor')) : - GrpBackgroundColor = config.getint('grp_properties', 'GrpBackgroundColor') - subgrpfontbold = config.get('grp_properties','subgrpfontbold') - grpfontbold = config.get('grp_properties', 'grpfontbold') - return FilenameIn - - - -def quoted(sss): - if ((',' in sss) or ('"' in sss) or ('\n' in sss)) and not(sss[0]=='"' and sss[-1]=='"') : - sss = '"'+sss.replace('"','""')+'"' - return sss diff --git a/abn_downLoader.py b/abn_downLoader.py deleted file mode 100644 index 4a146b6..0000000 --- a/abn_downLoader.py +++ /dev/null @@ -1,90 +0,0 @@ -import os -import logging -import logging.config -import time -import shutil - - - -def make_loger(): - global log - logging.config.fileConfig('logging.cfg') - log = logging.getLogger('logFile') -# log.debug('test debug message from downLoader') -# log.info( 'test info message from downLoader') -# log.warn( 'test warn message from downLoader') -# log.error('test error message from downLoader') -# log.critical('test critical message') - - - - -def download( myname ): - global log - pathDwnld = './tmp' - pathPython2 = 'c:/Python27/python.exe' - make_loger() - retCode = False - log.debug( 'Begin ' + __name__ + ' downLoader' ) - fUnitName = os.path.join( myname +'_unittest.py') - if not os.path.exists(fUnitName): - log.debug( 'Отсутствует юниттест для загрузки прайса ' + fUnitName) - else: - dir_befo_download = set(os.listdir(pathDwnld)) - os.system( fUnitName) # Вызов unittest'a - dir_afte_download = set(os.listdir(pathDwnld)) - new_files = list( dir_afte_download.difference(dir_befo_download)) - if len(new_files) == 1 : - new_file = new_files[0] # загружен ровно один файл. - new_ext = os.path.splitext(new_file)[-1] - new_name = os.path.splitext(new_file)[0] - DnewFile = os.path.join( pathDwnld,new_file) - new_file_date = os.path.getmtime(DnewFile) - log.info( 'Скачанный файл ' +DnewFile + ' имеет дату ' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(new_file_date) ) ) - if new_ext == '.zip': # Архив. Обработка не завершена - log.debug( 'Zip-архив. Разархивируем.') - work_dir = os.getcwd() - os.chdir( os.path.join( pathDwnld )) - dir_befo_download = set(os.listdir(os.getcwd())) - os.system('unzip -oj ' + DnewFile) - os.remove(new_file) - dir_afte_download = set(os.listdir(os.getcwd())) - new_files = list( dir_afte_download.difference(dir_befo_download)) - if len(new_files) == 1 : - new_file = new_files[0] # разархивирован ровно один файл. - new_ext = os.path.splitext(new_file)[-1] - DnewFile = os.path.join( os.getcwd(),new_file) - new_file_date = os.path.getmtime(DnewFile) - print(new) - log.debug( 'Файл из архива ' +DnewFile + ' имеет дату ' + str(datetime.fromtimestamp(new_file_date) ) ) - DnewPrice = DnewFile - elif len(new_files) >1 : - log.debug( 'В архиве не единственный файл. Надо разбираться.') - DnewPrice = "dummy" - else: - log.debug( 'Нет новых файлов после разархивации. Загляни в папку юниттеста поставщика.') - DnewPrice = "dummy" - os.chdir(work_dir) - elif new_ext == '.csv' or new_ext == '.htm' or new_ext == '.xls' or new_ext == '.xlsx': - DnewPrice = DnewFile # Имя скачанного прайса - if DnewPrice != "dummy" : - FoldName = 'old_' + myname + new_ext # Предыдущая копия прайса, для сравнения даты - FnewName = 'new_' + myname + new_ext # Файл, с которым работает макрос - if (not os.path.exists( FnewName)) or new_file_date>os.path.getmtime(FnewName) : - log.debug( 'Предыдущего прайса нет или он устарел. Копируем новый.' ) - if os.path.exists( FoldName): os.remove( FoldName) - if os.path.exists( FnewName): os.rename( FnewName, FoldName) - shutil.copy2(DnewPrice, FnewName) - retCode = True - else: - log.debug( 'Предыдущий прайс не старый, копироавать не надо.' ) - # Убрать скачанные файлы - if os.path.exists(DnewPrice): os.remove(DnewPrice) - - - elif len(new_files) == 0 : - log.debug( 'Не удалось скачать файл прайса ') - else: - log.debug( 'Скачалось несколько файлов. Надо разбираться ...') - - return retCode diff --git a/abn_unittest.py b/abn_unittest.py deleted file mode 100644 index 65e3f91..0000000 --- a/abn_unittest.py +++ /dev/null @@ -1,123 +0,0 @@ -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.common.keys import Keys -from selenium.webdriver.support.ui import Select -from selenium.common.exceptions import NoSuchElementException -from selenium.common.exceptions import NoAlertPresentException -import unittest, time, re -import os - -class Abn(unittest.TestCase): - def setUp(self): - ffprofile = webdriver.FirefoxProfile() - ffprofile.set_preference("browser.download.dir", os.getcwd()+'\\tmp') - ffprofile.set_preference("browser.download.folderList",2); - ffprofile.set_preference("browser.helperApps.neverAsk.saveToDisk", - ",application/octet-stream" + - ",application/vnd.ms-excel" + - ",application/vnd.msexcel" + - ",application/x-excel" + - ",application/x-msexcel" + - ",application/xls" + - ",application/vnd.ms-excel" + - ",application/vnd.ms-excel.addin.macroenabled.12" + - ",application/vnd.ms-excel.sheet.macroenabled.12" + - ",application/vnd.ms-excel.template.macroenabled.12" + - ",application/vnd.ms-excelsheet.binary.macroenabled.12" + - ",application/vnd.ms-fontobject" + - ",application/vnd.ms-htmlhelp" + - ",application/vnd.ms-ims" + - ",application/vnd.ms-lrm" + - ",application/vnd.ms-officetheme" + - ",application/vnd.ms-pki.seccat" + - ",application/vnd.ms-pki.stl" + - ",application/vnd.ms-word.document.macroenabled.12" + - ",application/vnd.ms-word.template.macroenabed.12" + - ",application/vnd.ms-works" + - ",application/vnd.ms-wpl" + - ",application/vnd.ms-xpsdocument" + - ",application/vnd.openofficeorg.extension" + - ",application/vnd.openxmformats-officedocument.wordprocessingml.document" + - ",application/vnd.openxmlformats-officedocument.presentationml.presentation" + - ",application/vnd.openxmlformats-officedocument.presentationml.slide" + - ",application/vnd.openxmlformats-officedocument.presentationml.slideshw" + - ",application/vnd.openxmlformats-officedocument.presentationml.template" + - ",application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + - ",application/vnd.openxmlformats-officedocument.spreadsheetml.template" + - ",application/vnd.openxmlformats-officedocument.wordprocessingml.template" + - ",application/x-ms-application" + - ",application/x-ms-wmd" + - ",application/x-ms-wmz" + - ",application/x-ms-xbap" + - ",application/x-msaccess" + - ",application/x-msbinder" + - ",application/x-mscardfile" + - ",application/x-msclip" + - ",application/x-msdownload" + - ",application/x-msmediaview" + - ",application/x-msmetafile" + - ",application/x-mspublisher" + - ",application/x-msschedule" + - ",application/x-msterminal" + - ",application/x-mswrite" + - ",application/xml" + - ",application/xml-dtd" + - ",application/xop+xml" + - ",application/xslt+xml" + - ",application/xspf+xml" + - ",application/xv+xml" + - ",application/excel") - - self.driver = webdriver.Firefox(ffprofile) - self.driver.implicitly_wait(30) - self.base_url = "https://b2b.abn.ru/" - self.verificationErrors = [] - self.accept_next_alert = True - - def test_abn(self): - driver = self.driver - driver.get(self.base_url + "/index.php") - driver.find_element_by_id("login").clear() - driver.find_element_by_id("login").send_keys("146348_1509_130006") - driver.find_element_by_id("pass").clear() - driver.find_element_by_id("pass").send_keys("146348_12345") - driver.find_element_by_name("Submit").click() -# driver.find_element_by_css_selector("div.image").click() -# driver.find_element_by_css_selector("a.download_ico.xls").click() -# driver.get(self.base_url + "get_price_list.php?action=proccess&login=72392&reqstr=13_6575,6582,6584,6608,6638,6696,6665,6616_1_1_1_0_0&fileMode=xls") -- -# driver.get(self.base_url + "get_price_list.php?action=proccess&login=72392&reqstr=-1_-1_1_1_1_0_0&fileMode=xls") - driver.get(self.base_url + "/get_price_list.php?action=proccess&login=72392&reqstr=-1_-1_6701,6575,6582" + - ",6630,6710,6584,7044,6608,6657,7047,6638,6618,6619,6681,6639,7365,6621,6641,6622,7081,6589,6660" + - ",6610,6624,6591,7369,6625,6684,6578,6696,6627,7413,6665,6667,6646,7410,6596,7199,6671,6658,6672" + - ",6598,6704,6698,7366,6649,6632,6687,6651,7364,6690,6614,6615,6602,6616,6635,7352,6708,6712,7378" + - ",6599,7348,6692,7362_1_1_1_0_0&fileMode=xls") - time.sleep(200) - - - def is_element_present(self, how, what): - try: self.driver.find_element(by=how, value=what) - except NoSuchElementException: return False - return True - - def is_alert_present(self): - try: self.driver.switch_to_alert() - except NoAlertPresentException: return False - return True - - def close_alert_and_get_its_text(self): - try: - alert = self.driver.switch_to_alert() - alert_text = alert.text - if self.accept_next_alert: - alert.accept() - else: - alert.dismiss() - return alert_text - finally: self.accept_next_alert = True - - def tearDown(self): - self.driver.quit() - self.assertEqual([], self.verificationErrors) - -if __name__ == "__main__": - unittest.main() diff --git a/cfg_abn.cfg b/cfg_abn.cfg new file mode 100644 index 0000000..9290ff4 --- /dev/null +++ b/cfg_abn.cfg @@ -0,0 +1,33 @@ +[basic] +filename_in = new_abn.xlsx +filename_out = abn.csv +filename_old = old_abn.xlsx +sheetname = Прайс-лист +срок годности = 14 # период устаревания прайса в днях + + +[download] +url_lk = https://b2b.abn.ru/ +url_file = https://b2b.abn.ru/get_price_list.php?action=proccess&login=72392&reqstr=-1_-1_6701,6575,6582,6630,6710,6584,7044,6608,6657,7047,6638,6618,6619,6681,6639,7365,6621,6641,6622,7081,6589,6660,6610,6624,6591,7369,6625,6684,6578,6696,6627,7413,6665,6667,6646,7410,6596,7199,6671,6658,6672,6598,6704,6698,7366,6649,6632,6687,6651,7364,6690,6614,6615,6602,6616,6635,7352,6708,6712,7378,6599,7348,6692,7362_1_1_1_0_0&fileMode=xls +filename_new = new_abn.xlsx +filename_old = old_abn.xlsx + + + # !!! ВАЖНО !!! Имена полей должны быть lowercase ! + +[cols_in] # Проставить номер импортируемым колонкам. Остальным - пусто +код_ = 1 +наименование = 2 +наличие = 4 +цена1 = 8 +бренд = 9 +модель = 10 + + +[cols_out] # Выводимым колонкам проставить названия входных полей, остальным - пусто +код = код_ +бренд = бренд +наименование = наименование +закупка = цена1 +наличие = наличие +модель = модель diff --git a/logging.cfg b/logging.cfg index aca437f..27fe7ba 100644 --- a/logging.cfg +++ b/logging.cfg @@ -51,16 +51,16 @@ args =( ('smtp.gmail.com','465'), 'mnp.docn@gmail.com', ['docn@mail.ru'], [formatter_form01] -format=F1 %(asctime)s %(levelname)-7s %(message)s +format=%(asctime)s %(levelname)-7s %(message)s datefmt= class=logging.Formatter [formatter_form02] -format=F2 %(asctime)s %(levelname)-7s %(message)s +format=%(asctime)s %(levelname)-7s %(message)s datefmt= class=logging.Formatter [formatter_form03] -format=F3 %(asctime)s %(levelname)s %(message)s +format=%(asctime)s %(levelname)s %(message)s datefmt= class=logging.Formatter diff --git a/price_tools.py b/price_tools.py new file mode 100755 index 0000000..cc3a5c2 --- /dev/null +++ b/price_tools.py @@ -0,0 +1,175 @@ +# -*- coding: UTF-8 -*- +import xlrd # для .xls +import openpyxl # Для .xlsx +import re + + + +def openX(fileName ): + typeX = fileName[fileName.find('.')+1 :] + if typeX.lower() == 'xlsx': + book = openpyxl.load_workbook(filename = fileName, read_only=False, keep_vba=False, data_only=False) # xlsx + else: + book = xlrd.open_workbook( fileName.encode('cp1251'), formatting_info=True) # xls + return book + + + +def sheetByName( fileName + ,sheetName): + typeX = fileName[fileName.find('.')+1 :] + try: + if typeX.lower() == 'xlsx': + book = openpyxl.load_workbook(filename = fileName, read_only=False, keep_vba=False, data_only=False) # xlsx + sheet = book[sheetName] # xlsx + else: + book = xlrd.open_workbook( fileName.encode('cp1251'), formatting_info=True) # xls + sheet = sheet_by_name(sheetName) + except Exception as e: + sheet = False + return sheet + + #sheet = book.worksheets[0] # xlsx + #sheet = book.sheets()[0] # xls + +def getCellXlsx( row # номер строки + , col # номер колонки + , isDigit # Признак, числовое ли значение нужно из этого поля + , sheet # лист XLSX + ): + ''' + Функция возвращает значение xls-ячейки в виде строки. + Для цифровых ячеек делается предварительное преобразование + в число (пустые и нечисловые значения преобразуются в "0") + ''' + ccc = sheet.cell(row=row, column=col) + cellType = ccc.data_type + cellValue = ccc.value + if (isDigit == 'Y') : + if (cellValue == None) : + ss = '0' + elif (cellType in ('n')) : # numeric + if int(cellValue) == cellValue: + ss = str(int(cellValue)) + else : + ss = str(cellValue) + else : +# ss = '0' + try: + ss = str(float(cellValue.replace(',','.'))) + except ValueError as e: + ss='0' + else : + if (cellValue == None) : + ss = '' + elif (cellType in ('n')) : # numeric + if int(cellValue) == cellValue: + ss = str(int(cellValue)) + else : + ss = str(cellValue) + else : + ss = str(cellValue) + return ss + + + +def getCell( row # номер строки + , col # номер колонки + , isDigit # Признак, числовое ли значение нужно из этого поля + , sheet # лист XLS + ): + ''' + Функция возвращает значение xls-ячейки в виде строки. + Для цифровых ячеек делается предварительное преобразование + в число (пустые и нечисловые значения преобразуются в "0") + ''' + ccc = sheet.cell(row, col) + cellType = ccc.ctype + cellValue = ccc.value + if (isDigit == 'Y') : + if (cellValue == '') : + ss = '0' + elif (cellType in (2,3)) : # numeric + if int(cellValue) == cellValue: + ss = str(int(cellValue)) + else : + ss = str(cellValue) + else : + ss = str(float(cellValue)) + print(cellValue, ss) + else : + if (cellType in (2,3)) : # numeric + if int(cellValue) == cellValue: + ss = str(int(cellValue)) + else : + ss = str(cellValue) + else : + ss = str(cellValue) + return ss + + + +def subInParentheses( sourceString): + re_parentheses = re.compile('^.*\(([^)]*)\).*$', re.LOCALE | re.IGNORECASE ) + is_parentheses = re_parentheses.match(sourceString) + if is_parentheses: # Файл соответствует шаблону имени + key = is_parentheses.group(1) # выделяю ключ из имени файла + else: + key = '' + return key + + + +def currencyType(sheet, rowx, colx): + ''' + Функция анализирует "формат ячейки" таблицы excel, является ли он "денежным" + и какая валюта указана в этом формате. + Распознаются не все валюты и способы их описания. + ''' + c = sheet.cell(rowx, colx) + xf = sheet.book.xf_list[c.xf_index] + fmt_obj = sheet.book.format_map[xf.format_key] + fmt_str = fmt_obj.format_str + if '\u20bd' in fmt_str: + val = 'RUB' + elif '\xa3' in fmt_str: + val = 'GBP' + elif chr(8364) in fmt_str: + val = 'EUR' + elif (fmt_str.find('USD')>=0) or (fmt_str.find('[$$')>=0) : + val = 'USD' + else: + val = '' + return val + +''' + +[$$-409]#,##0.0 +[$$-409]#,##0.0 +[$$-409]#,##0.0 +[$$-409]#,##0.0 +[$$-409]#,##0.0 +#,##0.0"р." +#,##0.0"р." +#,##0.0"р." +#,##0.0"р." +#,##0.0"р." +# +''' + + + +def dump_cell(sheet, rowx, colx): + c = sheet.cell(rowx, colx) + xf = sheet.book.xf_list[c.xf_index] + fmt_obj = sheet.book.format_map[xf.format_key] + ccc = ord(fmt_obj.format_str[4]) + print( rowx, colx, repr(c.value), c.ctype, fmt_obj.type, ccc, chr(ccc) ) + #print( repr(fmt_obj.format_str)) + + + +def quoted(sss): + if ((',' in sss) or ('"' in sss) or ('\n' in sss)) and not(sss[0]=='"' and sss[-1]=='"') : + sss = '"'+sss.replace('"','""')+'"' + return sss