import os |
import os |
|
from decimal import Decimal |
from simplegeneric import generic |
from simplegeneric import generic |
from peak.util.decorators import struct |
from peak.util.decorators import struct |
|
|
try: |
try: |
import xml.etree.cElementTree as ET |
import xml.etree.cElementTree as ET |
except ImportError: |
except ImportError: |
import elementtree.ElementTree as ET |
import elementtree.ElementTree as ET |
|
|
__all__ = [ |
__all__ = [ |
'Option', 'OptionConflict', 'Layout', 'OutputFile', 'Insurance', |
'Option', 'OptionConflict', 'Shipment', 'Batch', 'iter_options', |
'DateAdvance', 'Today', 'Tomorrow', 'WeekendDelivery', 'HolidayDelivery', |
'DAZzle', 'Services', 'Domestic', 'International', 'Customs', |
'NoPostage', 'Domestic', 'International', 'Shipment', 'Postcard', |
'Insurance', 'DateAdvance', 'Today', 'Tomorrow', |
'Envelope', 'Flat', 'RectangularParcel', 'NonRectangularParcel', |
'WeekendDelivery', 'HolidayDelivery', 'NoPostage', |
'FlatRateEnvelope', 'FlatRateBox', 'ToAddress', 'ReturnAddress', |
'Postcard', 'Envelope', 'Flat', 'RectangularParcel', |
'RubberStamp', 'Print', 'Verify', 'Batch', 'iter_options', |
'NonRectangularParcel', 'FlatRateEnvelope', 'FlatRateBox', |
|
'ToAddress', 'ReturnAddress', 'RubberStamp', |
# ...and many more symbols added dynamically! |
# ...and many more symbols added dynamically! |
] |
] |
|
|
for ob in ob: |
for ob in ob: |
yield ob |
yield ob |
|
|
@generic |
|
def add_to_package(ob, package, isdefault): |
|
"""Update `etree` to apply document info""" |
|
for ob in iter_options(ob): |
|
add_to_package(ob, package, isdefault) |
|
|
|
class Package: |
class Package: |
"""The XML for a single package/label""" |
"""The XML for a single package/label""" |
finished = False |
finished = False |
|
total_items = total_weight = total_value = 0 |
|
|
def __init__(self, batch): |
def __init__(self, batch): |
parent = batch.etree |
parent = batch.etree |
el.text = unicode(value) |
el.text = unicode(value) |
|
|
def should_queue(self, data): |
def should_queue(self, data): |
if self.finished: return False |
if self.finished: |
|
return False |
self.queue.append(data) |
self.queue.append(data) |
return True |
return True |
|
|
|
|
|
|
|
def add_customs_item(self, item): |
|
self.total_items += 1 |
|
self.total_value += item.value * item.qty |
|
self.total_weight += item.weight * item.qty |
|
n = str() |
|
add_to_package( |
|
NumberedOptions(self.total_items, |
|
CustomsWeight = item.weight * item.qty, |
|
CustomsDescription = item.desc, |
|
CustomsQuantity = item.qty, |
|
CustomsValue = item.value * item.qty, |
|
CustomsCountry = item.origin |
|
), self, False |
|
) |
|
|
def finish(self): |
def finish(self): |
self.finished = True |
self.finished = True |
for item in self.queue: add_to_package(item, self, False) |
|
|
for item in self.queue: |
|
add_to_package(item, self, False) |
|
|
|
if self.total_items: |
|
add_to_package(Value(self.total_value), self, False) |
|
from decimal import Decimal |
|
if self['WeightOz', None] is None: |
|
raise OptionConflict( |
|
"Total package weight must be specified when" |
|
" Customs.Items are used" |
|
) |
|
oz = Decimal(self['WeightOz', None]) |
|
if oz < self.total_weight: |
|
raise OptionConflict( |
|
"Total item weight is %s oz, but total package weight is" |
|
" only %s oz" % (self.total_weight, oz) |
|
) |
|
if not self['CustomsFormType',None]or not self['ContentsType',None]: |
|
raise OptionConflict( |
|
"Customs form + content type must be specified with items" |
|
) |
|
|
|
|
class Batch: |
class Batch: |
"""An XML document and its corresponding package objects""" |
"""An XML document and its corresponding package objects""" |
self.batches.append(batch) |
self.batches.append(batch) |
|
|
|
|
|
@generic |
|
def add_to_package(ob, package, isdefault): |
|
"""Update `etree` to apply document info""" |
|
for ob in iter_options(ob): |
|
add_to_package(ob, package, isdefault) |
|
|
|
|
|
|
|
|
def clone(self, value): |
def clone(self, value): |
return Option(self.tag, value, self.attr) |
return Option(self.tag, value, self.attr) |
|
__call__ = clone |
def set(self, package, isdefault=False): |
def set(self, package, isdefault=False): |
old = package[self.tag, self.attr] |
old = package[self.tag, self.attr] |
if old is not None and old<>unicode(self.value): |
if old is not None and old<>unicode(self.value): |
if self.value is not None: |
if self.value is not None: |
package[self.tag, self.attr] = self.value |
package[self.tag, self.attr] = self.value |
|
|
|
def __repr__(self): |
|
if self.attr: |
|
return "%s.%s(%r)" % (self.tag, self.attr, self.value) |
|
return "%s(%r)" % (self.tag, self.value) |
|
|
@struct(OptionBase) |
@struct(OptionBase, __repr__ = OptionBase.__repr__.im_func) |
def Option(tag, value=None, attr=None): |
def Option(tag, value=None, attr=None): |
"""Object representing DAZzle XML text or attributes""" |
"""Object representing DAZzle XML text or attributes""" |
return tag, value, attr |
return tag, value, attr |
|
|
add_to_package.when_type(Option)(Option.set) |
|
|
|
|
|
|
|
|
add_to_package.when_type(Option)(Option.set) |
|
|
def _make_symbols(d, nattr, names, factory=Option, **kw): |
def _make_symbols(d, nattr, names, factory=Option, **kw): |
for name in names: |
for name in names: |
__all__.extend(names) |
__all__.extend(names) |
|
|
_make_globals( |
_make_globals( |
'attr', """ |
|
Prompt AbortOnError Test SkipUnverified AutoClose AutoPrintCustomsForms |
|
""".split(), tag='DAZzle', value='YES' |
|
) |
|
_make_globals( |
|
'attr', """ |
|
RegisteredMail InsuredMail CertifiedMail RestrictedDelivery ReturnReceipt |
|
CertificateOfMailing DeliveryConfirmation SignatureConfirmation COD |
|
""".split(), tag='Services', value='ON' |
|
) |
|
_make_globals( |
|
'tag', """ |
'tag', """ |
ReplyPostage BalloonRate NonMachinable OversizeRate Stealth SignatureWaiver |
ReplyPostage BalloonRate NonMachinable OversizeRate Stealth SignatureWaiver |
NoWeekendDelivery NoHolidayDelivery ReturnToSender CustomsCertify |
NoWeekendDelivery NoHolidayDelivery ReturnToSender CustomsCertify |
""".split(), value='TRUE' |
""".split(), value='TRUE' |
) |
) |
|
|
WeekendDelivery = ~NoWeekendDelivery |
|
HolidayDelivery = ~NoHolidayDelivery |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_make_globals( |
_make_globals( |
'tag', """ |
'tag', """ |
ToName ToTitle ToCompany ToCity ToState ToPostalCode ToZIP4 ToCountry |
ToName ToTitle ToCompany ToCity ToState ToPostalCode ToZIP4 ToCountry |
ToCarrierRoute ToReturnCode ToEmail ToPhone EndorsementLine ReferenceID |
ToCarrierRoute ToReturnCode ToEmail ToPhone EndorsementLine ReferenceID |
ToDeliveryPoint Description MailClass PackageType |
ToDeliveryPoint Description MailClass PackageType |
ContentsType CustomsFormType |
ContentsType CustomsFormType CustomsSigner |
|
|
WeightOz Width Length Depth CostCenter Value |
WeightOz Width Length Depth CostCenter Value |
""".split(), lambda tag: Option(tag).clone |
""".split(), lambda tag: Option(tag).clone |
) |
) |
|
|
NoPostage = MailClass('NONE') |
NoPostage = MailClass('NONE') |
InsuredMail = Option('Services', None, 'InsuredMail').clone |
WeekendDelivery = ~NoWeekendDelivery |
|
HolidayDelivery = ~NoHolidayDelivery |
|
|
|
def NumberedOptions(n, **kw): |
|
n = str(n) |
|
return [Option(k+n, v)for k, v in kw.items()] |
|
|
|
|
def Layout(filename): |
|
"""Return an option specifying the desired layout""" |
|
return Option('DAZzle', os.path.abspath(filename), 'Layout') |
|
|
|
def OutputFile(filename): |
|
"""Return an option specifying the desired layout""" |
|
return Option('DAZzle', os.path.abspath(filename), 'OutputFile') |
class Services: |
|
_make_symbols( |
|
locals(), 'attr', """ |
|
RegisteredMail InsuredMail CertifiedMail RestrictedDelivery ReturnReceipt |
|
CertificateOfMailing DeliveryConfirmation SignatureConfirmation COD |
|
""".split(), tag='Services', value='ON' |
|
) |
|
|
class Insurance: |
class Insurance: |
UPIC = InsuredMail('UPIC') |
UPIC = Services.InsuredMail('UPIC') |
Endicia = InsuredMail('ENDICIA') |
Endicia = Services.InsuredMail('ENDICIA') |
USPS = InsuredMail('ON') |
USPS = Services.InsuredMail |
NONE = ~USPS |
NONE = ~USPS |
|
|
def ToAddress(*lines): |
def ToAddress(*lines): |
assert len(lines)<=6 |
assert len(lines)<=6 |
return [Option('ToAddress'+str(n+1), v) for n, v in enumerate(lines)] |
return [NumberedOptions(n+1, ToAddress=v)[0] for n, v in enumerate(lines)] |
|
|
def ReturnAddress(*lines): |
def ReturnAddress(*lines): |
assert len(lines)<=6 |
assert len(lines)<=6 |
return [Option('ReturnAddress'+str(n+1), v) for n, v in enumerate(lines)] |
return [NumberedOptions(n+1, ReturnAddress=v)[0] for n, v in enumerate(lines)] |
|
|
def RubberStamp(n, text): |
def RubberStamp(n, text): |
assert 1<=n<=50 |
assert 1<=n<=50 |
return Option('RubberStamp'+str(n), text) |
return Option('RubberStamp'+str(n), text) |
|
|
|
Postcard = PackageType('POSTCARD') |
|
Envelope = PackageType('ENVELOPE') |
|
Flat = PackageType('FLAT') |
|
RectangularParcel = PackageType('RECTPARCEL') |
|
NonRectangularParcel = PackageType('NONRECTPARCEL') |
|
FlatRateEnvelope = PackageType('FLATRATEENVELOPE') |
|
FlatRateBox = PackageType('FLATRATEBOX') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DAZzle: |
|
_make_symbols( |
|
locals(), 'attr', """ |
|
Prompt AbortOnError Test SkipUnverified AutoClose AutoPrintCustomsForms |
|
""".split(), tag='DAZzle', value='YES' |
|
) |
|
@staticmethod |
|
def Layout(filename): |
|
"""Return an option specifying the desired layout""" |
|
return Option('DAZzle', os.path.abspath(filename), 'Layout') |
|
@staticmethod |
|
def OutputFile(filename): |
|
"""Return an option specifying the desired layout""" |
|
return Option('DAZzle', os.path.abspath(filename), 'OutputFile') |
|
|
|
Start = Option('DAZzle', attr='Start').clone |
|
Print = Start('PRINTING') |
|
Verify = Start('DAZ') |
|
|
class Domestic: |
class Domestic: |
FirstClass = MailClass('FIRST') |
FirstClass = MailClass('FIRST') |
GXG = MailClass('INTLGXG') |
GXG = MailClass('INTLGXG') |
GXGNoDoc = MailClass('INTLGXGNODOC') |
GXGNoDoc = MailClass('INTLGXGNODOC') |
|
|
Postcard = PackageType('POSTCARD') |
|
Envelope = PackageType('ENVELOPE') |
|
Flat = PackageType('FLAT') |
|
RectangularParcel = PackageType('RECTPARCEL') |
|
NonRectangularParcel = PackageType('NONRECTPARCEL') |
|
FlatRateEnvelope = PackageType('FLATRATEENVELOPE') |
|
FlatRateBox = PackageType('FLATRATEBOX') |
|
|
|
def DateAdvance(days): |
def DateAdvance(days): |
"""Return an option for the number of days ahead of time we're mailing""" |
"""Return an option for the number of days ahead of time we're mailing""" |
if not isinstance(days, int) or not (0<=days<=30): |
if not isinstance(days, int) or not (0<=days<=30): |
raise ValueError("DateAdvance() must be an integer from 0-30") |
raise ValueError("DateAdvance() must be an integer from 0-30") |
return Option('DateAdvance', str(days)) |
return Option('DateAdvance', days) |
|
|
Today = DateAdvance(0) |
Today = DateAdvance(0) |
Tomorrow = DateAdvance(1) |
Tomorrow = DateAdvance(1) |
|
|
Print = Option('DAZzle', 'PRINTING', 'Start') |
|
Verify = Option('DAZzle', 'DAZ', 'Start') |
|
|
|
|
|
|
|
|
|
class Customs: |
class Customs: |
_make_symbols( |
_make_symbols( |
lambda value: ContentsType(value.upper()) |
lambda value: ContentsType(value.upper()) |
) |
) |
|
|
Signer = Option('CustomsSigner').clone |
Signer = CustomsSigner |
Certify = Option('CustomsCertify', 'TRUE') |
Certify = CustomsCertify |
|
|
@struct() |
@struct() |
def Item(desc, weight, value, qty=1, origin='United States'): |
def Item(desc, weight, value, qty=1, origin='United States'): |
assert qty==int(qty) |
assert qty==int(qty) |
return desc, Decimal(weight), Decimal(value), int(qty), origin |
return desc, Decimal(weight), Decimal(value), int(qty), origin |
|
|
|
@add_to_package.when_type(Item) |
|
def _add_item(ob, package, isdefault): |
|
assert not isdefault, "Customs.Item objects can't be defaults" |
|
package.add_customs_item(ob) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|