def add_product_dict(args, cc_browser, products, worksheet): """Create the Product Dictionary worksheet.""" # Prepare worksheet. worksheet.title = "Product Dictionary" # Add products. # Remove excluded SKUs. if args.exclude_skus: products = [ x for x in products if str(x["SKU"]) not in args.exclude_skus ] # Add product rows, grouped by category. row = 1 for _, product_group in itertools.groupby( products, key=cc_browser.product_key_by_category): # Add product rows. for product in product_group: if product["Discontinued Item"] == "Y": continue description = "{}: {}".format( product["Product Name"], cctools.html_to_plain_text(product["Teaser"])) worksheet.cell(row=row, column=1).value = product["SKU"] worksheet.cell(row=row, column=2).value = description worksheet.cell(row=row, column=3).value = product["HTSUS No"] row += 1 # Set column widths. worksheet.column_dimensions["A"].width = 6 worksheet.column_dimensions["B"].width = 95 worksheet.column_dimensions["C"].width = 13
def add_product(args, worksheet, row, item_no, product, variants): """Add row for each variant.""" size = product["Size"] product_name = product["Product Name"] sku = product["SKU"] teaser = cctools.html_to_plain_text(product["Teaser"]) price = float(product["Price"]) product_variants = get_product_variants(variants, sku) if len(product_variants) == 0: description = "{}: {}".format(product_name, teaser) add_variant( worksheet, row, item_no, size, sku, description, calc_wholesale_price(args, price), price ) row += 1 item_no += 1 else: any_variant_exists = False for variant in product_variants: variant_sku = variant["Variant SKU"] if variant_sku == "ANY" or variant_sku == "VAR": any_variant_exists = True variant_sku = "{}-{}".format(sku, variant_sku) variant_add_price = float(variant["Variant Add Price"]) variant_name = variant["Variant Name"] description = "{} ({}): {}".format( product_name, variant_name, teaser ) add_variant( worksheet, row, item_no, size, variant_sku, description, calc_wholesale_price(args, price + variant_add_price), price + variant_add_price ) row += 1 item_no += 1 if CHECK_FOR_LACK_OF_ANY and not any_variant_exists: logging.getLogger().warning( "No 'Any' or 'Variety' variant exists for {} {}".format( sku, product_name ) ) return row, item_no
def write_quantities(quant_filename, products): """Write empty quantities file.""" with open(quant_filename, "w") as quant_file: quant_file.write("Quantity,SKU,Description(ignored)\n") for product in products: sku = product["SKU"] description = "{}: {}".format( product["Product Name"], cctools.html_to_plain_text(product["Teaser"]) ) quant_file.write(",".join(["0", sku, '"%s"' % description]) + "\n")
def add_product(args, worksheet, row, item_no, product, variants): """Add row for each variant.""" size = product["Size"] product_name = product["Product Name"] sku = product["SKU"] teaser = cctools.html_to_plain_text(product["Teaser"]) msrp = float(product["Price"]) price = msrp * args.price_multiplier price =\ math.floor(price / args.price_precision + 0.5) * args.price_precision if args.include_variants: product_variants = get_product_variants(variants, sku) else: product_variants = [] if product_variants: any_variant_exists = False for variant in product_variants: variant_sku = variant["Variant SKU"] if variant_sku == "ANY" or variant_sku == "VAR": any_variant_exists = True variant_sku = "{}-{}".format(sku, variant_sku) variant_add_price = float(variant["Variant Add Price"]) variant_name = variant["Variant Name"] description = "{} ({}): {}".format(product_name, variant_name, teaser) add_variant( worksheet, row, item_no, size, variant_sku, description, calc_price.calc_wholesale_price(price + variant_add_price, args.wholesale_fraction), msrp + variant_add_price) row += 1 item_no += 1 if CHECK_FOR_LACK_OF_ANY and not any_variant_exists: logging.getLogger().warning( "No 'Any' or 'Variety' variant exists for {} {}".format( sku, product_name)) else: description = "{}: {}".format(product_name, teaser) add_variant( worksheet, row, item_no, size, sku, description, calc_price.calc_wholesale_price(price, args.wholesale_fraction), msrp) row += 1 item_no += 1 return row, item_no
def add_product(worksheet, row, lineno, product, variants): """Add row for each variant.""" product_name = product["Product Name"] sku = product["SKU"] teaser = cctools.html_to_plain_text(product["Teaser"]) size = product["Size"] cost = float(product["Cost"]) if "HTSUS No" in product: htsus_no = product["HTSUS No"] else: htsus_no = None product_variants = get_product_variants(variants, sku) if len(product_variants) == 0: description = "{}: {}".format(product_name, teaser) add_variant(worksheet, row, lineno, sku, description, size, cost, htsus_no) row += 1 lineno += 1 else: any_variant_exists = False for variant in product_variants: variant_sku = variant["Variant SKU"] if variant_sku == "ANY" or variant_sku == "VAR": any_variant_exists = True variant_sku = "{}-{}".format(sku, variant_sku) variant_add_cost = float(variant["Variant Add Cost"]) variant_name = variant["Variant Name"] description = "{} ({}): {}".format(product_name, variant_name, teaser) variant_group = variant["Variant Group"] if RE_SIZE_VARIANT.match(variant_group): size = variant_name add_variant(worksheet, row, lineno, variant_sku, description, size, cost + variant_add_cost, htsus_no) row += 1 lineno += 1 if CHECK_FOR_LACK_OF_ANY and not any_variant_exists: logging.getLogger().warning( "No 'Any' or 'Variety' variant exists for {} {}".format( sku, product_name)) return row, lineno
def add_product(worksheet, row, lineno, product, variants): """Add row for each variant.""" product_name = product["Product Name"] sku = product["SKU"] teaser = cctools.html_to_plain_text(product["Teaser"]) size = product["Size"] cost = float(product["Cost"]) if "HTSUS No" in product: htsus_no = product["HTSUS No"] else: htsus_no = None product_variants = get_product_variants(variants, sku) if len(product_variants) == 0: description = "{}: {}".format(product_name, teaser) add_variant(worksheet, row, lineno, sku, description, size, cost, htsus_no) row += 1 lineno += 1 else: any_variant_exists = False for variant in product_variants: variant_sku = variant["Variant SKU"] if variant_sku == "ANY" or variant_sku == "VAR": any_variant_exists = True variant_sku = "{}-{}".format(sku, variant_sku) variant_add_cost = float(variant["Variant Add Cost"]) variant_name = variant["Variant Name"] description = "{} ({}): {}".format(product_name, variant_name, teaser) variant_group = variant["Variant Group"] if RE_SIZE_VARIANT.match(variant_group): size = variant_name add_variant(worksheet, row, lineno, variant_sku, description, size, cost + variant_add_cost, htsus_no) row += 1 lineno += 1 if CHECK_FOR_LACK_OF_ANY and not any_variant_exists: logging.getLogger().warning("No 'Any' or 'Variety' variant exists for {} {}".format(sku, product_name)) return row, lineno
def add_product_dict(args, cc_browser, products, worksheet): """Create the Product Dictionary worksheet.""" # Prepare worksheet. worksheet.title = "Product Dictionary" # Add products. # Remove excluded SKUs. if args.exclude_skus: products = [ x for x in products if str(x["SKU"]) not in args.exclude_skus ] # Add product rows, grouped by category. row = 1 for _, product_group in itertools.groupby( products, key=cc_browser.product_key_by_category ): # Add product rows. for product in product_group: if product["Discontinued Item"] == "Y": continue description = "{}: {}".format( product["Product Name"], cctools.html_to_plain_text(product["Teaser"]) ) worksheet.cell(row=row, column=1).value = product["SKU"] worksheet.cell(row=row, column=2).value = description worksheet.cell(row=row, column=3).value = product["HTSUS No"] row += 1 # Set column widths. worksheet.column_dimensions["A"].width = 6 worksheet.column_dimensions["B"].width = 95 worksheet.column_dimensions["C"].width = 13
def generate_pdf(products, quantities, pdf_filename): """Generate the PDF file.""" # Construct a document. doc = reportlab.platypus.BaseDocTemplate( pdf_filename, pagesize=reportlab.lib.pagesizes.letter, title="Art Mart Inventory Sheet Check In/Out", # showBoundary=True ) # Construct a frame. page_width = doc.pagesize[0] page_height = doc.pagesize[1] frames = [ reportlab.platypus.Frame( id="table_frame", x1=0.25 * INCH, y1=1.60 * INCH, width=page_width - 0.45 * INCH, height=page_height - 3.60 * INCH, leftPadding=0, bottomPadding=0, rightPadding=0, topPadding=0 ) ] # Construct a template and add it to the document. doc.addPageTemplates( reportlab.platypus.PageTemplate( id="mytemplate", frames=frames, onPage=on_page ) ) # Construct a story and add it to the document. col_widths = [ 0.40 * INCH, # Existing Qty 1.15 * INCH, # Barcode 0.40 * INCH, # Qty Added 0.60 * INCH, # Price 5.00 * INCH, # Description 0.40 * INCH, # Total Qty ] col_header_style = reportlab.lib.styles.ParagraphStyle( name="col_header", fontName="Helvetica-Bold", fontSize=10 ) col_center_style = reportlab.lib.styles.ParagraphStyle( name="col_center", fontName="Helvetica-Bold", fontSize=10, alignment=reportlab.lib.enums.TA_CENTER ) col_align_right_style = reportlab.lib.styles.ParagraphStyle( name="col_align_right", fontName="Helvetica-Bold", fontSize=10, alignment=reportlab.lib.enums.TA_RIGHT ) # TableStyle cell formatting commands. styles = [ # Whole table. ('FONTSIZE', (0, 0), (-1, -1), 10), ('TOPPADDING', (0, 1), (-1, -1), 0.26 * INCH), ('BOTTOMPADDING', (0, 1), (-1, -1), 0.10 * INCH), # Existing Qty ('LEFTPADDING', (0, 0), (0, -1), 0), # Barcode ('ALIGN', (1, 0), (1, 0), 'CENTER'), ('ALIGN', (1, 0), (1, -1), 'RIGHT'), ('FONT', (1, 0), (1, -1), 'Courier-Bold'), # Qty Added ('LEFTPADDING', (2, 0), (2, 0), 0), ('ALIGN', (2, 0), (2, 0), 'LEFT'), ('ALIGN', (2, 1), (2, -1), 'RIGHT'), ('FONT', (2, 0), (2, -1), 'Courier-Bold'), # Price ('ALIGN', (3, 0), (3, -1), 'RIGHT'), ('FONT', (3, 0), (3, -1), 'Courier-Bold'), # Description ('ALIGN', (4, 0), (4, -1), 'LEFT'), ('FONT', (4, 0), (4, -1), 'Helvetica'), # Total Qty ('LEFTPADDING', (5, 0), (5, -1), 0), ('ALIGN', (5, 0), (5, 0), 'LEFT'), # Other ('FONT', (0, 0), (-1, 0), 'Helvetica-Bold'), # ('GRID', (0, 0), (-1, -1), 1.0, BLACK) ] header_row = ( reportlab.platypus.Paragraph("Existing<br/>Qty", col_header_style), reportlab.platypus.Paragraph("Barcode<br/> ", col_center_style), reportlab.platypus.Paragraph("Qty<br/>Added", col_header_style), reportlab.platypus.Paragraph( "Price<br/> ", col_align_right_style ), reportlab.platypus.Paragraph( "Description<br/> ", col_header_style ), reportlab.platypus.Paragraph("Total<br/>Qty", col_header_style) ) table_data = [header_row] for product in products: sku = product["SKU"] if sku in quantities: quantity = quantities[sku] # Round price to nearest dollar. price = product["Price"] price = "${.0f}".format(math.trunc(float(price) + 0.5)) description = "{}: {}".format( product["Product Name"], cctools.html_to_plain_text(product["Teaser"]) ) description = description[:68] table_data.append( ("_____", sku, quantity, price, description, "_____") ) table = reportlab.platypus.Table( data=table_data, colWidths=col_widths, style=styles, repeatRows=1 ) table.hAlign = "LEFT" story = [table] doc.build(story, canvasmaker=NumberedCanvas)
def generate_pdf( args, config, cc_browser, products ): """Generate a PDF given a list of products by category.""" # Construct a document. doc_title = config.get("price_list", "title") doc = reportlab.platypus.BaseDocTemplate( args.pdf_file, pagesize=reportlab.lib.pagesizes.letter, title=doc_title, # showBoundary=True # debug ) # Construct a frame for each column. frames = create_col_frames(doc, args.ncols) # Construct a template and add it to the document. doc.my_title = doc_title doc.discount_percent = args.discount_percent doc.addPageTemplates( reportlab.platypus.PageTemplate( id="mytemplate", frames=frames, onPage=on_page ) ) # Construct a story and add it to the document. category_style = reportlab.lib.styles.ParagraphStyle( name="category", spaceBefore=0.15 * INCH, spaceAfter=0.05 * INCH, fontName="Helvetica-Bold", fontSize=12 ) greybar_color = reportlab.lib.colors.Whiter( reportlab.lib.colors.lightgrey, 0.5 ) table_indent = 0.1 * INCH table_width = frames[0].width - table_indent price_width = 0.4 * INCH col_widths = [table_width - price_width, price_width] story = [] # Sort products by category, product_name. if args.categories: cc_browser.set_category_sort_order(args.categories) products = sorted(products, key=cc_browser.product_key_by_cat_and_name) # Setup styles. body_fontsize = float(config.get("price_list", "body_fontsize")) row_padding = float(config.get("price_list", "row_padding")) base_styles = [ ("FONTSIZE", (0, 0), (-1, -1), body_fontsize), ("ALIGN", (0, 0), (0, -1), "LEFT"), ("ALIGN", (1, 0), (1, -1), "RIGHT"), ("FONT", (1, 0), (1, -1), "Courier-Bold"), ("LEFTPADDING", (0, 0), (-1, -1), 3), ("RIGHTPADDING", (0, 0), (-1, -1), 3), ("TOPPADDING", (0, 0), (-1, -1), row_padding), ("BOTTOMPADDING", (0, 0), (-1, -1), 3 + row_padding), # ("GRID", (0, 0), (-1, -1), 1.0, reportlab.lib.colors.black) ] # Group products by category. first = True for _, product_group in itertools.groupby( products, key=cc_browser.product_key_by_category ): # Make a new styles instance just for this table. styles = list(base_styles) if args.add_teaser: rows_per_product = 2 else: rows_per_product = 1 # Assemble table data for the product_group. table_data = list() row = 0 for product in product_group: category = product["Category"] product_name = product["Product Name"] price = calc_price.calc_event_price( product["Price"], args.discount_percent, args.avg_tax_percent ) # Format price as whole number string like "$1,234". price = "${:,.0f}".format(price) if args.add_sku: row_data = ( "{} ({})".format(product_name, product["SKU"]), price ) else: row_data = (product_name, price) table_data.append(row_data) row += 1 if args.add_teaser: # Strip HTML formatting. product_teaser = cctools.html_to_plain_text(product["Teaser"]) row_data = (product_teaser,) table_data.append(row_data) # Indent the teaser. styles.append(("LEFTPADDING", (0, row), (0, row), 18)) row += 1 if len(table_data) == 0: continue # Prevent page splitting in the middle of a product. if rows_per_product > 1: for row in range(0, len(table_data), rows_per_product): styles.append( ("NOSPLIT", (0, row), (0, row + rows_per_product - 1)) ) # Grey background color to highlight alternate products. if args.greybar_interval > 1: interval = args.greybar_interval * rows_per_product for start_row in range(0, len(table_data), interval): for sub_row in range(0, rows_per_product): row = start_row + sub_row styles.append( ("BACKGROUND", (0, row), (1, row), greybar_color) ) # Create the table. table = reportlab.platypus.Table( data=table_data, colWidths=col_widths, style=styles ) # Create a group of the category name and the products. category_group = [ reportlab.platypus.Paragraph(category, category_style), reportlab.platypus.Indenter(left=table_indent), table, reportlab.platypus.Indenter(left=-table_indent) ] # There is a bug in platypus where if a KeepTogether is at # the top of a page and the KeepTogether is longer than a # page, then a blank page will be emitted. We can't always # know when this will happen, but we know for certain it will # happen for the first category group. if first: for flowable in category_group: story.append(flowable) first = False else: story.append(reportlab.platypus.KeepTogether(category_group)) doc.build(story)
def run_checks_core(self): """Run all checks, returning a list of findings.""" self.clear_finding_list() findings = [] # Create a connection to CoreCommerce. cc_browser = cctools.CCBrowser( self.config.get("website", "host"), self.config.get("website", "site"), self.config.get("website", "username"), self.config.get("website", "password"), clean=self.args.clean, cache_ttl=0 if self.args.refresh_cache else self.args.cache_ttl ) # Any subsequent calls should ignore the cache. If the user # clicks the Refresh button, it would be because they changed # something in CoreCommerce. self.args.refresh_cache = True # Check category list. categories = cc_browser.get_categories() self.eval_locals["items"] = categories for category in categories: findings.extend( self.check_item( "category", category, category["Category Name"] ) ) # Check products list. cc_browser.guess_product_ids() products = sorted( cc_browser.get_products(), key=cc_browser.product_key_by_cat_and_name ) self.eval_locals["items"] = products findings.extend(check_skus(self.config, products)) for product in products: for key in ["Teaser"]: product[key] = cctools.html_to_plain_text(product[key]) findings.extend( self.check_item( "product", product, product_display_name(product) ) ) # Check variants list. variants = sorted( cc_browser.get_variants(), key=cc_browser.variant_key_by_cat_product ) add_is_first_answer_flag(variants) self.eval_locals["items"] = variants for variant in variants: findings.extend( self.check_item( "variant", variant, variant_display_name(variant) ) ) self.checks_completed() return findings
def run_checks_core(self): """Run all checks, returning a list of findings.""" self.clear_finding_list() findings = [] # Create a connection to CoreCommerce. cc_browser = cctools.CCBrowser( self.config.get("website", "base_url"), self.config.get("website", "username"), self.config.get("website", "password"), clean=self.args.clean, cache_ttl=0 if self.args.refresh_cache else self.args.cache_ttl ) # Any subsequent calls should ignore the cache. If the user # clicks the Refresh button, it would be because they changed # something in CoreCommerce. self.args.refresh_cache = True # Check category list. categories = cc_browser.get_categories() self.eval_locals["items"] = categories for category in categories: findings.extend( self.check_item( "category", category, category["Category Name"] ) ) # Check products list. cc_browser.guess_product_ids() products = sorted( cc_browser.get_products(), key=cc_browser.product_key_by_cat_and_name ) self.eval_locals["items"] = products findings.extend(check_skus(self.config, products)) for product in products: for key in ["Teaser"]: product[key] = cctools.html_to_plain_text(product[key]) findings.extend( self.check_item( "product", product, product_display_name(product) ) ) # Check variants list. variants = sorted( cc_browser.get_variants(), key=cc_browser.variant_key_by_cat_product ) add_is_first_answer_flag(variants) self.eval_locals["items"] = variants for variant in variants: findings.extend( self.check_item( "variant", variant, variant_display_name(variant) ) ) self.checks_completed() return findings
def generate_pdf( args, config, cc_browser, products ): """Generate a PDF given a list of products by category.""" # Construct a document. doc_title = config.get("price_list", "title") doc = reportlab.platypus.BaseDocTemplate( args.pdf_file, pagesize=reportlab.lib.pagesizes.letter, title=doc_title, # showBoundary=True # debug ) # Construct a frame for each column. frames = create_col_frames(doc, args.ncols) # Construct a template and add it to the document. doc.my_title = doc_title doc.discount_percent = args.discount_percent doc.addPageTemplates( reportlab.platypus.PageTemplate( id="mytemplate", frames=frames, onPage=on_page ) ) # Construct a story and add it to the document. category_style = reportlab.lib.styles.ParagraphStyle( name="category", spaceBefore=0.15 * INCH, spaceAfter=0.05 * INCH, fontName="Helvetica-Bold", fontSize=12 ) greybar_color = reportlab.lib.colors.Whiter( reportlab.lib.colors.lightgrey, 0.5 ) table_indent = 0.1 * INCH table_width = frames[0].width - table_indent price_width = 0.4 * INCH col_widths = [table_width - price_width, price_width] story = [] # Sort products by category, product_name. if args.categories: cc_browser.set_category_sort_order(args.categories) products = sorted(products, key=cc_browser.product_key_by_cat_and_name) # Setup styles. body_fontsize = float(config.get("price_list", "body_fontsize")) row_padding = float(config.get("price_list", "row_padding")) base_styles = [ ("FONTSIZE", (0, 0), (-1, -1), body_fontsize), ("ALIGN", (0, 0), (0, -1), "LEFT"), ("ALIGN", (1, 0), (1, -1), "RIGHT"), ("FONT", (1, 0), (1, -1), "Courier-Bold"), ("LEFTPADDING", (0, 0), (-1, -1), 3), ("RIGHTPADDING", (0, 0), (-1, -1), 3), ("TOPPADDING", (0, 0), (-1, -1), row_padding), ("BOTTOMPADDING", (0, 0), (-1, -1), 3 + row_padding), # ("GRID", (0, 0), (-1, -1), 1.0, reportlab.lib.colors.black) ] # Group products by category. first = True for _, product_group in itertools.groupby( products, key=cc_browser.product_key_by_category ): # Make a new styles instance just for this table. styles = list(base_styles) if args.add_teaser: rows_per_product = 2 else: rows_per_product = 1 # Assemble table data for the product_group. table_data = list() row = 0 for product in product_group: category = product["Category"] product_name = product["Product Name"] price = calc_price.calc_event_price( product["Price"], args.discount_percent, args.avg_tax_percent ) # Format price as whole number string like "$1,234". price = "${:,.0f}".format(price) if args.add_sku: row_data = ( "{} ({})".format(product_name, product["SKU"]), price ) else: row_data = (product_name, price) table_data.append(row_data) row += 1 if args.add_teaser: # Strip HTML formatting. product_teaser = cctools.html_to_plain_text(product["Teaser"]) row_data = (product_teaser,) table_data.append(row_data) # Indent the teaser. styles.append(("LEFTPADDING", (0, row), (0, row), 18)) row += 1 if len(table_data) == 0: continue # Prevent page splitting in the middle of a product. if rows_per_product > 1: for row in range(0, len(table_data), rows_per_product): styles.append( ("NOSPLIT", (0, row), (0, row + rows_per_product - 1)) ) # Grey background color to highlight alternate products. if args.greybar_interval > 1: interval = args.greybar_interval * rows_per_product for start_row in range(0, len(table_data), interval): for sub_row in range(0, rows_per_product): row = start_row + sub_row styles.append( ("BACKGROUND", (0, row), (-1, row), greybar_color) ) # Create the table. table = reportlab.platypus.Table( data=table_data, colWidths=col_widths, style=styles ) # Create a group of the category name and the products. category_group = [ reportlab.platypus.Paragraph(category, category_style), reportlab.platypus.Indenter(left=table_indent), table, reportlab.platypus.Indenter(left=-table_indent) ] # There is a bug in platypus where if a KeepTogether is at # the top of a page and the KeepTogether is longer than a # page, then a blank page will be emitted. We can't always # know when this will happen, but we know for certain it will # happen for the first category group. if first: for flowable in category_group: story.append(flowable) first = False else: story.append(reportlab.platypus.KeepTogether(category_group)) doc.build(story)
def add_product(args, worksheet, row, item_no, product, variants): """Add row for each variant.""" size = product["Size"] product_name = product["Product Name"] sku = product["SKU"] teaser = cctools.html_to_plain_text(product["Teaser"]) msrp = float(product["Price"]) price = msrp * args.price_multiplier price =\ math.floor(price / args.price_precision + 0.5) * args.price_precision if args.include_variants: product_variants = get_product_variants(variants, sku) else: product_variants = [] if product_variants: any_variant_exists = False for variant in product_variants: variant_sku = variant["Variant SKU"] if variant_sku == "ANY" or variant_sku == "VAR": any_variant_exists = True variant_sku = "{}-{}".format(sku, variant_sku) variant_add_price = float(variant["Variant Add Price"]) variant_name = variant["Variant Name"] description = "{} ({}): {}".format( product_name, variant_name, teaser ) add_variant( worksheet, row, item_no, size, variant_sku, description, calc_price.calc_wholesale_price( price + variant_add_price, args.wholesale_fraction ), msrp + variant_add_price ) row += 1 item_no += 1 if CHECK_FOR_LACK_OF_ANY and not any_variant_exists: logging.getLogger().warning( "No 'Any' or 'Variety' variant exists for {} {}".format( sku, product_name ) ) else: description = "{}: {}".format(product_name, teaser) add_variant( worksheet, row, item_no, size, sku, description, calc_price.calc_wholesale_price(price, args.wholesale_fraction), msrp ) row += 1 item_no += 1 return row, item_no