def parse_error_response(self, response: etree.ElementBase) -> List[T.Error]: notifications = response.xpath(".//*[local-name() = $name]", name="Notifications") + response.xpath( ".//*[local-name() = $name]", name="Notification") return reduce(self._extract_error, notifications, [])
def _extract_package_rate( self, rates: List[T.QuoteDetails], detailNode: etree.ElementBase ) -> List[T.QuoteDetails]: rate = PRate.RatedShipmentType() rate.build(detailNode) if rate.NegotiatedRateCharges != None: total_charges = ( rate.NegotiatedRateCharges.TotalChargesWithTaxes or rate.NegotiatedRateCharges.TotalCharge ) taxes = rate.NegotiatedRateCharges.TaxCharges itemized_charges = rate.NegotiatedRateCharges.ItemizedCharges + taxes else: total_charges = rate.TotalChargesWithTaxes or rate.TotalCharges taxes = rate.TaxCharges itemized_charges = rate.ItemizedCharges + taxes extra_charges = itemized_charges + [rate.ServiceOptionsCharges] arrival = PRate.PickupType() [ arrival.build(arrival) for arrival in detailNode.xpath(".//*[local-name() = $name]", name="Arrival") ] currency_ = next(c.text for c in detailNode.xpath( ".//*[local-name() = $name]", name="CurrencyCode" )) return rates + [ T.QuoteDetails( carrier=self.client.carrier_name, currency=currency_, service_name=str(ShippingServiceCode(rate.Service.Code).name), service_type=rate.Service.Code, base_charge=float(rate.TransportationCharges.MonetaryValue), total_charge=float(total_charges.MonetaryValue), duties_and_taxes=reduce( lambda total, charge: total + float(charge.MonetaryValue), taxes or [], 0.0, ), discount=None, extra_charges=reduce( lambda total, charge: total + [ T.ChargeDetails( name=charge.Code, amount=float(charge.MonetaryValue), currency=charge.CurrencyCode, ) ], [charge for charge in extra_charges if charge != None], [], ), delivery_date=str(arrival.Date) ) ]
def parse_quote_response( self, response: etree.ElementBase ) -> Tuple[List[T.QuoteDetails], List[T.Error]]: details = response.xpath( ".//*[local-name() = $name]", name="FreightRateResponse" ) + response.xpath(".//*[local-name() = $name]", name="FreightRateResponse") if len(details) > 0: return self.parse_freight_rate_response(response) else: return self.parse_package_rate_response(response)
def _extract_quote( self, quotes: List[T.QuoteDetails], detailNode: etree.ElementBase ) -> List[T.QuoteDetails]: detail = RateReplyDetail() detail.build(detailNode) if not detail.RatedShipmentDetails: return quotes shipmentDetail: RatedShipmentDetail = detail.RatedShipmentDetails[0].ShipmentRateDetail delivery_ = reduce(lambda v, c: c.text, detailNode.xpath( ".//*[local-name() = $name]", name="DeliveryTimestamp" ), None) currency_ = reduce(lambda v, c: c.text, detailNode.xpath( ".//*[local-name() = $name]", name="Currency" ), None) Discounts_ = map( lambda d: T.ChargeDetails( name=d.RateDiscountType, amount=float(d.Amount.Amount), currency=currency_ ), shipmentDetail.FreightDiscounts, ) Surcharges_ = map( lambda s: T.ChargeDetails( name=s.SurchargeType, amount=float(s.Amount.Amount), currency=currency_ ), shipmentDetail.Surcharges, ) Taxes_ = map( lambda t: T.ChargeDetails(name=t.TaxType, amount=float(t.Amount.Amount), currency=currency_), shipmentDetail.Taxes, ) return quotes + [ T.QuoteDetails( carrier=self.client.carrier_name, service_name=detail.ServiceType, service_type=detail.ActualRateType, currency=currency_, delivery_date=datetime.strptime( delivery_, "%Y-%m-%dT%H:%M:%S" ).strftime("%Y-%m-%d") if delivery_ else None, base_charge=float(shipmentDetail.TotalBaseCharge.Amount), total_charge=float( shipmentDetail.TotalNetChargeWithDutiesAndTaxes.Amount ), duties_and_taxes=float(shipmentDetail.TotalTaxes.Amount), discount=float(shipmentDetail.TotalFreightDiscounts.Amount), extra_charges=list(Discounts_) + list(Surcharges_) + list(Taxes_), ) ]
def _extract_quote(self, postage_node: etree.ElementBase) -> QuoteDetails: postage: RateRes.PostageType = RateRes.PostageType() postage.build(postage_node) currency = "USD" services: List[RateRes.SpecialServiceType] = [ (lambda s: (s, s.build(svc)))(RateRes.SpecialServiceType())[0] for svc in postage_node.xpath(".//*[local-name() = $name]", name="SpecialService") ] def get(key: str) -> Any: return reduce(lambda r, v: v.text, postage_node.findall(key), None) return QuoteDetails( carrier=self.client.carrier_name, service_name=None, service_type=get("MailService"), base_charge=None, duties_and_taxes=None, total_charge=float(postage_node.find("Rate").text), currency=currency, delivery_date=postage.CommitmentDate, discount=None, extra_charges=[ ChargeDetails( name=SpecialService(str(svc.ServiceID)).name, amount=svc.Price, currency=currency, ) for svc in services ], )
def _extract_tracking(self, tracking_node: etree.ElementBase) -> TrackingDetails: tracking: TrackInfoType = TrackInfoType() tracking.build(tracking_node) details: List[TrackDetailType] = [ (lambda t: (t, t.build(detail)))(TrackDetailType())[0] for detail in tracking_node.xpath(".//*[local-name() = $name]", name="TrackDetail") ] return TrackingDetails( carrier=self.client.carrier_name, tracking_number=tracking.TrackInfoID, shipment_date=None, events=[ TrackingEvent( code=str(event.EventCode), date=event.EventDate, description=event.ActionCode, location=", ".join([ location for location in [ event.EventCity, event.EventState, event.EventCountry, str(event.EventZIPCode), ] if location is not None ]), time=event.EventTime, signatory=None, ) for event in details ], )
def parse_shipment_info( self, response: etree.ElementBase ) -> Tuple[T.ShipmentDetails, List[T.Error]]: shipment = (self._extract_shipment(response) if len( response.xpath(".//*[local-name() = $name]", name="shipment-id")) > 0 else None) return (shipment, self.parse_error_response(response))
def _extract_tracking( self, trackings: List[T.TrackingDetails], shipmentNode: etree.ElementBase) -> List[T.TrackingDetails]: trackDetail = Track.ShipmentType() trackDetail.build(shipmentNode) activityNodes = shipmentNode.xpath(".//*[local-name() = $name]", name="Activity") def buildActivity(node): activity = Track.ActivityType() activity.build(node) return activity activities = map(buildActivity, activityNodes) return trackings + [ T.TrackingDetails( carrier=self.client.carrier_name, tracking_number=trackDetail.InquiryNumber.Value, events=list( map( lambda a: T.TrackingEvent( date=str(a.Date), time=str(a.Time), code=a.Status.Code if a.Status else None, location=a.ActivityLocation.Address.City if a.ActivityLocation and a.ActivityLocation. Address else None, description=a.Status.Description if a.Status else None, ), activities, )), ) ]
def _extract_intl_quote(self, service_node: etree.ElementBase) -> QuoteDetails: service: IntlRateRes.ServiceType = IntlRateRes.ServiceType() service.build(service_node) currency = "USD" special_services: List[IntlRateRes.ExtraServiceType] = [ (lambda s: (s, s.build(svc)))(IntlRateRes.ExtraServiceType())[0] for svc in service_node.xpath(".//*[local-name() = $name]", name="ExtraService") ] return QuoteDetails( carrier=self.client.carrier_name, service_name=None, service_type=service.MailType, base_charge=None, duties_and_taxes=None, total_charge=service.Postage, currency=currency, delivery_date=service.GuaranteeAvailability, discount=None, extra_charges=[ ChargeDetails( name=ExtraService(special.ServiceID).name, amount=special.Price, currency=currency, ) for special in special_services ], )
def parse_tracking_summary( self, response: etree.ElementBase ) -> Tuple[List[T.TrackingDetails], List[T.Error]]: pin_summaries = response.xpath(".//*[local-name() = $name]", name="pin-summary") trackings: List[T.TrackingDetails] = reduce( self._extract_tracking, pin_summaries, [] ) return (trackings, self.parse_error_response(response))
def parse_rate_reply( self, response: etree.ElementBase ) -> Tuple[List[T.QuoteDetails], List[T.Error]]: rate_replys = response.xpath( ".//*[local-name() = $name]", name="RateReplyDetails" ) quotes: List[T.QuoteDetails] = reduce(self._extract_quote, rate_replys, []) return (quotes, self.parse_error_response(response))
def parse_process_shipment_reply( self, response: etree.ElementBase ) -> Tuple[T.ShipmentDetails, List[T.Error]]: details = response.xpath(".//*[local-name() = $name]", name="CompletedShipmentDetail") shipment: T.ShipmentDetails = self._extract_shipment( details[0]) if len(details) > 0 else None return (shipment, self.parse_error_response(response))
def parse_track_response( self, response: etree.ElementBase ) -> Tuple[List[T.TrackingDetails], List[T.Error]]: track_details = response.xpath(".//*[local-name() = $name]", name="Shipment") trackings: List[T.TrackingDetails] = reduce(self._extract_tracking, track_details, []) return (trackings, self.parse_error_response(response))
def parse_price_quotes( self, response: etree.ElementBase ) -> Tuple[List[T.QuoteDetails], List[T.Error]]: price_quotes = response.xpath(".//*[local-name() = $name]", name="price-quote") quotes: List[T.QuoteDetails] = reduce(self._extract_quote, price_quotes, []) return quotes, self.parse_error_response(response)
def parse_dct_response( self, response: etree.ElementBase ) -> Tuple[List[T.QuoteDetails], List[T.Error]]: qtdshp_list = response.xpath(".//*[local-name() = $name]", name="QtdShp") quotes: List[T.QuoteDetails] = reduce(self._extract_quote, qtdshp_list, []) return (quotes, self.parse_error_response(response))
def _xpath(node: etree.ElementBase, query: Text, *args, **kwargs) -> List[etree.ElementBase]: result = node.xpath(query, *args, **kwargs) if result is None: return [] if isinstance(result, etree.ElementBase): return [result] return result
def parse_shipment_response( self, response: etree.ElementBase ) -> Tuple[T.ShipmentDetails, List[T.Error]]: details = response.xpath( ".//*[local-name() = $name]", name="FreightShipResponse" ) + response.xpath(".//*[local-name() = $name]", name="ShipmentResponse") if len(details) > 0: shipmentNode = details[0] is_freight = "FreightShipResponse" in shipmentNode.tag shipment = ( self.parse_freight_shipment_response(shipmentNode) if is_freight else self.parse_package_shipment_response(shipmentNode) ) return ( shipment if len(details) > 0 else None, self.parse_error_response(response), )
def get_definition_name(res: etree.ElementBase) -> str: item_str = '' for elem in res.xpath('object/a'): for x in elem.iter(): for item in [x.text, x.tail]: if item is not None: if item.strip() != '': item_str += f' {item}' return item_str.strip()
def parse_track_response( self, response: etree.ElementBase ) -> Tuple[List[TrackingDetails], List[Error]]: return ( [ self._extract_tracking(node) for node in response.xpath(".//*[local-name() = $name]", name="TrackInfo") ], self.parse_error_response(response), )
def read_xml(context: Context, prop: Property, *, source=str, value: etree.ElementBase): result = value.xpath(source) if len(result) == 1: return result[0] elif len(result) == 0: return None else: context.error(f"More than one value returned for {source}: {value}")
def post_pr(xml: etree.ElementBase, url) -> (str, etree.ElementBase): parts = urlparse(url) for u in xml.xpath("//Request"): u.attrib['deploymentMode'] = 'test' pass for u in xml.xpath("//BrowserFormPost/URL"): u.text = "//%s/m2po/cxml/cartecho" % parts.netloc pass res = post(url, etree.tostring(xml, pretty_print=True)) if not res.ok: raise Exception("failed with status %s" % res.status_code) res_xml: etree.ElementBase = etree.fromstring(res.content) punchout_url = str(res_xml.xpath('string(//URL)')) return punchout_url, res_xml
def parse_rate_response( self, response: etree.ElementBase ) -> Tuple[List[QuoteDetails], List[Error]]: is_intl = response.tag == "IntlRateV2Response" quotes: List[QuoteDetails] = [ (self._extract_quote if not is_intl else self._extract_intl_quote)(package) for package in response.xpath( ".//*[local-name() = $name]", name="Postage" if not is_intl else "Service", ) ] return quotes, self.parse_error_response(response)
def _extract_quote(self, quotes: List[T.QuoteDetails], detailNode: etree.ElementBase) -> List[T.QuoteDetails]: detail = RateReplyDetail() detail.build(detailNode) if not detail.RatedShipmentDetails: return quotes shipmentDetail: RatedShipmentDetail = detail.RatedShipmentDetails[ 0].ShipmentRateDetail currency_ = next(c.text for c in detailNode.xpath( ".//*[local-name() = $name]", name="Currency")) Discounts_ = map( lambda d: T.ChargeDetails(name=d.RateDiscountType, amount=float(d.Amount.Amount), currency=currency_), shipmentDetail.FreightDiscounts, ) Surcharges_ = map( lambda s: T.ChargeDetails(name=s.SurchargeType, amount=float(s.Amount.Amount), currency=currency_), shipmentDetail.Surcharges, ) Taxes_ = map( lambda t: T.ChargeDetails(name=t.TaxType, amount=float(t.Amount.Amount), currency=currency_), shipmentDetail.Taxes, ) return quotes + [ T.QuoteDetails( carrier=self.client.carrier_name, service_name=detail.ServiceType, service_type=detail.ActualRateType, currency=currency_, base_charge=float(shipmentDetail.TotalBaseCharge.Amount), total_charge=float( shipmentDetail.TotalNetChargeWithDutiesAndTaxes.Amount), duties_and_taxes=float(shipmentDetail.TotalTaxes.Amount), discount=float(shipmentDetail.TotalFreightDiscounts.Amount), extra_charges=list(Discounts_) + list(Surcharges_) + list(Taxes_), ) ]
def _extract_freight_rate( self, rates: List[T.QuoteDetails], detailNode: etree.ElementBase ) -> List[T.QuoteDetails]: detail = Rate.FreightRateResponse() detail.build(detailNode) total_charge = [r for r in detail.Rate if r.Type.Code == "AFTR_DSCNT"][0] Discounts_ = [ T.ChargeDetails( name=r.Type.Code, currency=r.Factor.UnitOfMeasurement.Code, amount=float(r.Factor.Value), ) for r in detail.Rate if r.Type.Code == "DSCNT" ] Surcharges_ = [ T.ChargeDetails( name=r.Type.Code, currency=r.Factor.UnitOfMeasurement.Code, amount=float(r.Factor.Value), ) for r in detail.Rate if r.Type.Code not in ["DSCNT", "AFTR_DSCNT", "DSCNT_RATE", "LND_GROSS"] ] extra_charges = Discounts_ + Surcharges_ currency_ = next(c.text for c in detailNode.xpath( ".//*[local-name() = $name]", name="CurrencyCode" )) return rates + [ T.QuoteDetails( carrier=self.client.carrier_name, currency=currency_, service_name=detail.Service.Description, service_type=detail.Service.Code, base_charge=float(detail.TotalShipmentCharge.MonetaryValue), total_charge=float(total_charge.Factor.Value or 0.0), duties_and_taxes=reduce(lambda r, c: r + c.amount, Surcharges_, 0.0), discount=reduce(lambda r, c: r + c.amount, Discounts_, 0.0), extra_charges=extra_charges, ) ]
def text(element: etree.ElementBase, name: str, is_attribute: bool = False, nullable: bool = False) -> Optional[str]: if is_attribute: if nullable: return element.attrib.get(name) else: return element.attrib[name] value = element.xpath(name) if (not value) and nullable: return None if len(value) != 1: raise ValueError( f"Only one '{name}' tag per tree is supported, {len(value)} found" ) return value[0].text
def _xpath_one(node: etree.ElementBase, query: Text, *args, **kwargs) -> etree.ElementBase: result = node.xpath(query, *args, **kwargs) if not result: raise ValueError(f'Query {query} has no result.') return result[0]
def tei_xpath(parent: etree.ElementBase, xpath: str) -> List[etree.ElementBase]: return parent.xpath(xpath, namespaces=TEI_NS_MAP)
def _extract_shipment(self, response: etree.ElementBase) -> T.ShipmentDetails: is_non_contract = (len( response.xpath(".//*[local-name() = $name]", name="non-contract-shipment-info")) > 0) info = (NCShipment.NonContractShipmentInfoType() if is_non_contract else Shipment.ShipmentInfoType()) data = (NCShipment.NonContractShipmentReceiptType() if is_non_contract else Shipment.ShipmentPriceType()) info.build( response.xpath( ".//*[local-name() = $name]", name=("non-contract-shipment-info" if is_non_contract else "shipment-info"), )[0]) data.build( response.xpath( ".//*[local-name() = $name]", name=("non-contract-shipment-receipt" if is_non_contract else "shipment-price"), )[0]) currency_ = data.cc_receipt_details.currency if is_non_contract else "CAD" return T.ShipmentDetails( carrier=self.client.carrier_name, tracking_numbers=[info.tracking_pin], total_charge=T.ChargeDetails( name="Shipment charge", amount=data.cc_receipt_details.charge_amount if is_non_contract else data.due_amount, currency=currency_, ), charges=([ T.ChargeDetails(name="base-amount", amount=data.base_amount, currency=currency_), T.ChargeDetails(name="gst-amount", amount=data.gst_amount, currency=currency_), T.ChargeDetails(name="pst-amount", amount=data.pst_amount, currency=currency_), T.ChargeDetails(name="hst-amount", amount=data.hst_amount, currency=currency_), ] + [ T.ChargeDetails( name=adjustment.adjustment_code, amount=adjustment.adjustment_amount, currency=currency_, ) for adjustment in data.adjustments.get_adjustment() ] + [ T.ChargeDetails( name=option.option_code, amount=option.option_price, currency=currency_, ) for option in data.priced_options.get_priced_option() ]), shipment_date=data.service_standard.expected_delivery_date, services=([data.service_code] + [ option.option_code for option in data.priced_options.get_priced_option() ]), documents=[ link.get("href") for link in response.xpath(".//*[local-name() = $name]", name="link") if link.get("rel") == "label" ], reference=T.ReferenceDetails(value=info.shipment_id, type="Shipment Id"), )
def parse_error_response(self, response: etree.ElementBase) -> List[T.Error]: messages = response.xpath(".//*[local-name() = $name]", name="message") return reduce(self._extract_error, messages, [])
def alto_xpath(parent: etree.ElementBase, xpath: str) -> List[etree.ElementBase]: return parent.xpath(xpath, namespaces=ALTO_NS_MAP)