update: fixed unwanted overwriting
This commit is contained in:
36
index.html
36
index.html
@@ -90,8 +90,8 @@
|
|||||||
<input id="max_occurrence" class="inactive pure-u-23-24" type="number" min="0" value="5" disabled/>
|
<input id="max_occurrence" class="inactive pure-u-23-24" type="number" min="0" value="5" disabled/>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-md-1-3">
|
<div class="pure-u-1 pure-u-md-1-3">
|
||||||
<label for="retry">Retry</label>
|
<label for="attempts">Attempts</label>
|
||||||
<input id="retry" class="pure-u-23-24" type="number" min="0"/>
|
<input id="attempts" class="pure-u-23-24" type="number" min="1"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-u-md-1-3">
|
<div class="pure-u-1 pure-u-md-1-3">
|
||||||
<label for="locale">Locale</label>
|
<label for="locale">Locale</label>
|
||||||
@@ -230,13 +230,13 @@ $$$('#send', 'click', async (e) => {
|
|||||||
switch (Status) {
|
switch (Status) {
|
||||||
case 'STANDBY':
|
case 'STANDBY':
|
||||||
Connection.send('RESUME');
|
Connection.send('RESUME');
|
||||||
$('#send > span.text').innerHTML = 'Pause';
|
$('#send > span.text').innerText = 'Pause';
|
||||||
$('#send > span.icon').classList.remove('hidden');
|
$('#send > span.icon').classList.remove('hidden');
|
||||||
$('#send').disabled = true;
|
$('#send').disabled = true;
|
||||||
break;
|
break;
|
||||||
case 'RUNNING':
|
case 'RUNNING':
|
||||||
Connection.send('ONHOLD');
|
Connection.send('ONHOLD');
|
||||||
$('#send > span.text').innerHTML = 'Resume';
|
$('#send > span.text').innerText = 'Resume';
|
||||||
$('#send > span.icon').classList.add('hidden');
|
$('#send > span.icon').classList.add('hidden');
|
||||||
$('#send').disabled = true;
|
$('#send').disabled = true;
|
||||||
break;
|
break;
|
||||||
@@ -267,7 +267,7 @@ $$$('#send', 'click', async (e) => {
|
|||||||
Status = 'RUNNING';
|
Status = 'RUNNING';
|
||||||
Timer.setTimestamp(true);
|
Timer.setTimestamp(true);
|
||||||
|
|
||||||
$('#send > span.text').innerHTML = 'Pause';
|
$('#send > span.text').innerText = 'Pause';
|
||||||
$('#send > span.icon').classList.remove('hidden');
|
$('#send > span.icon').classList.remove('hidden');
|
||||||
$('#skip').disabled = false;
|
$('#skip').disabled = false;
|
||||||
break;
|
break;
|
||||||
@@ -298,8 +298,8 @@ $$$('#send', 'click', async (e) => {
|
|||||||
Connection.send(buffer);
|
Connection.send(buffer);
|
||||||
Status = 'READY';
|
Status = 'READY';
|
||||||
|
|
||||||
$('#fileLabel').innerHTML = file.name;
|
$('#fileLabel').innerText = file.name;
|
||||||
$('#send > span.text').innerHTML = 'Send';
|
$('#send > span.text').innerText = 'Send';
|
||||||
$('#cancel').disabled = false;
|
$('#cancel').disabled = false;
|
||||||
$('#send').disabled = true;
|
$('#send').disabled = true;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -433,7 +433,7 @@ function main(url, parameters, locales) {
|
|||||||
break;
|
break;
|
||||||
case 'setSubject':
|
case 'setSubject':
|
||||||
let [subject] = args;
|
let [subject] = args;
|
||||||
$('#subjectLabel').innerHTML = subject;
|
$('#subjectLabel').innerText = subject;
|
||||||
break;
|
break;
|
||||||
case 'setMetadata':
|
case 'setMetadata':
|
||||||
[Limit, Columns] = args;
|
[Limit, Columns] = args;
|
||||||
@@ -454,8 +454,8 @@ function main(url, parameters, locales) {
|
|||||||
break;
|
break;
|
||||||
case 'setProgress':
|
case 'setProgress':
|
||||||
let [index, name, recipient, sent, warnings, errors] = args;
|
let [index, name, recipient, sent, warnings, errors] = args;
|
||||||
let label = name ? `${name} <${recipient}>` : recipient;
|
let label = name ? `${name} <${recipient}>` : recipient;
|
||||||
$('#recipientLabel').innerHTML = label;
|
$('#recipientLabel').innerText = label;
|
||||||
$('#progressLabel').dataset.sent = sent;
|
$('#progressLabel').dataset.sent = sent;
|
||||||
$('#progressLabel').dataset.index = index;
|
$('#progressLabel').dataset.index = index;
|
||||||
$('#progressLabel').dataset.errors = errors;
|
$('#progressLabel').dataset.errors = errors;
|
||||||
@@ -477,7 +477,7 @@ function main(url, parameters, locales) {
|
|||||||
break;
|
break;
|
||||||
case 'FAILED':
|
case 'FAILED':
|
||||||
Status = 'STANDBY';
|
Status = 'STANDBY';
|
||||||
$('#send > span.text').innerHTML = 'Resume';
|
$('#send > span.text').innerText = 'Resume';
|
||||||
$('#send > span.icon').classList.add('hidden');
|
$('#send > span.icon').classList.add('hidden');
|
||||||
break;
|
break;
|
||||||
case 'FINISH':
|
case 'FINISH':
|
||||||
@@ -485,7 +485,7 @@ function main(url, parameters, locales) {
|
|||||||
alert(`${sent||0} Sent; ${warnings||0} Warning(s); ${errors||0} Error(s)`);
|
alert(`${sent||0} Sent; ${warnings||0} Warning(s); ${errors||0} Error(s)`);
|
||||||
case 'CANCEL':
|
case 'CANCEL':
|
||||||
Status = null;
|
Status = null;
|
||||||
$('#send > span.text').innerHTML = 'Open';
|
$('#send > span.text').innerText = 'Open';
|
||||||
$('#send > span.icon').classList.add('hidden');
|
$('#send > span.icon').classList.add('hidden');
|
||||||
$('#cancel').disabled = true;
|
$('#cancel').disabled = true;
|
||||||
$('#skip').disabled = true;
|
$('#skip').disabled = true;
|
||||||
@@ -518,22 +518,22 @@ function main(url, parameters, locales) {
|
|||||||
let city = timezone.split('/')[1];
|
let city = timezone.split('/')[1];
|
||||||
let date = dayjs().tz(timezone).format("YYYY-MM-DD HH:mm:ss");
|
let date = dayjs().tz(timezone).format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
|
||||||
$('#timezoneLabel').innerHTML = `${date} (${city})`;
|
$('#timezoneLabel').innerText = `${date} (${city})`;
|
||||||
$('#statusLabel').innerHTML = Status ? Status.charAt(0).toUpperCase() + Status.slice(1).toLowerCase() : 'Idle';
|
$('#statusLabel').innerText = Status ? Status.charAt(0).toUpperCase() + Status.slice(1).toLowerCase() : 'Idle';
|
||||||
|
|
||||||
if (Status === 'RUNNING') {
|
if (Status === 'RUNNING') {
|
||||||
let index = Number($('#progressLabel').dataset.index) || 0;
|
let index = Number($('#progressLabel').dataset.index) || 0;
|
||||||
let limit = Number($('#subcategory').dataset.limit) || Limit;
|
let limit = Number($('#subcategory').dataset.limit) || Limit;
|
||||||
let percentage = parseFloat((index / limit * 100).toFixed(2));
|
let percentage = parseFloat((index / limit * 100).toFixed(2));
|
||||||
$('#progressLabel').innerHTML = `${index} / ${limit} (${percentage} %)`;
|
$('#progressLabel').innerText = `${index} / ${limit} (${percentage} %)`;
|
||||||
|
|
||||||
let uptime = Timer.getTimedelta();
|
let uptime = Timer.getTimedelta();
|
||||||
$('#uptimeLabel').innerHTML = uptime.format("HH:mm:ss");
|
$('#uptimeLabel').innerText = uptime.format("HH:mm:ss");
|
||||||
|
|
||||||
let rate = Number($('#progressLabel').dataset.sent) / uptime.asSeconds();
|
let rate = Number($('#progressLabel').dataset.sent) / uptime.asSeconds();
|
||||||
let spm = parseFloat(Number(rate*60).toFixed(2));
|
let spm = parseFloat(Number(rate*60).toFixed(2));
|
||||||
$('#remainingLabel').innerHTML = rate > 0 ? `<span>${dayjs.duration((limit - index) / rate, 'second').humanize()}</span>` : '';
|
$('#remainingLabel').innerText = rate > 0 ? `<span>${dayjs.duration((limit - index) / rate, 'second').humanize()}</span>` : '';
|
||||||
$('#remainingLabel').innerHTML += spm >= 1 ? `<span class="${spm > 8.33 ? 'rate warning' : 'rate'}">${spm} per minute</span>` : '';
|
$('#remainingLabel').innerText += spm >= 1 ? `<span class="${spm > 8.33 ? 'rate warning' : 'rate'}">${spm} per minute</span>` : '';
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|||||||
155
main.py
155
main.py
@@ -41,7 +41,7 @@ parser.add_argument('-a', '--address', type=str, nargs='?')
|
|||||||
parser.add_argument('-p', '--password', type=str, nargs='?')
|
parser.add_argument('-p', '--password', type=str, nargs='?')
|
||||||
parser.add_argument('-t', '--timeout', type=int, nargs='?', default=60)
|
parser.add_argument('-t', '--timeout', type=int, nargs='?', default=60)
|
||||||
parser.add_argument('-i', '--interval', type=int, nargs='?', default=10)
|
parser.add_argument('-i', '--interval', type=int, nargs='?', default=10)
|
||||||
parser.add_argument('-r', '--retry', type=int, nargs='?', default=3)
|
parser.add_argument('-r', '--attempts', type=int, nargs='?', default=3)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
inbox, outbox = Queue(), Queue()
|
inbox, outbox = Queue(), Queue()
|
||||||
@@ -102,7 +102,7 @@ def main(driver: WebDriver):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
def locate(selector, condition=EC.presence_of_element_located, parent=driver) -> WebElement:
|
def locate(selector, condition=EC.presence_of_element_located, parent=driver) -> WebElement:
|
||||||
for attempt in range(parameters.get('retry')):
|
for attempt in range(parameters.get('attempts')):
|
||||||
try:
|
try:
|
||||||
wait = WebDriverWait(parent, timeout=parameters.get('timeout'))
|
wait = WebDriverWait(parent, timeout=parameters.get('timeout'))
|
||||||
return wait.until(condition((By.CSS_SELECTOR, selector)))
|
return wait.until(condition((By.CSS_SELECTOR, selector)))
|
||||||
@@ -122,7 +122,7 @@ def main(driver: WebDriver):
|
|||||||
value = counter()
|
value = counter()
|
||||||
driver.execute_script("arguments[0].addEventListener('click', () => arguments[0].setAttribute('taximeter', arguments[1] + 1));", element, value)
|
driver.execute_script("arguments[0].addEventListener('click', () => arguments[0].setAttribute('taximeter', arguments[1] + 1));", element, value)
|
||||||
|
|
||||||
for attempt in range(parameters.get("retry")):
|
for attempt in range(parameters.get('attempts')):
|
||||||
try:
|
try:
|
||||||
if not error: element.click()
|
if not error: element.click()
|
||||||
else: driver.execute_script("arguments[0].click();", element)
|
else: driver.execute_script("arguments[0].click();", element)
|
||||||
@@ -255,9 +255,10 @@ def main(driver: WebDriver):
|
|||||||
filename = str(inbox.get())
|
filename = str(inbox.get())
|
||||||
buffer = BytesIO(inbox.get())
|
buffer = BytesIO(inbox.get())
|
||||||
|
|
||||||
workbook = pandas.read_excel(buffer, sheet_name=0)
|
spreadsheet = pandas.read_excel(buffer, sheet_name=0)
|
||||||
frame = workbook.where(pandas.notnull(workbook), None)
|
frame = spreadsheet.where(pandas.notnull(spreadsheet), None)
|
||||||
limit = int(frame.last_valid_index())
|
index = frame.index
|
||||||
|
limit = len(index)
|
||||||
|
|
||||||
driver.switch_to.window(driver.window_handles[1])
|
driver.switch_to.window(driver.window_handles[1])
|
||||||
columns = { column: frame[column].unique() for column in frame.columns }
|
columns = { column: frame[column].unique() for column in frame.columns }
|
||||||
@@ -272,78 +273,84 @@ def main(driver: WebDriver):
|
|||||||
if request is not None:
|
if request is not None:
|
||||||
parameters = dict(json.loads(request))
|
parameters = dict(json.loads(request))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
ca = parameters.get('column_address')
|
||||||
|
cn = parameters.get('column_name')
|
||||||
|
cc = parameters.get('column_code')
|
||||||
|
cs = parameters.get('column_sent')
|
||||||
|
cp = parameters.get('column_pays')
|
||||||
|
|
||||||
|
if parameters.get('slice'):
|
||||||
|
if subcategory := parameters.get('subcategory'):
|
||||||
|
data = frame[frame[cp].isin(subcategory)]
|
||||||
|
index = data.index
|
||||||
|
|
||||||
|
offset = parameters.get('offset')
|
||||||
|
size = parameters.get('chunk_size')
|
||||||
|
start = offset * size
|
||||||
|
end = start + size
|
||||||
|
index = index[start:end]
|
||||||
|
limit = len(index)
|
||||||
|
|
||||||
|
if ca not in frame or not limit:
|
||||||
|
tell('输入无效', filename, level=1)
|
||||||
|
outbox.put(Command('setStatus', 'FINISH'))
|
||||||
|
continue
|
||||||
|
if cs not in frame:
|
||||||
|
frame[cs] = None
|
||||||
|
|
||||||
|
seriesdict = frame.iloc[index].to_dict(orient='list')
|
||||||
|
recipients = seriesdict.get(ca, [None] * limit)
|
||||||
|
names = seriesdict.get(cn, [None] * limit)
|
||||||
|
codes = seriesdict.get(cc, [None] * limit)
|
||||||
|
|
||||||
|
rate = 60 / parameters.get('interval')
|
||||||
|
length = limit - frame.loc[index, cs].count()
|
||||||
|
|
||||||
|
tell(f'已读取邮件:{subject}')
|
||||||
|
tell(f'指定发件人:{address}')
|
||||||
|
tell(f'已读取联系人信息共 {limit} 条')
|
||||||
|
tell(f'预计发送数量 {length}')
|
||||||
|
|
||||||
|
tell(f'当前发送速率 {round(rate, 2)} 封/分钟')
|
||||||
|
if rate > 8.33: tell('当前发送速率已超出限制 8.33 封/分钟', level=1)
|
||||||
|
|
||||||
|
tell(f'预计使用时间 {timedelta(minutes=length / rate)}')
|
||||||
|
tell(f'已设定允许重试次数:{parameters.get('attempts')}')
|
||||||
|
tell(f'已设定最大重复次数:{parameters.get('max_occurrence') or '无限制'}')
|
||||||
|
|
||||||
|
locale = parameters.get('locale')
|
||||||
|
greetings = [item for item in Greetings.presets() if item.locale == locale][0]
|
||||||
|
timezone = ZoneInfo(greetings.timezone)
|
||||||
|
tell(f'当前时区:{greetings.timezone}')
|
||||||
|
tell(f'当前语言:{greetings.locale.upper()}')
|
||||||
except Faillable:
|
except Faillable:
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
tell('读取数据时发生错误', e, level=0)
|
tell('读取数据时发生错误', e, level=0)
|
||||||
return 5
|
return 5
|
||||||
|
|
||||||
if parameters.get('slice'):
|
cursor = 0
|
||||||
if subcategory := parameters.get('subcategory'):
|
|
||||||
column = parameters.get('column_pays')
|
|
||||||
frame = frame[frame[column].isin(subcategory)]
|
|
||||||
|
|
||||||
offset = parameters.get('offset')
|
|
||||||
size = parameters.get('chunk_size')
|
|
||||||
start = size * offset
|
|
||||||
end = start + size
|
|
||||||
frame = frame.iloc[start:end]
|
|
||||||
|
|
||||||
data = frame.to_dict(orient='list')
|
|
||||||
recipients = data.get(parameters.get('column_address'), [])
|
|
||||||
limit = len(recipients)
|
|
||||||
|
|
||||||
if not limit:
|
|
||||||
tell('输入无效', filename, level=1)
|
|
||||||
outbox.put(Command('setStatus', 'FINISH'))
|
|
||||||
continue
|
|
||||||
|
|
||||||
names = data.get(parameters.get('column_name'), [None] * limit)
|
|
||||||
codes = data.get(parameters.get('column_code'), [None] * limit)
|
|
||||||
sents = data.setdefault(parameters.get('column_sent'), [None] * limit)
|
|
||||||
|
|
||||||
rate = 60 / (parameters.get('interval') + 3)
|
|
||||||
length = list.count(sents, None)
|
|
||||||
|
|
||||||
tell(f'已读取邮件:{subject}')
|
|
||||||
tell(f'指定发件人:{address}')
|
|
||||||
tell(f'已读取联系人信息共 {limit} 条')
|
|
||||||
tell(f'预计发送数量 {length}')
|
|
||||||
|
|
||||||
tell(f'当前发送速率 {round(rate, 2)} 封/分钟')
|
|
||||||
if rate > 8.33: tell('当前发送速率已超出限制 8.33 封/分钟', level=1)
|
|
||||||
|
|
||||||
tell(f'预计使用时间 {timedelta(minutes=length / rate)}')
|
|
||||||
tell(f'已设定允许重试次数:{parameters.get('retry')}')
|
|
||||||
tell(f'已设定最大重复次数:{parameters.get('max_occurrence') or '无限制'}')
|
|
||||||
|
|
||||||
locale = parameters.get('locale')
|
|
||||||
greetings = [item for item in Greetings.presets() if item.locale == locale][0]
|
|
||||||
timezone = ZoneInfo(greetings.timezone)
|
|
||||||
tell(f'当前时区:{greetings.timezone}')
|
|
||||||
tell(f'当前语言:{greetings.locale.upper()}')
|
|
||||||
|
|
||||||
index = 0
|
|
||||||
status = Status.ACTIVE
|
status = Status.ACTIVE
|
||||||
occurrences = {}
|
occurrences = {}
|
||||||
sent = 0
|
sent = 0
|
||||||
errors = 0
|
errors = 0
|
||||||
warnings = 0
|
warnings = 0
|
||||||
|
|
||||||
while status.isactive() and index < limit:
|
while status.isactive() and cursor < limit:
|
||||||
attempt = 0
|
attempt = 0
|
||||||
current = index
|
current = cursor
|
||||||
index += 1
|
cursor += 1
|
||||||
|
|
||||||
recipient = str(recipients[current]).strip()
|
recipient = str(recipients[current]).strip()
|
||||||
name = names[current]
|
name = names[current]
|
||||||
code = codes[current]
|
code = codes[current]
|
||||||
mark = sents[current]
|
axis = index[current]
|
||||||
|
|
||||||
occurrence = occurrences.setdefault(code, [0]) if code else [0]
|
occurrence = occurrences.setdefault(code, [0]) if code else [0]
|
||||||
outbox.put(Command('setProgress', index, name, recipient, sent, warnings, errors))
|
outbox.put(Command('setProgress', cursor, name, recipient, sent, warnings, errors))
|
||||||
|
|
||||||
if mark is not None and str(mark).strip():
|
if (remarks := frame.loc[axis, cs]) is not None and str(remarks).strip():
|
||||||
tell(f'已跳过项目 {recipient}')
|
tell(f'已跳过项目 {recipient}')
|
||||||
occurrence[0] += 1
|
occurrence[0] += 1
|
||||||
continue
|
continue
|
||||||
@@ -382,8 +389,8 @@ def main(driver: WebDriver):
|
|||||||
click(".modal-dialog .modal-footer button[data-action='ok']", condition)
|
click(".modal-dialog .modal-footer button[data-action='ok']", condition)
|
||||||
# 返回草稿箱
|
# 返回草稿箱
|
||||||
click("li[data-id='default0/Brouillons']")
|
click("li[data-id='default0/Brouillons']")
|
||||||
|
|
||||||
for attempt in range(parameters.get('retry')):
|
for attempt in range(parameters.get('attempts')):
|
||||||
ready(driver)
|
ready(driver)
|
||||||
click("ul[aria-label='List view'] li[data-index='0']", condition)
|
click("ul[aria-label='List view'] li[data-index='0']", condition)
|
||||||
if get_subject() == subject: break
|
if get_subject() == subject: break
|
||||||
@@ -442,14 +449,13 @@ def main(driver: WebDriver):
|
|||||||
wait = WebDriverWait(driver, timeout=parameters.get('interval'))
|
wait = WebDriverWait(driver, timeout=parameters.get('interval'))
|
||||||
alert = wait.until(lambda x: x.find_element(By.CSS_SELECTOR, "div.io-ox-alert.io-ox-alert-error"))
|
alert = wait.until(lambda x: x.find_element(By.CSS_SELECTOR, "div.io-ox-alert.io-ox-alert-error"))
|
||||||
|
|
||||||
sents[current] = '❌'
|
frame.loc[axis, cs] = '❌'
|
||||||
message = alert.text.replace('\n', ' ')
|
message = alert.text.replace('\n', ' ')
|
||||||
tell(f'邮件系统错误 ({attempt})', message or None, level=1)
|
tell(f'邮件系统错误 ({attempt})', message or None, level=1)
|
||||||
|
|
||||||
# 关闭警告
|
# 关闭警告
|
||||||
click("div.io-ox-alert.io-ox-alert-error button[data-action='close']")
|
click("div.io-ox-alert.io-ox-alert-error button[data-action='close']")
|
||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
sents[current] = '✔️'
|
frame.loc[axis, cs] = '✔️'
|
||||||
occurrence[0] += 1
|
occurrence[0] += 1
|
||||||
sent += 1
|
sent += 1
|
||||||
break
|
break
|
||||||
@@ -462,7 +468,7 @@ def main(driver: WebDriver):
|
|||||||
tell("关闭邮件时发生了错误", e, level=1)
|
tell("关闭邮件时发生了错误", e, level=1)
|
||||||
break
|
break
|
||||||
|
|
||||||
if attempt < parameters.get('retry'):
|
if attempt < parameters.get('attempts'):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
tell('已超出最大重试上限', level=1)
|
tell('已超出最大重试上限', level=1)
|
||||||
@@ -489,16 +495,25 @@ def main(driver: WebDriver):
|
|||||||
outbox.put(Command('setStatus', 'CANCEL'))
|
outbox.put(Command('setStatus', 'CANCEL'))
|
||||||
status = Status.INACTIVE
|
status = Status.INACTIVE
|
||||||
|
|
||||||
progress = index / limit * 100
|
progress = cursor / limit * 100
|
||||||
tell('[信息] 当前进度:%.2f %%' % progress)
|
tell('[信息] 当前进度:%.2f %%' % progress)
|
||||||
tell(f'已发送 {sent} 封;发送失败 {errors} 封;跳过重复项 {warnings} 个')
|
tell(f'已发送 {sent} 封;发送失败 {errors} 封;跳过重复项 {warnings} 个')
|
||||||
|
|
||||||
if parameters.get('save'):
|
if parameters.get('save'):
|
||||||
try:
|
for attempt in range(parameters.get('attempts')):
|
||||||
tell(f'正在写入文件:{filename}')
|
try:
|
||||||
pandas.DataFrame.from_dict(data).to_excel(filename, index=False, sheet_name='Sheet1')
|
path = Path(filename)
|
||||||
except Exception as e:
|
tell(f'正在写入文件:{path}')
|
||||||
tell('写入文件时发生了错误', e, level=0)
|
if path.exists():
|
||||||
|
spreadsheet = pandas.read_excel(path, sheet_name=0)
|
||||||
|
frame = frame.combine_first(spreadsheet)
|
||||||
|
with pandas.ExcelWriter(path, mode='w') as writer:
|
||||||
|
frame.to_excel(writer, index=False)
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
tell(f'写入文件时发生错误 ({attempt})', e, level=1)
|
||||||
|
time.sleep(1)
|
||||||
|
continue
|
||||||
|
|
||||||
if status.isalive(): outbox.put(Command('setStatus', 'FINISH'))
|
if status.isalive(): outbox.put(Command('setStatus', 'FINISH'))
|
||||||
else: return 0
|
else: return 0
|
||||||
|
|||||||
3
profiles/profile-example.bat
Normal file
3
profiles/profile-example.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
cd /D .\..\
|
||||||
|
.\venv\Scripts\pythonw.exe .\main.py --address "user@example.com" --password "example" --interval 10
|
||||||
|
@pause
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
subprocess.run([".\\..\\venv\\Scripts\\pythonw.exe", ".\\main.py", "--address", "user@example.com", "--password", "password", "--interval", "10"], cwd=".\\..\\")
|
|
||||||
4
profiles/setup-virtualenv.bat
Normal file
4
profiles/setup-virtualenv.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
cd /D .\..\
|
||||||
|
python -m venv venv
|
||||||
|
.\venv\Scripts\pip.exe install -r ./requirements.txt
|
||||||
|
@pause
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
subprocess.run(["powershell.exe", "python.exe -m venv venv; .\\venv\\Scripts\\python.exe -m pip install -r requirements.txt"], cwd=".\\..\\")
|
|
||||||
Reference in New Issue
Block a user