Rev 6903 | Rev 6962 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
'''Created on 23-Mar-2010@author: ashish'''from elixir import *from functools import partialfrom shop2020.clients.CatalogClient import CatalogClientfrom shop2020.clients.HelperClient import HelperClientfrom shop2020.clients.InventoryClient import InventoryClientfrom shop2020.config.client.ConfigClient import ConfigClientfrom shop2020.model.v1.catalog.impl import DataServicefrom shop2020.model.v1.catalog.impl.CategoryManager import CategoryManagerfrom shop2020.model.v1.catalog.impl.Convertors import to_t_item, to_t_sourcefrom shop2020.model.v1.catalog.impl.DataService import Item, ItemChangeLog, \Category, EntityIDGenerator, SimilarItems, ProductNotification, Source, \SourceItemPricing, AuthorizationLog, VoucherItemMapping, CategoryVatMaster, \OOSTracker,EntityTag,ItemInsurerMapping, Insurer, Banner, BannerMapfrom shop2020.thriftpy.model.v1.catalog.ttypes import status, ItemShippingInfo, \ItemType, PremiumTypefrom shop2020.thriftpy.model.v1.inventory.ttypes import \InventoryServiceException, IgnoredInventoryUpdateItemsfrom shop2020.utils import EmailAttachmentSenderfrom shop2020.utils.EmailAttachmentSender import mailfrom shop2020.utils.Utils import to_py_date, log_risky_flagfrom sqlalchemy import desc, ascfrom sqlalchemy.sql.expression import or_, distinct, func, and_from string import Templateimport datetimeimport sysimport threadingimport urllib2sourceId = int(ConfigClient().get_property("sourceid"))to_addresses = ["khushal.bhatia@shop2020.in", "chandan.kumar@shop2020.in", "chaitnaya.vats@shop2020.in"]mail_user = "cnc.center@shop2020.in"mail_password = "5h0p2o2o"source_name = "Saholic"source_url = "www.saholic.com"skippedItems = { 175 : [27, 2160, 2175, 2163, 2158, 7128, 26, 2154],193 : [5839] }def initialize(dbname='catalog', db_hostname="localhost"):DataService.initialize(dbname, db_hostname)def get_all_items_by_status(status, offset=0, limit=None):query = Item.queryif status is not None:query = query.filter_by(status=status)query = query.order_by(Item.product_group, Item.brand, Item.model_number, Item.model_name).offset(offset)if limit:query = query.limit(limit)items = query.all()return itemsdef get_all_alive_items():query = Item.queryquery = query.filter(Item.status!=status.DELETED).filter(Item.status!=status.PHASED_OUT)items = query.all()return itemsdef get_all_items(is_active, offset=0, limit=None):if is_active:items = get_all_items_by_status(status.ACTIVE, offset, limit)else:items = get_all_items_by_status(None, offset, limit)return itemsdef get_item_count_by_status(use_status, status):if use_status:return Item.query.filter_by(status=status).count()else:return Item.query.count()def get_item(item_id):item = Item.get_by(id=item_id)return itemdef get_items_by_catalog_id(catalog_id):query = Item.query.filter_by(catalog_item_id=catalog_id)try:items = query.all()return itemsexcept Exception as ex:print exraise InventoryServiceException(109, "Item not found")def is_valid_catalog_id(catalog_id):item = Item.query.filter_by(catalog_item_id=catalog_id).first()if item is not None:return Trueelse:return Falsedef is_active(item_id):t_item_shipping_info = ItemShippingInfo()try:item = get_item(item_id)t_item_shipping_info.isRisky = item.riskyclient = InventoryClient().get_client()itemInfo = client.getItemAvailabilityAtLocation(item.id, sourceId)warehouse_id = itemInfo[0]if item.risky and item.status == status.ACTIVE:availability = client.getItemAvailibilityAtWarehouse(warehouse_id, item_id)if availability <= 0:add_status_change_log(item, status.PAUSED_BY_RISK)item.status = status.PAUSED_BY_RISKitem.status_description = "This item is currently out of stock"session.commit()__send_mail_for_oos_item(item)#This will clear cache from tomcat__clear_homepage_cache()else:availability = itemInfo[4]t_item_shipping_info.isActive = (item.status == status.ACTIVE)t_item_shipping_info.quantity = availabilityexcept InventoryServiceException:print "[ERROR] Unexpected error:", sys.exc_info()[0]return t_item_shipping_infodef get_item_status_description(itemId):item = get_item(itemId)return item.status_descriptiondef update_item(item):if not item:raise InventoryServiceException(108, "Bad item in request")if not item.id:raise InventoryServiceException(101, "Missing id for update")validate_item_prices(item)ds_item = get_item(item.id)message = ""if not ds_item:raise InventoryServiceException(101, "Item missing in our database")if item.productGroup:ds_item.product_group = item.productGroupif item.brand:ds_item.brand = item.brandif item.modelNumber:ds_item.model_number = item.modelNumberds_item.color = item.colords_item.model_name = item.modelNameds_item.category = item.categoryif item.category in [10001, 10002, 10003, 10004, 10005]:ds_item.preferredInsurer = 1ds_item.comments = item.commentsds_item.catalog_item_id = item.catalogItemIdds_item.mrp = item.mrpif ds_item.sellingPrice or item.sellingPrice:if ds_item.sellingPrice != item.sellingPrice:message += "Selling Price is changed from {0} to {1}.\n".format(ds_item.sellingPrice, item.sellingPrice)ds_item.sellingPrice = item.sellingPriceds_item.weight = item.weightds_item.showSellingPrice = item.showSellingPriceif item.startDate:ds_item.startDate = to_py_date(item.startDate)ds_item.startDate = ds_item.startDate.replace(hour=0,second=0,minute=0)if item.itemStatus == status.COMING_SOON and ds_item.startDate < datetime.datetime.now() :item.itemStatus = status.ACTIVEitem.status_description = "This item is active"else:ds_item.startDate = Noneif ds_item.status != item.itemStatus:add_status_change_log(ds_item, item.itemStatus)if item.itemStatus == status.PHASED_OUT:message += "Item is phased out."ds_item.status = item.itemStatusif item.status_description:ds_item.status_description = item.status_descriptionif item.retireDate:ds_item.retireDate = to_py_date(item.retireDate)else:ds_item.retireDate = Noneif item.expectedArrivalDate:ds_item.expectedArrivalDate = to_py_date(item.expectedArrivalDate)else:ds_item.expectedArrivalDate = Noneif item.comingSoonStartDate:ds_item.comingSoonStartDate = to_py_date(item.comingSoonStartDate)ds_item.comingSoonStartDate = ds_item.comingSoonStartDate.replace(hour=0,second=0,minute=0)else:ds_item.comingSoonStartDate = Noneds_item.feature_id = item.featureIdds_item.feature_description = item.featureDescriptionif ds_item.bestDealText or item.bestDealText:if item.bestDealText != ds_item.bestDealText:message += "Promotion text is changed from '{0}' to '{1}'.\n".format(ds_item.bestDealText, item.bestDealText)ds_item.bestDealText = item.bestDealTextds_item.bestDealValue = item.bestDealValueds_item.bestSellingRank = item.bestSellingRankif ds_item.bestDealsDetailsText or item.bestDealsDetailsText:if item.bestDealsDetailsText != ds_item.bestDealsDetailsText:message += "Best deals details text is changed from '{0}' to '{1}'.\n".format(ds_item.bestDealsDetailsText, item.bestDealsDetailsText)ds_item.bestDealsDetailsText = item.bestDealsDetailsTextif ds_item.bestDealsDetailsLink or item.bestDealsDetailsLink:if item.bestDealsDetailsLink != ds_item.bestDealsDetailsLink:message += "Best deals details link is changed from '{0}' to '{1}'.\n".format(ds_item.bestDealsDetailsLink, item.bestDealsDetailsLink)ds_item.bestDealsDetailsLink = item.bestDealsDetailsLinkds_item.defaultForEntity = item.defaultForEntityif ds_item.risky or item.risky:if ds_item.risky != item.risky:message += "Risky flag is changed to '{0}'.\n".format(set)ds_item.risky = item.riskyds_item.type = ItemType._VALUES_TO_NAMES[item.type]ds_item.hasItemNo = item.hasItemNods_item.clearance = item.clearanceif item.expectedDelay is not None:ds_item.expectedDelay = item.expectedDelayif item.preferredVendor:if item.preferredVendor != ds_item.preferredVendor:inventoryClient = InventoryClient().get_client()newPreferredVendorName = inventoryClient.getVendor(item.preferredVendor).nameoldPreferredVendorName = 'None'if ds_item.preferredVendor:oldPreferredVendorName = inventoryClient.getVendor(ds_item.preferredVendor).namemessage += "Preferred vendor is changed from '{0}' to '{1}'.\n".format(oldPreferredVendorName, newPreferredVendorName)ds_item.preferredVendor = item.preferredVendorif item.isWarehousePreferenceSticky != ds_item.isWarehousePreferenceSticky:flag = "ON" if item.isWarehousePreferenceSticky else "OFF"message += "Warehouse preference sticky is {0}.\n".format(flag)ds_item.isWarehousePreferenceSticky = item.isWarehousePreferenceStickyds_item.updatedOn = datetime.datetime.now()session.commit();subject = "Item '{0}' is updated in Catalog. Id is {1}".format(__get_product_name(ds_item),ds_item.id)if message:__send_mail(subject, message)return ds_item.iddef add_item(item):if not item:raise InventoryServiceException(108, "Bad item in request")if get_item(item.id):raise InventoryServiceException(101, "Item already exists")validate_item_prices(item)ds_item = Item()if item.productGroup:ds_item.product_group = item.productGroupif item.brand:ds_item.brand = item.brandif item.modelName:ds_item.model_name = item.modelNameif item.modelNumber:ds_item.model_number = item.modelNumberif item.color:ds_item.color = item.colorif item.category:ds_item.category = item.categoryif item.comments:ds_item.comments = item.commentsds_item.addedOn = datetime.datetime.now()ds_item.updatedOn = datetime.datetime.now()if item.startDate:ds_item.startDate = to_py_date(item.startDate)ds_item.startDate = ds_item.startDate.replace(hour=0,second=0,minute=0)if item.retireDate:ds_item.retireDate = to_py_date(item.retireDate)if item.comingSoonStartDate:ds_item.comingSoonStartDate = to_py_date(item.comingSoonStartDate)if item.expectedArrivalDate:ds_item.expectedArrivalDate = to_py_date(item.expectedArrivalDate)if item.mrp:ds_item.mrp = item.mrpif item.sellingPrice:ds_item.sellingPrice = item.sellingPriceif item.weight:ds_item.weight = item.weightif item.featureId:ds_item.feature_id = item.featureIdif item.featureDescription:ds_item.feature_description = item.featureDescription#check if categories present. If yes, add them to systemif item.bestDealValue:ds_item.bestDealValue = item.bestDealValueif item.bestDealText:ds_item.bestDealText = item.bestDealTextif item.bestDealsDetailsText:ds_item.bestDealsDetailsText = item.bestDealsDetailsTextif item.bestDealsDetailsLink:ds_item.bestDealsDetailsLink = item.bestDealsDetailsLinkif item.bestSellingRank:ds_item.bestSellingRank = item.bestSellingRankds_item.defaultForEntity = item.defaultForEntityds_item.risky = item.riskyds_item.type = ItemType._VALUES_TO_NAMES[item.type]ds_item.hasItemNo = item.hasItemNods_item.clearance = item.clearanceif item.expectedDelay is not None:ds_item.expectedDelay = item.expectedDelayelse:ds_item.expectedDelay = 0preferredVendorName = "None"if item.preferredVendor:ds_item.preferredVendor = item.preferredVendorinventoryClient = InventoryClient().get_client()preferredVendorName = inventoryClient.getVendor(item.preferredVendor).nameif item.preferredInsurer is not None:ds_item.preferredInsurer = item.preferredInsurerif item.catalogItemId:catalog_client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()master_items = catalog_client.getItemsByCatalogId(item.catalogItemId)itemStatus = status.IN_PROCESSfor masterItem in master_items:if masterItem.itemStatus in [status.CONTENT_COMPLETE, status.COMING_SOON, status.ACTIVE, status.PAUSED]:itemStatus = status.CONTENT_COMPLETEds_item.category = masterItem.categorybreakds_item.catalog_item_id = item.catalogItemIdds_item.status = itemStatusds_item.status_description = "This item is in process."else:# Check if a similar item already exists in our databasesimilar_item = Item.query.filter_by(brand=item.brand, model_number=item.modelNumber, model_name=item.modelName).first()print "[SIMILAR ITEM FOUND:] FOR {0} {1} {2}".format(item.brand, item.modelNumber, item.modelName)if similar_item is None or similar_item.catalog_item_id is None:# If there is no similar item in the database from before,# use the entity_id_generatorentity_id = EntityIDGenerator.query.first()ds_item.catalog_item_id = entity_id.id + 1ds_item.status = status.IN_PROCESSds_item.status_description = "This item is in process."entity_id.id = entity_id.id + 1if similar_item is not None and similar_item.catalog_item_id is None:similar_item.catalog_item_id = entity_id.idelse:#If a similar item already exists for a product group, brand and model_number, set it as same.ds_item.catalog_item_id = similar_item.catalog_item_idds_item.category = similar_item.categoryds_item.product_group = similar_item.product_groupds_item.status = similar_item.statusds_item.status_description = similar_item.status_descriptionsession.commit();subject = "New item is added. Id is {0}".format(str(ds_item.id))message = "Category : {6}, Brand : {0}, Model : {1}, Model Number : {2}\nColor : {3}, Selling Price : {4}, Mrp : {5}, \nPromotion Text : {7}, Preferred Vendor: {8}".format(item.brand, item.modelNumber, item.modelName, item.color, item.sellingPrice, item.mrp, item.category, item.bestDealText,item.bestDealsDetailsText,item.bestDealsDetailsLink, preferredVendorName)__send_mail(subject, message)return ds_item.iddef retire_item(item_id):if not item_id:raise InventoryServiceException(101, "bad item id")item = get_item(item_id)if not item:raise InventoryServiceException(108, "item id not present")item.status = status.PHASED_OUTitem.retireDate = datetime.datetime.now()session.commit()#need to implement threads based solution heredef start_item_on(item_id, timestamp):if not item_id:raise InventoryServiceException(101, "bad item id")item = get_item(item_id)if not item:raise InventoryServiceException(108, "item id not present")item.status = status.ACTIVEitem.startDate = datetime.datetime.fromtimestamp(to_py_date(timestamp))add_status_change_log(item, status.ACTIVE)session.commit()#need to implement threads heredef retire_item_on(item_id, timestamp):if not item_id:raise InventoryServiceException(101, "bad item id")item = get_item(item_id)if not item:raise InventoryServiceException(108, "item id not present")item.status = status.PHASED_OUTitem.retireDate = datetime.datetime.fromtimestamp(to_py_date(timestamp))add_status_change_log(item, status.PHASED_OUT)session.commit()def add_status_change_log(item, new_status):item_change_log = ItemChangeLog()item_change_log.new_status = new_statusitem_change_log.old_status = item.statusitem_change_log.timestamp = datetime.datetime.now()item_change_log.item = itemsession.commit()def change_item_status(item_id, new_status):if not item_id:raise InventoryServiceException(101, "bad item id")item = get_item(item_id)if not item:raise InventoryServiceException(108, "item id not present")add_status_change_log(item, new_status)item.status = new_statusif item.status == status.PHASED_OUT:item.status_description = "This item has been phased out"__send_mail("Item '{0}' is Phased-Out. Item id is {1}".format(__get_product_name(item), item_id), "")elif item.status == status.DELETED:item.status_description = "This item has been deleted"elif item.status == status.PAUSED:item.status_description = "This item is currently out of stock"elif item.status == status.PAUSED_BY_RISK:item.status_description = "This item is currently out of stock"#This will clear cache from tomcat__clear_homepage_cache()elif item.status == status.ACTIVE:item.status_description = "This item is active"elif item.status == status.IN_PROCESS:item.status_description = "This item is in process"elif item.status == status.CONTENT_COMPLETE:item.status_description = "This item is in process"session.commit()def check_risky_item(item_id):item = get_item(item_id)if not item.risky:returnclient = InventoryClient().get_client()itemInfo = client.getItemAvailabilityAtLocation(item.id, sourceId)warehouse_id = itemInfo[0]availability = client.getItemAvailibilityAtWarehouse(warehouse_id, item.id)if availability <= 0:if item.status == status.ACTIVE:change_item_status(item.id, status.PAUSED_BY_RISK)__send_mail_for_oos_item(item)else:if item.status == status.PAUSED_BY_RISK:change_item_status(item.id, status.ACTIVE)__send_mail_for_active_item(item.id, "Item '{0}' is Active. Item id is {1}".format(__get_product_name(item), item_id), "")session.commit()def mark_item_as_content_complete(entity_id, category, brand, modelName, modelNumber):'''Get all the items for this entityID and update category, brand, modelName and modelNumber for all.Update Status for only IN_PROCESS items to CONTENT_COMPLETE'''content_complete_status = status.CONTENT_COMPLETEitems = Item.query.filter_by(catalog_item_id=entity_id).all()current_timestamp = datetime.datetime.now()for item in items:if item.status == status.IN_PROCESS:item.status = content_complete_statusitem_change_log = ItemChangeLog()item_change_log.old_status = item.statusitem_change_log.new_status = content_complete_statusitem_change_log.timestamp = current_timestampitem_change_log.item = itemcategory_object = get_category(category)if category_object is not None:item.category = categoryitem.product_group = category_object.display_nameitem.brand = branditem.model_name = modelNameitem.model_number = modelNumberitem.updatedOn = current_timestampsession.commit()return Truedef get_child_categories(category):cm = CategoryManager()cat = cm.getCategory(category)return cat.children_category_ids if cat else Nonedef get_best_sellers(start_index, stop_index, category=-1):'''Returns the Best Sellers between the start and the stop index in the given category'''query = get_best_sellers_query(category, None)best_sellers = query.all()[start_index:stop_index]return get_thrift_item_list(best_sellers)def get_best_sellers_count(category=-1):'''Returns the number of best sellers in the given category'''count = get_best_sellers_query(category, None).count()if count is None:count = 0return countdef get_best_sellers_catalog_ids(start_index, stop_index, brand, category=-1):'''Returns the Best sellers for the given brand and category between the start and the stop index.Ignores the category if it's passed as -1 and the brand if it's passed as None.'''query = get_best_sellers_query(category, brand)best_sellers = query.all()[start_index:stop_index]return [item.catalog_item_id for item in best_sellers]def get_best_sellers_query(category, brand):'''Returns the query to be used for getting Best Sellers.Ignores the category if it's passed as -1 and the brand if it's passed as None.'''query = Item.query.filter_by(status=status.ACTIVE).filter(Item.bestSellingRank != None)if category != -1:all_categories = [category]child_categories = get_child_categories(category)if child_categories is not None:all_categories = all_categories + child_categoriesquery = query.filter(Item.category.in_(all_categories))if brand is not None:query = query.filter_by(brand=brand)query = query.order_by(asc(Item.bestSellingRank))return querydef get_best_deals(category=-1):'''Returns the Best deals in the given category. Ignores the category if it's passed as -1.'''query = get_best_deals_query(Item, category, None)items = query.all()return get_thrift_item_list(items)def get_best_deals_count(category=-1):'''Returns the count of best deals in the given category.Ignores the category if it's -1.'''count = get_best_deals_counting_query(func.count(distinct(Item.catalog_item_id)), category, None).scalar()if count is None:count = 0return countdef get_best_deals_catalog_ids(start_index, stop_index, brand, category=-1):'''Returns the catalog_item_ids of best deal items for the given brand and category.Ignores the category if it's passed as -1 and the brand if it's passed as None.'''query = get_best_deals_query(Item, category, brand)best_deal_items = query.all()[start_index:stop_index]return [item.catalog_item_id for item in best_deal_items]def get_best_deals_counting_query(obj, category, brand):'''Returns the query to be used to select the best deals in the given brand and category.Ignores the category if it's passed as -1 and the brand if it's passed as None.'''query = session.query(obj).filter_by(status=status.ACTIVE).filter(Item.bestDealValue != None)if category != -1:all_categories = [category]child_categories = get_child_categories(category)if child_categories is not None:all_categories = all_categories + child_categoriesquery = query.filter(Item.category.in_(all_categories))if brand is not None:query = query.filter_by(brand=brand)return querydef get_best_deals_query(obj, category, brand):'''Returns the query to be used to get the best deals in the given category and brand.Ignores the category if it's passed as -1 and the brand if it's passed as None.'''query = get_best_deals_counting_query(obj, category, brand)query = query.group_by(Item.catalog_item_id).order_by(desc(Item.bestDealValue))return querydef get_coming_soon(category=-1):'''Returns the Coming Soon items in the given category. Ignores the category if it's passed as -1.'''query = get_coming_soon_query(Item, category, None)items = query.all()return get_thrift_item_list(items)def get_coming_soon_count(category=-1):'''Returns the count of coming in the given category.Ignores the category if it's -1.'''count = get_coming_soon_counting_query(func.count(distinct(Item.catalog_item_id)), category, None).scalar()if count is None:count = 0return countdef get_coming_soon_catalog_ids(start_index, stop_index, brand, category=-1):'''Returns the catalog_item_ids of coming soon items for the given brand and category.Ignores the category if it's passed as -1 and the brand if it's passed as None.'''query = get_coming_soon_query(Item, category, brand)coming_soon_items = query.all()[start_index:stop_index]return [item.catalog_item_id for item in coming_soon_items]def get_coming_soon_counting_query(obj, category, brand):'''Returns the query to be used to select the coming soon product in the given brand and category.Ignores the category if it's passed as -1 and the brand if it's passed as None.'''query = session.query(obj).filter_by(status=status.COMING_SOON)if category != -1:all_categories = [category]child_categories = get_child_categories(category)if child_categories is not None:all_categories = all_categories + child_categoriesquery = query.filter(Item.category.in_(all_categories))if brand is not None:query = query.filter_by(brand=brand)return querydef get_coming_soon_query(obj, category, brand):'''Returns the query to be used to get the coming soon products in the given category and brand.Ignores the category if it's passed as -1 and the brand if it's passed as None.'''query = get_coming_soon_counting_query(obj, category, brand)query = query.group_by(Item.catalog_item_id).order_by(asc(Item.comingSoonStartDate))return querydef get_latest_arrivals(limit, category=-1):'''Returns up to limit number of Latest Arrivals in the given category.'''categories = []if category != -1:categories = [category]query = get_latest_arrivals_query(Item, categories, None)items = query.all()[0:limit]return get_thrift_item_list(items)def get_latest_arrivals_count(limit, category=-1):'''Returns the number of latest arrivals which will be displayed on the website.To ignore the categories, pass the list as empty. To ignore brand, pass it as null.'''categories = []if category != -1:categories = [category]count = get_latest_arrivals_counting_query(func.count(distinct(Item.catalog_item_id)), categories, None).scalar()if count is None:count = 0count = min(count, limit)return countdef get_latest_arrivals_catalog_ids(start_index, stop_index, brand, categories=[]):'''Returns the catalog_item_ids of the latest arrivals between the start and the stop indexTo ignore the categories, pass the list as empty. To ignore brand, pass it as null.'''query = get_latest_arrivals_query(Item, categories, brand)latest_arrivals = query.all()[start_index:stop_index]return [item.catalog_item_id for item in latest_arrivals]def get_latest_arrivals_counting_query(obj, categories, brand):'''Returns the query to be used to count Latest arrivals.To ignore the categories, pass the list as empty. To ignore brand, pass it as null.'''query = session.query(obj).filter_by(status=status.ACTIVE)all_categories = []for category in categories:all_categories.append(category)child_categories = get_child_categories(category)if child_categories:all_categories = all_categories + child_categoriesif all_categories:query = query.filter(Item.category.in_(all_categories))if brand is not None:query = query.filter_by(brand=brand)return querydef get_latest_arrivals_query(obj, categories, brand):'''Returns the query to be used to retrieve Latest Arrivals.Ignores the category if it's passed as -1 and the brand if it's passed as None.'''query = get_latest_arrivals_counting_query(obj, categories, brand)query = query.group_by(Item.catalog_item_id).order_by(desc(Item.startDate)).order_by(Item.catalog_item_id)return querydef get_thrift_item_list(items):return [to_t_item(item) for item in items if item != None]def generate_new_entity_id():generator = EntityIDGenerator.query.one()id = generator.id + 1generator.id = idsession.commit()return iddef put_category_object(object):category = Category.get_by(id=1)if category is None:category = Category()category.object = objectsession.commit()return Truedef get_category_object():object = Category.get_by(id=1).objectreturn objectdef add_category(t_category):category = Category.get_by(id=t_category.id)if category is None:category = Category()category.id = t_category.idcategory.label = t_category.labelcategory.description = t_category.descriptioncategory.display_name = t_category.display_namecategory.parent_category_id = t_category.parent_category_idsession.commit()return Truedef get_category(id):return Category.query.filter_by(id=id).first()def get_all_categories():return Category.query.all()def validate_item_prices(item):if item.mrp == None or item.sellingPrice == None or item.mrp == "" or item.sellingPrice == "":returnif item.mrp < item.sellingPrice:print "[BAD MRP and SP:] for {0} {1} {2} {3}. MRP={4}, SP={5}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(item.mrp), str(item.sellingPrice))raise InventoryServiceException(101, "[BAD MRP and SP:] for {0} {1} {2} {3}. MRP={4}, SP={5}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(item.mrp), str(item.sellingPrice)))returndef validate_vendor_prices(item, vendorPrices):if item.mrp != None and item.mrp != "" and vendorPrices.mop != "" and item.mrp < vendorPrices.mop:print "[BAD MRP and MOP:] for {0} {1} {2} {3}. MRP={4}. MOP={5}, Vendor={6}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(item.mrp), str(vendorPrices.mop), str(vendorPrices.vendorId))raise InventoryServiceException(101, "[BAD MRP and MOP:] for {0} {1} {2} {3}. MRP={4}. MOP={5}, Vendor={6}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(item.mrp), str(vendorPrices.mop), str(vendorPrices.vendorId)))if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:print "[BAD MOP and TP:] for {0} {1} {2} {3}. TP={4}. MOP={5}, Vendor={6}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(vendorPrices.transferPrice), str(vendorPrices.mop), str(vendorPrices.vendorId))raise InventoryServiceException(101, "[BAD MOP and TP:] for {0} {1} {2} {3}. TP={4}. MOP={5}, Vendor={6}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(vendorPrices.transferPrice), str(vendorPrices.mop), str(vendorPrices.vendorId)))returndef check_color_valid(color):if color is not None:color = color.strip().lower()if color != '' and color != 'na' and color != 'blank' and color != '(blank)':return Truereturn Falsedef check_similar_item(brand, model_number, model_name, color):query = Item.queryquery = query.filter_by(brand=brand)query = query.filter_by(model_number=model_number)query = query.filter_by(model_name=model_name)similar_items = query.all()item = None# Check if a similar item already exists in our databasefor old_item in similar_items:if old_item.color != None and old_item.color.strip().lower() == color.strip().lower():item = old_itembreak# Check if a similar item already exists in our database with out valid color if similar item with same color is not foundif item is None:for old_item in similar_items:if not check_color_valid(old_item.color):item = old_itembreaki = 0color_of_similar_item = None# Check if a similar item already exists in our database to be used to get catalog_item_idfor old_item in similar_items:# get a similar item already existing in our database with valid colorif check_color_valid(old_item.color):similar_item = old_itemcolor_of_similar_item = similar_item.colorbreaki = i + 1# get a similar item already existing in our database if similar item with valid color is not foundif i == len(similar_items):similar_item = old_itemcolor_of_similar_item = similar_item.color# Check if a similar item that is obtained above is having a valid colorif check_color_valid(color_of_similar_item):# if a similar item that is obtained above is having a valid color and new item is about to be created with out valid color it is not done.# since for example if their is a item with red color in our database and we are creating a new item with no color for the same product which is wrong.if item is None and not check_color_valid(color):return similar_item.idif item is None:return 0else:return item.iddef change_risky_flag(item_id, risky):item = get_item(item_id)if not item:raise InventoryServiceException(101, "Item missing in our database")try:log_risky_flag(item_id, risky)except:print "Not able to log risky flag change"item.risky = riskyif not risky and item.status == status.PAUSED_BY_RISK:change_item_status(item.id, status.ACTIVE)__send_mail_for_active_item(item.id, "Item '{0}' is Active. Item id is {1}".format(__get_product_name(item), item_id), "")session.commit()flag = "ON" if risky else "OFF"subject = "Risky flag is {0} for Item {1}.".format(flag, __get_product_name(item))__send_mail(subject,"")def get_items_for_mastersheet(categoryName, brand):if not categoryName or not brand:raise InventoryServiceException(101, "Invalid category or brand in request")categories = ["Handsets", "Tablets", "Laptops"]query = Item.query.filter(Item.status != status.PHASED_OUT)if categoryName == "ALL":passelif categoryName == "ALL Accessories":query = query.filter(~Item.product_group.in_(categories))elif categoryName == "ALL Handsets":query = query.filter(Item.product_group.in_(categories))elif categoryName == "Mobile Accessories":child_categories = get_child_categories(10011)if child_categories is not None:child_categories.append(0)query = query.filter(Item.category.in_(child_categories))elif categoryName == "Laptop Accessories":child_categories = get_child_categories(10070)if child_categories is not None:child_categories.append(0)query = query.filter(Item.category.in_(child_categories))else:query = query.filter(Item.product_group == categoryName)if brand == "ALL":passelse:query = query.filter(Item.brand == brand)items = query.all()return itemsdef get_risky_items():items = Item.query.filter_by(risky=True).all()return itemsdef get_similar_items_catalog_ids(start_index, stop_index, itemId):query = SimilarItems.query.filter_by(item_id=itemId).limit(stop_index-start_index)similar_items = query.all()return_list = []for similar_item in similar_items:isActive = Falsetry:all_items = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).all()except:continuefor item in all_items:isActive = isActive or item.status == status.ACTIVEif isActive:return_list.append(similar_item.catalog_item_id)return return_listdef get_all_similar_items_catalog_ids(itemId):query = SimilarItems.query.filter_by(item_id=itemId)similar_items = query.all()return_list = []for similar_item in similar_items:item_query = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).limit(1)item = item_query.one()return_list.append(item)return get_thrift_item_list(return_list)def add_similar_item_catalog_id(itemId, catalog_item_id):if not itemId or not catalog_item_id:raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")items_for_entity = get_items_by_catalog_id(catalog_item_id)if not len(items_for_entity):raise InventoryServiceException(101, "catalogItemId does not exists in database")s_items = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()if not len(s_items):s_item = SimilarItems()s_item.item_id=itemIds_item.catalog_item_id=catalog_item_idsession.commit()return items_for_entity[0]else:raise InventoryServiceException(101, "Already exists")def delete_similar_item_catalog_id(itemId, catalog_item_id):if not itemId or not catalog_item_id:raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")similar_item = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()if len(similar_item):similar_item[0].delete()session.commit()return Truedef get_all_vouchers_for_item(itemId):vouchers = VoucherItemMapping.query.filter_by(item_id=itemId).all()return vouchersdef get_voucher_amount(itemId, voucher_type):voucher = VoucherItemMapping.query.filter_by(item_id=itemId, voucherType=voucher_type).all()if len(voucher):return voucher[0].amountelse:return 0def add_update_voucher_for_item(catalog_item_id, voucher_type, voucher_amount):if not catalog_item_id or not voucher_type or not voucher_amount:raise InventoryServiceException(101, "Bad catalogItemId or voucherType or voucherAmount in request")items_for_entity = get_items_by_catalog_id(catalog_item_id)if not len(items_for_entity):raise InventoryServiceException(101, "catalogItemId does not exists in database")for item in items_for_entity:itemId = item.idvoucher = VoucherItemMapping.query.filter_by(item_id=itemId, voucherType=voucher_type).all()if not len(voucher):voucher = VoucherItemMapping()voucher.item_id=itemIdvoucher.voucherType=voucher_typevoucher.amount=voucher_amountelse:voucher[0].amount=voucher_amountsession.commit()return Truedef delete_voucher_for_item(catalog_item_id, voucher_type):if not catalog_item_id or not voucher_type:raise InventoryServiceException(101, "Bad catalogItemId or voucherType in request")items_for_entity = get_items_by_catalog_id(catalog_item_id)if not len(items_for_entity):raise InventoryServiceException(101, "catalogItemId does not exists in database")for item in items_for_entity:itemId = item.idvoucher = VoucherItemMapping.query.filter_by(item_id=itemId, voucherType=voucher_type).all()if len(voucher):voucher[0].delete()session.commit()return Truedef add_product_notification(itemId, email):try:try:product_notification = ProductNotification.query.filter_by(item_id=itemId, email=email).one()except:product_notification = ProductNotification()product_notification.email = emailproduct_notification.item_id = itemIdproduct_notification.addedOn = datetime.datetime.now()session.commit()return Trueexcept:return Falsedef send_product_notifications():product_notifications = ProductNotification.query.all()for product_notification in product_notifications:item = product_notification.itemclient = InventoryClient().get_client()availability = client.getItemAvailabilityAtLocation(item.id, sourceId)[4]if item.status == status.ACTIVE and (not item.risky or availability > 0):__enque_product_notification_email(product_notification.email, __get_product_name(item) , product_notification.addedOn, __get_product_url(item), item.id)product_notification.delete()session.commit()return Truedef __get_product_name(item):product_name = item.brand + " " + item.model_name + " " + item.model_numbercolor = item.colorif color is not None and color != 'NA':product_name = product_name + " (" + color + ")"product_name = product_name.replace(" "," ")return product_namedef __get_product_url(item):product_url = "http://" + source_url + "/mobile-phones/" + item.brand + "-" + item.model_name + "-" + item.model_number + "-" + str(item.catalog_item_id)product_url = product_url.replace("--","-")product_url = product_url.replace(" ","")return product_urldef get_all_brands_by_category(category_id):catm = CategoryManager()child_categories = catm.getCategory(category_id).children_category_idsbrands = session.query(distinct(Item.brand)).filter(Item.category.in_(child_categories)).all()return [brand[0] for brand in brands]def get_all_brands():brands = session.query(distinct(Item.brand)).order_by(Item.brand).all()return [brand[0] for brand in brands]def __enque_product_notification_email(email, product, date, url, itemId):html = """<html><body><div><p>Hi,<br /><br />The product requested by you on $date is now available on $source_url.</p><p><strong>Product: $product </strong></p><p>Click the link below to visit the product:<br/>$url</p><p>Regards,<br/>$source_name Customer Support Team<br/>$source_url<br/>Email: help@saholic.com<br/></p></div></body></html>"""html = Template(html).substitute(dict(product=product,date=date,url=url,source_url=source_url,source_name=source_name))try:helper_client = HelperClient().get_client()helper_client.saveUserEmailForSending([email], "", "Product requested by you is available now.", html, str(itemId), "ProductNotification", [], [])except Exception as e:print edef get_all_sources():sources = Source.query.all()return [to_t_source(source) for source in sources]def get_item_pricing_by_source(itemId, sourceId):item = Item.query.filter_by(id=itemId).first()if item is None:raise InventoryServiceException(101, "Bad Item")source = Source.query.filter_by(id=sourceId).first()if source is None:raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))item_pricing = SourceItemPricing.query.filter_by(source=source, item=item).first()if item_pricing is None:raise InventoryServiceException(101, "Pricing information not found for sourceId " + str(sourceId))return item_pricingdef add_source_item_pricing(sourceItemPricing):if not sourceItemPricing:raise InventoryServiceException(108, "Bad sourceItemPricing in request")if not sourceItemPricing.sellingPrice:raise InventoryServiceException(101, "Selling Price is not defined for sourceId " + str(sourceItemPricing.sourceId))sourceId = sourceItemPricing.sourceIditemId = sourceItemPricing.itemIditem = Item.query.filter_by(id=itemId).first()if item is None:raise InventoryServiceException(101, "Bad Item")source = Source.query.filter_by(id=sourceId).first()if source is None:raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))ds_sourceItemPricing = SourceItemPricing.get_by(source=source, item=item)if ds_sourceItemPricing is None:ds_sourceItemPricing = SourceItemPricing()ds_sourceItemPricing.source = sourceds_sourceItemPricing.item = itemif sourceItemPricing.mrp:ds_sourceItemPricing.mrp = sourceItemPricing.mrpds_sourceItemPricing.sellingPrice = sourceItemPricing.sellingPricesession.commit()returndef get_all_source_pricing(itemId):item = Item.query.filter_by(id=itemId).first()if item is None:raise InventoryServiceException(101, "Bad Item")source_pricing = SourceItemPricing.query.filter_by(item=item).all()return source_pricingdef get_item_for_source(item_id, sourceId):item = get_item(item_id)if sourceId == -1:return itemtry:sip = get_item_pricing_by_source(item_id, sourceId)item.sellingPrice = sip.sellingPriceif sip.mrp:item.mrp = sip.mrpexcept:print "No source pricing"return itemdef search_items(search_terms, offset, limit):query = Item.querysearch_terms = ['%' + search_term + '%' for search_term in search_terms]for search_term in search_terms:query_clause = []query_clause.append(Item.brand.like(search_term))query_clause.append(Item.model_number.like(search_term))query_clause.append(Item.model_name.like(search_term))query = query.filter(or_(*query_clause))query = query.order_by(Item.product_group, Item.brand, Item.model_number, Item.model_name).offset(offset)if limit:query = query.limit(limit)items = query.all()return itemsdef get_search_result_count(search_terms):query = Item.querysearch_terms = ['%' + search_term + '%' for search_term in search_terms]for search_term in search_terms:query_clause = []query_clause.append(Item.brand.like(search_term))query_clause.append(Item.model_number.like(search_term))query_clause.append(Item.model_name.like(search_term))query = query.filter(or_(*query_clause))return query.count()def __clear_homepage_cache():try:# create a password managerpassword_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()# Add the username and password.configclient = ConfigClient()ips = configclient.get_property("production_servers_private_ips");ips = ips.split(" ")for ip in ips:try:top_level_url = "http://" + ip + ":8080/"password_mgr.add_password(None, top_level_url, "saholic", "shop2020")handler = urllib2.HTTPBasicAuthHandler(password_mgr)opener = urllib2.build_opener(handler)# use the opener to fetch a URLres = opener.open(top_level_url + "cache-admin/HomePageSnippets?_method=delete")print "Successfully cleared home page cache" + res.read()except:print "Unable to clear home page cache" + res.read()except:print "Unable to clear cache, still should continue with other operations"def get_product_notifications(start_datetime):'''Returns a list of Product Notification objects each representing user requests for notification'''query = ProductNotification.queryif start_datetime:query = query.filter(ProductNotification.addedOn > start_datetime)notifications = query.order_by(desc('addedOn')).all()return notificationsdef get_product_notification_request_count(start_datetime):'''Returns list of items and the counts of product notification requests'''print start_datetimequery = session.query(ProductNotification, func.count(ProductNotification.email).label('count'))if start_datetime:query = query.filter(ProductNotification.addedOn > start_datetime)counts = query.group_by(ProductNotification.item_id).order_by(desc('count')).all()return countsdef close_session():if session.is_active:print "session is active. closing it."session.close()def is_alive():try:session.query(Item.id).limit(1).one()return Trueexcept:return Falsedef add_authorization_log_for_item(itemId, username, reason):if not itemId or not username:raise InventoryServiceException(101, "Bad itemId or Invalid username in request")authorize_log = AuthorizationLog()authorize_log.item_id = itemIdauthorize_log.username = usernameauthorize_log.reason = reasonsession.commit()return Truedef __send_mail_for_oos_item(item):oos = OOSTracker.get_by(itemId = item.id)if oos is None:oos = OOSTracker()oos.itemId = item.idsession.commit()try:EmailAttachmentSender.mail(mail_user, mail_password, to_addresses + ["abhishek.mathur@shop2020.in"], "Item is out of stock. ID: " + str(item.id) + " " + str(item.brand) + " " + str(item.model_name) + " " + str(item.model_number)+ " " + str(item.color), None)except Exception as e:print edef __send_mail_for_active_item(itemId, subject, message):oos = OOSTracker.get_by(itemId = itemId)if oos is not None:oos.delete()session.commit()__send_mail(subject, message)def __send_mail(subject, message):try:thread = threading.Thread(target=partial(mail, mail_user, mail_password, to_addresses, subject, message))thread.start()except Exception as ex:print exdef get_clearance_sale_catalog_ids():all_status = [status.ACTIVE, status.PAUSED, status.PAUSED_BY_RISK]query = Item.query.filter_by(clearance=True)query = query.filter(Item.status.in_(all_status))query = query.group_by(Item.catalog_item_id).order_by(desc(Item.startDate)).order_by(Item.catalog_item_id)clearance_sales = query.all()return [item.catalog_item_id for item in clearance_sales]def get_vat_amount_for_item(itemId, price):item = Item.query.filter_by(id=itemId).first()vatPercentage = item.vatPercentageif vatPercentage is None:vatMaster = CategoryVatMaster.query.filter(and_(CategoryVatMaster.categoryId==item.category, CategoryVatMaster.minVal<=price, CategoryVatMaster.maxVal>=price)).first()vatPercentage = vatMaster.vatPercentif vatPercentage is None:vatPercentage = 0return (price*vatPercentage)/100def get_vat_percentage_for_item(itemId, price):item = Item.query.filter_by(id=itemId).first()vatPercentage = item.vatPercentageif vatPercentage is None:vatMaster = CategoryVatMaster.query.filter(and_(CategoryVatMaster.categoryId==item.category, CategoryVatMaster.minVal<=price, CategoryVatMaster.maxVal>=price)).first()vatPercentage = vatMaster.vatPercentif vatPercentage is None:vatPercentage = 0return vatPercentagedef get_all_ignored_inventoryupdate_items_list(offset,limit):client = InventoryClient().get_client()itemids = client.getIgnoredInventoryUpdateItemids(offset,limit)result = []if itemids is not None and len(itemids)>0:query = Item.query.filter(Item.id.in_(itemids))query = query.order_by(Item.product_group, Item.brand, Item.model_number, Item.model_name)result = [to_t_item(item) for item in query.all()]return resultdef add_tag (displayName, catalogId):ent_tag = Noneif catalogId is None or (not is_valid_catalog_id(catalogId)):raise InventoryServiceException(id, "Invalid CatalogId")else:ent_tag = EntityTag()ent_tag.entityId = catalogIdif displayName is None:raise InventoryServiceException(id, "Tag should not be empty")else:ent_tag.tag = displayNamesession.commit()return Truedef add_banner(bannerName, imageName,link, priority, isActive, hasMap):banner_details=Banner()banner_details.bannerName=bannerNamebanner_details.imageName=imageNamebanner_details.link=linkbanner_details.priority=prioritybanner_details.isActive=isActivebanner_details.hasMap=hasMapsession.commit()return Truedef get_all_banners():return [tuple[0] for tuple in session.query(Banner.bannerName).all()]def delete_banner(name):session.query(Banner.bannerName).filter_by(bannerName=name).delete()session.commit()return Truedef get_banner_details(name):banner = Banner.get_by(bannerName=name)return bannerdef get_active_banners():print "Data accessor"query= session.query(Banner)banner= query.filter_by(isActive =True).order_by(desc(Banner.priority)).all()print "Banner is",print bannerreturn bannerdef add_banner_map(bannerName, mapLink, coordinates):banner_map_details=BannerMap()banner_map_details.bannerName=bannerNamebanner_map_details.mapLink=mapLinkbanner_map_details.coordinates=coordinatessession.commit()return Truedef delete_banner_map(name):session.query(BannerMap.bannerName).filter_by(bannerName=name).delete()session.commit()return Truedef get_banner_map_details(name):print "Data accessor"query= session.query(BannerMap)bannermap= query.filter_by(bannerName=name).all()print "Banner is",print bannermapreturn bannermapdef get_all_tags ():return [tuple[0] for tuple in session.query(EntityTag.tag).distinct().all()]def get_all_entities_by_tag_name(displayName):return [tuple[0] for tuple in session.query(EntityTag.entityId).filter_by(tag=displayName).all()]def delete_tag(displayName):session.query(EntityTag.entityId).filter_by(tag=displayName).delete()session.commit()return Truedef delete_entity_tag(displayName, catalogId):session.query(EntityTag.tag).filter_by(tag=displayName,entityId=catalogId).delete()session.commit()return Truedef get_insurance_amount(itemId, price, insurerId, quantity):itemInsurerMapping = ItemInsurerMapping.query.filter(ItemInsurerMapping.itemId == itemId).filter(ItemInsurerMapping.insurerId == insurerId).first()if not itemInsurerMapping:#Default insurance premium is 1.5%return round(price * (1.5/100) * quantity)insuranceAmount = 0.0if itemInsurerMapping.premiumType == PremiumType._NAMES_TO_VALUES.get("PERCENT"):insuranceAmount = price * (itemInsurerMapping.premiumAmount/100) * quantityelse :insuranceAmount = itemInsurerMapping.premiumAmount * quantityreturn insuranceAmountdef get_insurer(insurerId):return Insurer.get_by(id = insurerId)def get_all_entity_tags():entitiesTag = EntityTag.query.all()entityMap = {}for e in entitiesTag:if not entityMap.has_key(e.entityId):entityMap[e.entityId] = []entityMap[e.entityId].append(e.tag)return entityMapdef get_all_insurers():print session.query(Insurer).all()return session.query(Insurer).all()