update: bump 'common' version to 0.1.18

This commit is contained in:
2026-07-16 11:59:53 +08:00
parent 0150468161
commit 8ba58f4749
3 changed files with 94 additions and 131 deletions
+84 -114
View File
@@ -1,4 +1,3 @@
import unicodedata
import argparse
import openpyxl
import logging
@@ -20,7 +19,7 @@ from enum import Enum
from wakepy import keep
from pathlib import Path
from datetime import datetime
from itertools import repeat, count
from itertools import count
from urllib3 import PoolManager
parser = argparse.ArgumentParser(description="Order Import")
@@ -45,8 +44,8 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
class Status(Enum):
IDLE = 0
READY = 1
RUNNING = 3
STANDBY = 4
RUNNING = 2
STANDBY = 3
class Profile:
def __init__(self, name, subdomain, token, remise, person=None, prefix=None, suffix=None):
@@ -88,9 +87,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
t1.start()
t2.start()
sp.set('begin', begin)
sp.set('pause', pause)
sp.set('resume', resume)
sp.add(begin, pause, resume)
sp.set('status', lambda: status.name)
sp.set('uptime', lambda: [t1.delta()])
@@ -109,7 +106,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
except Exception as e:
logger.critical('Unable to load starup page', exc_info=e)
return 3
try:
driver.switch_to.new_window('tab')
driver.set_page_load_timeout(parameters['timeout'])
@@ -117,10 +114,10 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
except TimeoutException:
logger.warning('Timeout')
driver.execute_script("window.stop();")
setup(driver, parameters)
until(lambda x: 'loginProgress' in x.find_element(By.TAG_NAME, "body").get_attribute('class'), watch=False)
logger.info('Waiting for authentication to complete...')
logger.info('Waiting for authentication...')
if (account := parameters['account']) and (password := parameters['password']):
try:
@@ -158,7 +155,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
if self.wb.active.cell(row, a+1).value == needle:
b = row
break
if b is None: return dict()
return { k: self.wb.active.cell(b, c+1).value for c, k in enumerate(self.headers) }
@@ -186,8 +183,8 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
p = ProductInfo(file)
driver.close()
driver.switch_to.window(driver.window_handles[0])
status = Status.READY
logger.info('Done')
status = Status.READY
except Exception as e:
logger.critical('Unable to load products', exc_info=e)
return 4
@@ -200,13 +197,13 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
return result
except Exception as e:
logger.warning('Error while fetching data from %s, retrying... (%d)', url, attempt, exc_info=e)
assert attempt < retry
assert attempt < retry, "Exceeded maximum retry attempts"
class Wait(Action):
@classmethod
def prepare(cls):
return True
@classmethod
def perform(cls):
if status == Status.RUNNING: return False
@@ -216,7 +213,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
@classmethod
def prepare(cls):
return True
@classmethod
def perform(cls):
sleep(parameters['interval'])
@@ -228,7 +225,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
nonlocal status
status = Status.RUNNING
return True
@classmethod
def perform(cls):
nonlocal status
@@ -240,25 +237,20 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
@classmethod
def prepare(cls):
return True
@classmethod
def perform(cls):
driver.switch_to.window(driver.current_window_handle)
raise cls
flow = ActionFlow()
flow.append(Wait)
flow.append(Sleep)
flow.append(Cancel)
flow.append(Skip)
flow.stage(Wait, Sleep, Cancel, Skip)
profile = None
progress = { 'task': '' }
selection = 0
sp.add(pairs=[ (k.lower(), v) for k, v in flow ])
sp.set('actions', lambda: flow.capabilities())
sp.set('cancel', lambda: flow.queue(Cancel))
sp.set('skip', lambda: flow.queue(Skip))
sp.set('progress', lambda: progress)
while not flow.react(Wait):
@@ -303,7 +295,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
logger.info('Profile selected: %s', profile.name)
logger.info('Date from %s to %s', df, dt)
flow.allow(Cancel)
flow.allow(Skip, False)
flow.deter(Skip)
for page in count(1):
try:
@@ -388,17 +380,17 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
if 'error' in (category := fetch(f'{base}/categories/{o}.json?api_token={profile.token}')):
error = category['error']
code = category['code']
logger.warning("Error while fetching 'category' (code: %s, message: %s); skipping", code, error)
logger.warning("Error while fetching field 'category' (code: %s, message: %s); skipping", code, error)
continue
if (client := clients.get(o := item['client_id'])) is None:
if 'error' in (client := fetch(f'{base}/clients/{o}.json?api_token={profile.token}')):
error = client['error']
code = client['code']
logger.warning("Error while fetching 'client' (code: %s, message: %s); skipping", code, error)
logger.warning("Error while fetching field 'client' (code: %s, message: %s); skipping", code, error)
continue
identity = client['external_id'] if client['company'] else profile.person
identity = client['shortcut'] if client['company'] else profile.person
date: str = item['issue_date']
kind: str = item['kind']
total = float(item['price_net'])
@@ -453,7 +445,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
try:
filename = f'Order-Import-{profile.name}-{datetime.now().strftime('%Y%m%d-%H%M%S-%f')}.xlsx'
file = Path(parameters['directory']).joinpath(filename)
logger.info('Saving excel file to %s', str(file))
logger.info('Saving document at %s', str(file))
flow.react(Wait)
workbook.save(file)
except Skip:
@@ -461,12 +453,12 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
except Cancel:
continue
except Exception as e:
logger.error('Error while saving excel file', exc_info=e)
logger.error('Error while saving document', exc_info=e)
status = Status.STANDBY
continue
try:
logger.info('Uploading...')
logger.info('Uploading data...')
progress.clear()
progress['task'] = 'Task 3 of 4'
t2.clear()
@@ -477,29 +469,26 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
click(".product-import-img-box .import-img-radio:nth-child(2) .mm-radio-group > label:nth-child(2) .mm-radio-input", condition=None)
click(".product-import-img-box .mm-selector-rendered")
click(".mm-outside.mm-select-dropdown ul li:nth-child(%d) span" % (1 if options.get('draft') else 6))
flow.react(Wait)
locate(".big-file-upload input", wait=False).send_keys(str(file))
click(".product-import-img-footer button")
click(".product-import-img-footer button.mm-button__primary")
while True:
while not flow.react(Wait, Sleep):
try:
innerText = driver.find_element(By.CSS_SELECTOR, ".img-box-result-title").get_attribute('innerText')
if innerText == '导入完成': break
except:
flow.react(Sleep)
click(".mm-notification-container .mm-icon-close")
click(".product-import-img-footer button.mm-button__primary")
err = locate(".mm-tbody table tbody tr:nth-child(1) td:nth-child(5) .okki-space-item:nth-child(1) button")
err = locate(".mm-tbody table tbody tr:nth-child(1) td:nth-child(5) .okki-space-item:nth-child(1) button", wait=False)
ok = locate(".mm-tbody table tbody tr:nth-child(1) td:nth-child(3) a", wait=False)
break
except: pass
try: click(".product-import-img-footer button.mm-button__primary", wait=False)
except: pass
try: click(".list-header-wrap button", wait=False)
except: pass
if err.get_attribute('disabled') is None:
logger.warning('Incomplete import detected; downloaded 1 related document')
err.click()
click(err, condition=None)
logger.warning('Incomplete import detected; downloading 1 related document')
flow.react(Sleep)
click(".mm-tbody table tbody tr:nth-child(1) td:nth-child(3) a")
click(ok, condition=None)
flow.react(Sleep)
logger.info('Done')
except Skip:
@@ -507,7 +496,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
except Cancel:
continue
except Exception as e:
logger.error('Error while uploading excel file', exc_info=e)
logger.error('Error while uploading document', exc_info=e)
status = Status.STANDBY
continue
@@ -528,7 +517,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
def get(self, key: str):
return self.query[key]
def set(self, key: str, value):
self.query[key] = value
@@ -542,6 +531,7 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
while index < len(data):
try:
attempts += 1
if len(driver.window_handles) > 3: driver.close()
driver.switch_to.window(driver.window_handles[2])
item = data[index]
kind = item['kind']
@@ -551,21 +541,16 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
opportunity = None
if kind != 'vat':
logger.info('[%d/%d] Skipping %s', index+1, len(data), number)
index += 1
attempts = 0
continue
logger.info('[%d/%d] Undesired invoice type; skipping %s', index+1, len(data), number)
raise Skip()
if attempts > parameters['attempts']:
logger.warning('Exhausted all allowed attempts; skipping %s', number)
index += 1
attempts = 0
continue
raise Skip()
progress['number'] = number
progress['index'] = index
flow.allow(Cancel)
flow.allow(Skip)
flow.allow(Skip, Cancel)
flow.react(Wait)
try:
@@ -597,17 +582,11 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
flow.react(Wait)
click(".sticky .okki-space-item:nth-child(1) button")
break
except Skip:
index += 1
attempts = 0
continue
except Cancel:
break
except NoSuchElementException:
logger.warning("Could not find invoice '%s'; skipping", number)
index += 1
attempts = 0
continue
raise Skip()
except Action as e:
raise e
except Exception as e:
logger.error("Error while looking up invoice '%s'", number, exc_info=e)
status = Status.STANDBY
@@ -638,16 +617,12 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
cell = locate(".virtual-list-wrap .vue-recycle-scroller .row-item > .cell:nth-child(3) .ow-serial-read-pretty_ellipsis", wait=False)
if cell.text != match: continue
link = locate(".virtual-list-wrap .vue-recycle-scroller .row-item > .cell:nth-child(6) a", wait=False)
opportunity = unicodedata.normalize('NFKD', link.get_attribute('title'))
opportunity = link.text
break
except Skip:
index += 1
attempts = 0
continue
except Cancel:
break
except NoSuchElementException:
logger.warning("Could not find opportunity '%s'; skipping", match)
logger.warning("Could not find opportunity '%s'", match)
except Action as e:
raise e
except Exception as e:
logger.error("Error while looking up opportunity '%s'", match, exc_info=e)
status = Status.STANDBY
@@ -659,24 +634,29 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
if opportunity is not None:
try:
flow.react(Wait)
label = locate("label.paas-form-item-label")
dropdown = locate("#rc_select_1")
dropdown.clear()
dropdown.send_keys(opportunity)
menu = locate(".okki-select-dropdown")
menuitems = menu.find_elements(By.CSS_SELECTOR, ".rc-virtual-list-holder-inner > div")
for iteration in count(1):
dropdown.click()
dropdown.clear()
dropdown.send_keys(opportunity)
menu = locate(".okki-select-dropdown")
menuitems = menu.find_elements(By.CSS_SELECTOR, ".rc-virtual-list-holder-inner > div")
for menuitem in menuitems:
if menuitem.get_attribute('label').strip().startswith(opportunity):
click(menuitem)
except Skip:
index += 1
attempts = 0
continue
except Cancel:
break
try:
for menuitem in menuitems:
if menuitem.get_attribute('label') == opportunity:
click(menuitem)
raise StopIteration()
except StopIteration: break
except: pass
assert iteration < parameters['attempts'], "Exceeded maximum retry attempts"
click(label, condition=None)
except Action as e:
raise e
except Exception as e:
logger.warning('Could not select opportunity; skipping', exc_info=e)
logger.warning("Could not select opportunity '%s'", opportunity, exc_info=e)
try:
pagination = 10
@@ -730,12 +710,8 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
raise Exception('Product list imcomplete; expected %d, got %d' % (len(positions), len(ids)))
flow.react(Wait)
click(button)
except Skip:
index += 1
attempts = 0
continue
except Cancel:
break
except Action as e:
raise e
except Exception as e:
logger.error('Error while modifying invoice', exc_info=e)
status = Status.STANDBY
@@ -745,30 +721,24 @@ def main(driver: WebDriver, logger = logging.getLogger('main')):
click(".ow-box button.okki-btn-round", wait=False)
flow.react(Sleep)
except Exception as e:
logger.warning('Unable to unset additional fees; skipping', exc_info=e)
try:
flow.react(Wait)
flow.allow(Cancel, False)
flow.allow(Skip, False)
click(".sticky.bottom-0 button.okki-btn-primary", condition=None)
flow.react(Sleep)
except Skip:
pass
except Cancel:
break
except Exception as e:
logger.error('Error while saving document', exc_info=e)
status = Status.STANDBY
continue
finally:
driver.close()
logger.warning('Unable to unset additional fees', exc_info=e)
index += 1
attempts = 0
flow.react(Wait)
flow.deter(Skip, Cancel)
click(".sticky.bottom-0 button.okki-btn-primary", condition=None)
flow.react(Sleep)
driver.close()
except Skip:
pass
except Cancel:
break
except Exception as e:
logger.error('Unexpected error', exc_info=e)
status = Status.STANDBY
continue
index += 1
attempts = 0
if __name__ == '__main__':
try: