def build_ebay_item_model(single_item_response, ebay_seller_id=None, attempt_parse=False, measurement_parse_strategy='default', affiliate_url=None): """Takes an ebay_seller_id, and GetSingleItem ebay API response, and affiliate_url (if provided) and returns a appropriately configured Item model. Parameters ---------- single_item_response : dict ebay_seller_id : str attempt_parse : bool if true: - response will be searched for a description - will attempt to locate parser - parser will attempt to parse measurements and identify clothing type affiliate_url = str or None Returns ------- configured sqlalchemy app.models.Item model object if attempt_parse=True and any part of the parsing process fails, will return None """ logger.debug('Attempting to create model') try: assert ebay_seller_id is not None except AssertionError: raise ValueError('ebay_seller_id not provided') try: seller = EbaySeller.query.filter( EbaySeller.ebay_seller_id == ebay_seller_id).one() except NoResultFound: # Couldn't find seller in database. Should fail. raise NoResultFound( 'No matching seller found for <{}> in the database'.format( ebay_seller_id)) logger.debug('Seller found. Using seller={}'.format(seller)) # All times from these responses are UTC # http://developer.ebay.com/devzone/Shopping/docs/CallRef/types/simpleTypes.html#dateTime m = Item() m.last_access_date = datetime.strptime(single_item_response['Timestamp'], '%Y-%m-%dT%H:%M:%S.%fZ') r = single_item_response['Item'] m.seller = seller m.ebay_item_id = int(r['ItemID']) m.end_date = datetime.strptime(r['EndTime'], '%Y-%m-%dT%H:%M:%S.%fZ') m.ebay_title = r['Title'] m.primary_category_number = int(r['PrimaryCategoryID']) m.current_price = int( decimal.Decimal(r['ConvertedCurrentPrice']['value']) * 100) m.ebay_url = r['ViewItemURLForNaturalSearch'] m.ebay_affiliate_url = affiliate_url if attempt_parse: # The thought process here is that with attempt_parse=True, it can be assumed that # for any item found, the measurements are also wanted. From that, if the # measurements cannot be created, the entire item should be discarded. # Thoughts: Maybe ParseResult should have an error attribute? This attribute should # a list of all errors encountered during parsing. Then, once ParseResult is # dehydrated, this list of errors is checked. If empty, proceed as normal. Else, # discard this item. This is better than assuming an invalid listing when no # measurements are returned. I can't think of scenarios where I want an item in the # database that without measurements, but... maybe I should keep those items anyways? logger.debug('Will attempt to parse') try: assert seller.template_parser is not None except AssertionError: logger.warn( 'No template parser associated with <{}>. Returning None.'. format(seller)) return None # raise ValueError( # 'No template parser associated with <{}>'.format(seller)) try: # what error does this raise if it fails? parser_file_num = seller.template_parser.file_name_number except: raise logger.debug('A parser for this seller has been found') logger.debug('Passing off to master_parse') parse_input_json_str = json.dumps({ 'parser_id_num': parser_file_num, 'parse_strategy': measurement_parse_strategy, 'response': single_item_response }) try: parser_response = master_parse.parse(parse_input_json_str) except Exception: logger.error('Something failed with the parser', exc_info=True) return None logger.debug('Attempting to rehydrate parse result') parse_result = ParseResult.rehydrate(parser_response) if parse_result.clothing_type is None: logger.warn( 'Parser could not identify clothing type. Returning None') return None try: clothing_category = ClothingCategory.query.filter( ClothingCategory.clothing_category_name == parse_result.clothing_type).one() except NoResultFound: logger.warn( 'The parser returned a clothing category that could not be found ' 'by the database. Parser returned ' 'clothing_type={}. Returning None.'.format( parse_result.clothing_type)) return None m.assigned_clothing_category = clothing_category logger.debug( 'Parser identified clothing category. Its db association is={}'. format(m.assigned_clothing_category)) for concern in parse_result.meta['concerns']: logger.warn( 'Parser returned this concern about what it was sent: {}'. format(concern)) for msmt in parse_result.measurements: msmt_model = build_item_measurement( clothing_cat_string_name=msmt.category, attribute=msmt.attribute, measurement_value=msmt.value) m.measurements.append(msmt_model) logger.info('Model created successfully. Model={}'.format(m)) return m