100 lines
5.8 KiB
Python
100 lines
5.8 KiB
Python
"""Resolve same-conversation quote edits and return local facts without model I/O."""
|
|
|
|
from decimal import Decimal, InvalidOperation
|
|
import re
|
|
import sqlite3
|
|
|
|
from .db import connect
|
|
from .quote_data import load_book, postcode_fee
|
|
from .quote_engine import US, QUOTE_WORDS, parse_intent, is_followup, variants, render_rates
|
|
|
|
|
|
def intent_with_context(message, conv_id, book):
|
|
from copy import deepcopy
|
|
codes = {row['code'] for row in book['warehouses']}
|
|
codes.update(value.upper() for rate in book['rates'] for value in rate['destinations'])
|
|
intent = parse_intent(message,codes)
|
|
def destination_reset(value):
|
|
if value.get('warehouse') or value.get('unknown_warehouses'):return {'postal','zone','zones'}
|
|
if value.get('postal'):return {'warehouse','warehouses','unknown_warehouses','zone','zones'}
|
|
if value.get('zone'):return {'warehouse','warehouses','unknown_warehouses','postal'}
|
|
return set()
|
|
excluded=destination_reset(intent)
|
|
selected_destination=bool(excluded)
|
|
if selected_destination and not intent.get('unknown_warehouses'):excluded.add('unknown_warehouses')
|
|
if intent['transport'] or intent['kinds'] or intent['services']:excluded.update(('air_only','air_requested'))
|
|
if intent['air_only'] or intent['tax']=='excluded':excluded.add('kinds')
|
|
if intent.get('weight') is not None and not intent.get('packages'):excluded.add('packages')
|
|
explicit_dims=bool(intent['dims'])
|
|
if conv_id and is_followup(message,intent):
|
|
connection=connect()
|
|
try:
|
|
recent=connection.execute("SELECT content FROM messages WHERE conv_id=? AND role='user' ORDER BY id DESC LIMIT 10",(conv_id,)).fetchall()
|
|
finally:connection.close()
|
|
for (previous,) in recent:
|
|
if re.search(r'理赔|赔偿|破损|丢失|退件|投诉|查单|轨迹|签收',previous):break
|
|
earlier=parse_intent(previous,codes)
|
|
if earlier['country']=='other':break
|
|
if not (QUOTE_WORDS.search(previous) or US.search(previous) or is_followup(previous,earlier) or
|
|
any(earlier.get(key) for key in ('weight','dims','cbm','warehouse','postal'))):break
|
|
if not selected_destination and destination_reset(earlier):
|
|
excluded.update(destination_reset(earlier));selected_destination=True
|
|
if intent['dims'] is None and earlier['dims']:
|
|
intent['dims']=earlier['dims'];intent['dim_unit']=earlier['dim_unit']
|
|
for key,value in earlier.items():
|
|
if key in ('mode','text','errors','dim_unit','dims'):continue
|
|
if intent.get(key) is None and key not in excluded:intent[key]=deepcopy(value)
|
|
if not is_followup(previous,earlier):break
|
|
if explicit_dims and intent.get('packages'):
|
|
for piece in intent['packages']:
|
|
piece['dims']=intent['dims'];piece['dim_unit']=intent['dim_unit']
|
|
if (intent['warehouse'] or intent.get('unknown_warehouses')) and intent['country'] is None:intent['country']='US'
|
|
return intent
|
|
|
|
|
|
def answer_quote(message, conv_id):
|
|
if re.search(r'理赔|赔偿|破损|丢失|退件|投诉|查单|轨迹|签收', message):
|
|
return None
|
|
# Avoid a new read for ordinary chat; short followups need their local context.
|
|
if not (US.search(message) or QUOTE_WORDS.search(message) or conv_id or
|
|
re.search(r'(?<![A-Za-z0-9])[A-Za-z]{2,4}[0-9](?![A-Za-z0-9])', message)):
|
|
return None
|
|
try:
|
|
book = load_book()
|
|
if not book:
|
|
return None
|
|
intent = intent_with_context(message, conv_id, book)
|
|
if intent['country'] != 'US':
|
|
return None
|
|
if intent.get('errors'):
|
|
return {'t':'\n'.join(intent['errors'])}
|
|
if intent.get('unknown_warehouses'):
|
|
return {'t':'未找到目的仓码 '+ '、'.join(intent['unknown_warehouses'])+',请核对仓码,或提供5位美国邮编继续查询。'}
|
|
if not (QUOTE_WORDS.search(message) or is_followup(message,intent) or
|
|
any(intent.get(key) for key in ('weight','dims','cbm','warehouse','postal'))):
|
|
return None
|
|
for key in ('weight','cbm'):
|
|
if intent[key] is not None and not (Decimal(0) < Decimal(intent[key]) <= Decimal(1000000)):
|
|
return {'t':'请提供大于 0 的有效重量或体积。'}
|
|
if intent['boxes'] is not None and not 0 < intent['boxes'] <= 10000:
|
|
return {'t':'请提供有效的货物箱数。'}
|
|
dimensions=[intent['dims']] if intent['dims'] else []
|
|
dimensions += [piece['dims'] for piece in intent.get('packages') or [] if piece.get('dims')]
|
|
if any(not 0 < value <= 100000 for dims in dimensions for value in dims):
|
|
return {'t':'请提供大于 0 的有效长宽高。'}
|
|
from .quote_answers import answer_business
|
|
business=answer_business(intent,book)
|
|
if business is not None:return {'t':business}
|
|
if intent['air_only']:
|
|
return {'t':'当前美国报价资料包含美森、EXX、合德及普船的海卡、海铁和海派。发我目的仓码或邮编、重量,可以列出这些线路的价格;空运费率当前资料没有列出。'}
|
|
destinations = variants(intent,book)
|
|
fees = []
|
|
for destination in destinations:
|
|
postal = destination['postal']
|
|
if postal and (postal[:3] in ('006','007','008','009','966','967','968','969','995','996','997','998','999') or postal=='89124'):
|
|
return {'t':f'当前海派资料不接收邮编 {postal} 的地址,请提供其他目的仓码或联系人工确认可用线路。'}
|
|
fees.append(postcode_fee(book['id'],postal) if postal else None)
|
|
return {'t':render_rates(intent,book,destinations,fees)}
|
|
except (sqlite3.Error, ValueError, KeyError, TypeError, InvalidOperation):
|
|
return {'t':'报价数据暂时无法读取,请稍后重试。'}
|