Source code for mws.apis.feeds

"""Amazon MWS Feeds API."""

import datetime
import string
from enum import Enum

from mws import MWS
from mws.decorators import next_token_action
from mws.utils.crypto import calc_md5
from mws.utils.deprecation import kwargs_renamed_for_v11
from mws.utils.params import coerce_to_bool, enumerate_param

def clean_feed_option_val(val):
    """Cleans ``val`` for use in the FeedOptions parameter when submitting a feed
    for "_UPLOAD_VAT_INVOICE_" feed type.

    - Values of `None` are returned as `None`. The calling method is expected to
      exclude the key-value pair for this value from its output.
    - Booleans (True/False) will be converted to string in lowercase ('true'/'false')
    - datetime.datetime or instances will be formatted as ISO-8601 strings

    For any other value

    Amazon states the only safe characters are:

        ``,`` (commas), ``/`` and ``\\`` (slashes), ``-`` (dash), ``_`` (underscore),
        ``;`` (semi colon), ``:`` (colon), ``#``, 0-9, A-Z, a-z, spaces

    Any character not matching the above set is stripped.
    if val is None:
        return None

    if val is True or val is False:
        # Stringify and lowercase the boolean
        val = str(val).lower()
    elif isinstance(val, (datetime.datetime,
        # Convert that date to ISO-8601 format string.
        # We explicitly set microseconds to 0, however, because the ``.`` character
        # (used to denote microseconds, if present) is NOT permitted in final output.
        val = val.replace(microsecond=0).isoformat()
        # NOTE granted, users are likely not going to send datetime objects through
        # this method. But it's best to be safe!

    # Convert val from any other type into string before further processing.
    val = str(val)

    # Only the following characters are permitted in Amazon output
    # (Note the intentional space character added at the end for clarity!)
    permitted = string.ascii_letters + string.digits + ",\\/-_;:#" + " "

    # Join permitted characters and return the result.
    return "".join(c for c in val if c in permitted)

def feed_options_str(feed_options):
    """Convert a ``feed_options`` dict into a formatted metadata string,
    for use with the SubmitFeed ``FeedOptions`` parameter when submitting
    VAT invoices.


    .. code-block:: python

        feed_opts = {'orderid': '407-XXXXXX-6760332', 'invoicenumber': 51}
        opts_str = feed_options_str(feed_opts)
        # 'metadata:orderid=407-XXXXXX-6760332;metadata:invoicenumber=51'

    See Amazon documentation, `Invoice Uploader Developer Guide (PDF)
    (section 6.4), for details.
    if not feed_options:
        return None
    if not isinstance(feed_options, dict):
        raise ValueError("`feed_options` should be a dict or None")
    output = []
    for key, val in feed_options.items():
        clean_val = clean_feed_option_val(val)
        if clean_val is not None:
    return ";".join(output)

[docs]class Feeds(MWS): """Amazon MWS Feeds API. Docs: """ URI = "/Feeds/2009-01-01" ACCOUNT_TYPE = "Merchant" NEXT_TOKEN_OPERATIONS = [ "GetFeedSubmissionList", ]
[docs] @kwargs_renamed_for_v11([("marketplaceids", "marketplace_ids")]) def submit_feed( self, feed, feed_type, feed_options=None, marketplace_ids=None, amazon_order_id=None, document_type=None, content_type="text/xml", purge=False, ): """The `SubmitFeed operation. <>`_ Uploads a feed for processing by Amazon MWS. Requires ``feed``, a file in XML or flat-file format encoded to bytes; and ``feed_type``, a string detailing a `FeedType enumeration <>`_. All other parameters may change depending on the ``feed_type`` you select. See Amazon docs for details. ``feed_options`` is used for ``feed_type`` "_UPLOAD_VAT_INVOICE_", to provide FeedOption metadata. See `Invoice Uploader Developer Guide (PDF) <>`_, for details. Can accept a dict of simple key-value pairs, which will be converted to the proper string format automatically. ``marketplace_ids`` accepts a list of one or more marketplace IDs where you want the feed to be applied. Can also accept a single marketplace ID as a string. ``amazon_order_id`` and ``document_type`` are used for ``feed_type`` "_POST_EASYSHIP_DOCUMENTS_", used for Amazon Easy Ship orders (available only in India marketplace). Provide an Amazon Order ID as a string and the type of PDF document ("ShippingLabel", "Invoice", or "Warranty"; or `None` to get all). ``content_type`` sets the "Content-Type" request header, indicating the type of file being sent. Defaults to ``"text/xml"``. ``purge`` enables Amazon's "purge and replace" functionality. Set to ``True`` to purge and replace existing data, otherwise use ``False`` (the default). Only applies to product-related flat file feed types. **Use only in exceptional cases.** Usage is throttled to allow only one purge and replace within a 24-hour period. """ if isinstance(feed_options, dict): # Convert dict of options to str value feed_options = feed_options_str(feed_options) if purge is not None: purge = coerce_to_bool(purge) data = { "FeedType": feed_type, "FeedOptions": feed_options, "PurgeAndReplace": purge, } # for feed type _POST_EASYSHIP_DOCUMENTS_ # check if amazon_order_id: data.update({"AmazonOrderId": amazon_order_id}) # by default all document PDFs are included # allowed values: ShippingLabel, Invoice, Warranty if document_type: data.update({"DocumentType": document_type}) data.update(enumerate_param("MarketplaceIdList.Id.", marketplace_ids)) # Add headers to this request. extra_headers = {"Content-MD5": calc_md5(feed), "Content-Type": content_type} return self.make_request( "SubmitFeed", data, body=feed, extra_headers=extra_headers, )
[docs] @kwargs_renamed_for_v11( [ ("feedids", "feed_ids"), ("feedtypes", "feed_types"), ("processingstatuses", "processing_statuses"), ("fromdate", "from_date"), ("todate", "to_date"), ] ) @next_token_action("GetFeedSubmissionList") def get_feed_submission_list( self, feed_ids=None, max_count=None, feed_types=None, processing_statuses=None, from_date=None, to_date=None, next_token=None, ): """Returns a list of all feed submissions submitted between `from_date` and `to_date`. If these params are omitted, defaults to the previous 90 days. Pass `next_token` to call "GetFeedSubmissionListByNextToken" instead. Docs: """ data = { "MaxCount": max_count, "SubmittedFromDate": from_date, "SubmittedToDate": to_date, } data.update(enumerate_param("FeedSubmissionIdList.Id", feed_ids)) data.update(enumerate_param("FeedTypeList.Type.", feed_types)) data.update( enumerate_param("FeedProcessingStatusList.Status.", processing_statuses) ) return self.make_request("GetFeedSubmissionList", data)
[docs] def get_feed_submission_list_by_next_token(self, token): """Alias for `get_feed_submission_list(next_token=token)`. Docs: """ return self.get_feed_submission_list(next_token=token)
[docs] @kwargs_renamed_for_v11( [ ("feedtypes", "feed_types"), ("processingstatuses", "processing_statuses"), ("fromdate", "from_date"), ("todate", "to_date"), ] ) def get_feed_submission_count( self, feed_types=None, processing_statuses=None, from_date=None, to_date=None ): """Returns a count of the feeds submitted between `from_date` and `to_date`. If these params are omitted, defaults to the previous 90 days. Docs: """ data = { "SubmittedFromDate": from_date, "SubmittedToDate": to_date, } data.update(enumerate_param("FeedTypeList.Type.", feed_types)) data.update( enumerate_param("FeedProcessingStatusList.Status.", processing_statuses) ) return self.make_request("GetFeedSubmissionCount", data)
[docs] @kwargs_renamed_for_v11( [ ("feedids", "feed_ids"), ("feedtypes", "feed_types"), ("fromdate", "from_date"), ("todate", "to_date"), ] ) def cancel_feed_submissions( self, feed_ids=None, feed_types=None, from_date=None, to_date=None ): """Cancels one or more feed submissions and returns a count of the feed submissions that were canceled. Docs: """ data = { "SubmittedFromDate": from_date, "SubmittedToDate": to_date, } data.update(enumerate_param("FeedSubmissionIdList.Id.", feed_ids)) data.update(enumerate_param("FeedTypeList.Type.", feed_types)) return self.make_request("CancelFeedSubmissions", data)
[docs] @kwargs_renamed_for_v11( [ ("feedid", "feed_id"), ] ) def get_feed_submission_result(self, feed_id): """Returns the feed processing report and the Content-MD5 header. Docs: """ data = {"FeedSubmissionId": feed_id} return self.make_request("GetFeedSubmissionResult", data, result_key="Message")
class FeedProcessingStatus(str, Enum): """Enumerates all the feed processing status values that are available through the Feeds API section. `MWS Docs: FeedProcessingStatus enumeration <>`_ """ AWAITING_ASYNCHRONOUS_REPLY = "_AWAITING_ASYNCHRONOUS_REPLY_" """The request is being processed, but is waiting for external information before it can complete. """ CANCELLED = "_CANCELLED_" """The request has been aborted due to a fatal error.""" DONE = "_DONE_" """The request has been processed. You can call the GetFeedSubmissionResult operation to receive a processing report that describes which records in the feed were successful and which records generated errors. """ IN_PROGRESS = "_IN_PROGRESS_" """The request is being processed.""" IN_SAFETY_NET = "_IN_SAFETY_NET_" """The request is being processed, but the system has determined that there is a potential error with the feed (for example, the request will remove all inventory from a seller's account.) An Amazon seller support associate will contact the seller to confirm whether the feed should be processed. """ SUBMITTED = "_SUBMITTED_" """The request has been received, but has not yet started processing.""" UNCONFIRMED = "_UNCONFIRMED_" """The request is pending.""" class FeedType(str, Enum): """Enumerates all the feed types that are available through the Feeds API section. `MWS Docs: FeedType enumeration <>`_ Please refer to MWS documentation for details on each FeedType, including usage, template files, and additional information links. """ POST_PRODUCT_DATA = "_POST_PRODUCT_DATA_" """Product Feed""" POST_INVENTORY_AVAILABILITY_DATA = "_POST_INVENTORY_AVAILABILITY_DATA_" """Inventory Feed""" POST_PRODUCT_OVERRIDES_DATA = "_POST_PRODUCT_OVERRIDES_DATA_" """Overrides Feed""" POST_PRODUCT_PRICING_DATA = "_POST_PRODUCT_PRICING_DATA_" """Pricing Feed""" POST_PRODUCT_IMAGE_DATA = "_POST_PRODUCT_IMAGE_DATA_" """Product Images Feed""" POST_PRODUCT_RELATIONSHIP_DATA = "_POST_PRODUCT_RELATIONSHIP_DATA_" """Relationships Feed""" POST_FLAT_FILE_INVLOADER_DATA = "_POST_FLAT_FILE_INVLOADER_DATA_" """Flat File Inventory Loader Feed""" POST_FLAT_FILE_LISTINGS_DATA = "_POST_FLAT_FILE_LISTINGS_DATA_" """Flat File Listings Feed""" POST_FLAT_FILE_BOOKLOADER_DATA = "_POST_FLAT_FILE_BOOKLOADER_DATA_" """Flat File Book Loader Feed""" POST_FLAT_FILE_CONVERGENCE_LISTINGS_DATA = ( "_POST_FLAT_FILE_CONVERGENCE_LISTINGS_DATA_" ) """Flat File Music Loader Feed""" POST_FLAT_FILE_VIDEO_LOADER = "_POST_FLAT_FILE_LISTINGS_DATA_" """Flat File Video Loader Feed""" POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA = ( "_POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA_" ) """Flat File Price and Quantity Update Feed""" POST_UIEE_BOOKLOADER_DATA = "_POST_UIEE_BOOKLOADER_DATA_" """UIEE Inventory Feed""" POST_STD_ACES_DATA = "_POST_STD_ACES_DATA_" """ACES 3.0 Data (Automotive Part Finder) Feed""" POST_ORDER_ACKNOWLEDGEMENT_DATA = "_POST_ORDER_ACKNOWLEDGEMENT_DATA_" """Order Acknowledgement Feed""" POST_PAYMENT_ADJUSTMENT_DATA = "_POST_PAYMENT_ADJUSTMENT_DATA_" """Order Adjustments Feed""" POST_ORDER_FULFILLMENT_DATA = "_POST_ORDER_FULFILLMENT_DATA_" """Order Fulfillment Feed""" POST_INVOICE_CONFIRMATION_DATA = "_POST_INVOICE_CONFIRMATION_DATA_" """Invoice Confirmation Feed""" POST_EXPECTED_SHIP_DATE_SOD = "_POST_EXPECTED_SHIP_DATE_SOD_" """Sourcing On Demand Feed (Japan only)""" POST_FLAT_FILE_ORDER_ACKNOWLEDGEMENT_DATA = ( "_POST_FLAT_FILE_ORDER_ACKNOWLEDGEMENT_DATA_" ) """Flat File Order Acknowledgement Feed""" POST_FLAT_FILE_PAYMENT_ADJUSTMENT_DATA = "_POST_FLAT_FILE_PAYMENT_ADJUSTMENT_DATA_" """Flat File Order Adjustments Feed""" POST_FLAT_FILE_FULFILLMENT_DATA = "_POST_FLAT_FILE_FULFILLMENT_DATA_" """Flat File Order Fulfillment Feed""" POST_EXPECTED_SHIP_DATE_SOD_FLAT_FILE = "_POST_EXPECTED_SHIP_DATE_SOD_FLAT_FILE_" """Flat File Sourcing On Demand Feed (Japan only)""" POST_FULFILLMENT_ORDER_REQUEST_DATA = "_POST_FULFILLMENT_ORDER_REQUEST_DATA_" """FBA Fulfillment Order Feed""" POST_FULFILLMENT_ORDER_CANCELLATION_REQUEST_DATA = ( "_POST_FULFILLMENT_ORDER_CANCELLATION_REQUEST_DATA_" ) """FBA Fulfillment Order Cancellation Feed""" POST_FBA_INBOUND_CARTON_CONTENTS = "_POST_FBA_INBOUND_CARTON_CONTENTS_" """FBA Inbound Shipment Carton Information Feed""" POST_FLAT_FILE_FULFILLMENT_ORDER_REQUEST_DATA = ( "_POST_FLAT_FILE_FULFILLMENT_ORDER_REQUEST_DATA_" ) """Flat File FBA Fulfillment Order Feed""" POST_FLAT_FILE_FULFILLMENT_ORDER_CANCELLATION_REQUEST_DATA = ( "_POST_FLAT_FILE_FULFILLMENT_ORDER_CANCELLATION_REQUEST_DATA_" ) """Flat File FBA Fulfillment Order Cancellation Feed""" POST_FLAT_FILE_FBA_CREATE_INBOUND_PLAN = "_POST_FLAT_FILE_FBA_CREATE_INBOUND_PLAN_" """Flat File FBA Create Inbound Shipment Plan Feed""" POST_FLAT_FILE_FBA_UPDATE_INBOUND_PLAN = "_POST_FLAT_FILE_FBA_UPDATE_INBOUND_PLAN_" """Flat File FBA Update Inbound Shipment Plan Feed""" POST_FLAT_FILE_FBA_CREATE_REMOVAL = "_POST_FLAT_FILE_FBA_CREATE_REMOVAL_" """Flat File FBA Create Removal Feed""" RFQ_UPLOAD_FEED = "_RFQ_UPLOAD_FEED_" """Flat File Manage Quotes Feed""" POST_EASYSHIP_DOCUMENTS = "_POST_EASYSHIP_DOCUMENTS_" """Easy Ship Feed""" # Attach enums to Feeds class Feeds.FeedProcessingStatus = FeedProcessingStatus Feeds.FeedType = FeedType