Guest User

IndustryBuying.com script to plot/graph price vs parameter

a guest
Jun 30th, 2018
224
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.37 KB | None | 0 0
  1. import re, sys
  2. import itertools
  3. import matplotlib.pyplot as plt
  4.  
  5.  
  6. #fname = 'angle_grinder_pages'
  7.  
  8. class Debug(object):
  9.     def printq(self, txt = ''):
  10.         print '+'
  11.         if type(txt) is not str:
  12.             for item in txt:
  13.                 print item
  14.         else: print txt
  15.         print '!'
  16.        
  17.     def debug(self, txt):
  18.         print txt
  19.  
  20. class FatalError(Exception):
  21.     def __init__(self, msg):
  22.         print msg
  23.        
  24. class Plot(object):
  25.     font = { 'family' : 'monospace',
  26.              'weight' : 'normal',
  27.              'size'   :  10 }
  28.  
  29.     def plot(self):
  30.         plt.rc('font', **self.font)
  31.    
  32.         #sort items inplace on price
  33.         Item.items.sort(key=lambda x: float(x.price))
  34.    
  35.         #Display all items using __str__
  36.         for count, item in enumerate(Item.items):
  37.             try:
  38.                 x = getattr(item, Plot.x)
  39.                 y = getattr(item, Plot.y)
  40.             except Exception as e:
  41.                 title = getattr(item, 'title', '')
  42.                 print title, e
  43.                 continue
  44.             if Plot.k_on:
  45.                 if 'k' in item.color: continue
  46.             if Plot.x_limit != None:
  47.                 if x > Plot.x_limit: continue
  48.                 if y > Plot.y_limit: continue
  49.             #if x > 30000: print item.item
  50.                
  51.             print x, y, item.price, item.title, count
  52.             plt.scatter(float(x), float(y), color = item.color, s=50)
  53.             plt.annotate(xy = (float(x), float(y)), s = item.brand[0:1] + str(count))
  54.            
  55.            
  56.         plt.show(block=False)
  57.         raw_input('>')
  58.        
  59.     def __init__(self, item_attributes = ('price', 'power'), k_on = 1, x_limit = 10000, y_limit = 5000):
  60.         Plot.k_on = k_on
  61.         Plot.x_limit = x_limit
  62.         Plot.y_limit = y_limit
  63.        
  64.         Plot.x = item_attributes[0]; Plot.y = item_attributes[1]
  65.         self.plot()
  66.        
  67. #--------------------------------------------------------------------------------
  68.  
  69. class Item(FatalError, Debug, object):
  70.    
  71.     fmt_spec = 'input|output ^\s+.+Rs\..+ ^.+$'
  72.    
  73.     def x_brand(self):
  74.         #creates two dictonaries and then uses the brand-keys from dict to search the title for a matching
  75.         #known brand
  76.         color_to_brand = { 'g' : ['Bosch', 'Makita', 'Hitachi' ],
  77.                            'm' : ['Walt', 'Maktec', 'Decker', 'Skil', 'Stanley', 'Ralli' ],
  78.                            'r' : ['Metabo', 'Milwaukee', 'Fein', 'Festool' ],
  79.                            'y' : [ 'Dongcheng', 'Cumi', 'JCB', 'Ferm', 'Maf', 'Eastman', 'Yking' ] }
  80.  
  81.         brand_to_color = dict()
  82.         for color, brands in color_to_brand.items():
  83.             for brand in brands:
  84.                 brand_to_color.setdefault(brand, color)
  85.  
  86.         #locate color for brand
  87.         for brand in brand_to_color.keys():
  88.             m = re.search(brand, self.title, flags=re.I)
  89.             if m:
  90.                 self.color = brand_to_color[brand]
  91.                 self.brand = brand
  92.                 return
  93.         #return nobrand, default color
  94.         self.brand = ''; self.color = 'k'
  95.  
  96.     def x_title(self, txt):
  97.         #set title of object
  98.         lis_t = re.findall(r'([A-Z].+?)\n.    +?   by ', txt, re.M|re.S|re.X)
  99.         if len(lis_t):
  100.             self.title = lis_t[0]
  101.        
  102.     def x_features(self, lis_t, pat):
  103.         if 'Features' not in pat: return
  104.         if len(lis_t) == 0:
  105.             self.features = ''; return
  106.        
  107.         lines = lis_t.strip().lower().split('\n')
  108.      
  109.         for line in lines:
  110.             line = line.split(':')
  111.             try:
  112.                 key = line[0].strip().strip('\n'); value = line[1].strip().strip('\n')
  113.                 old = getattr(self, 'features', ''); value = old + value
  114.                 setattr(self, key, value)
  115.             except Exception as e:
  116.                 pass
  117.                 #print e, lines, self.title,
  118.                
  119.     def x_description(self, lis_t, pat):
  120.         if 'Description' not in pat: return
  121.         if len(lis_t) == 0:
  122.             self.description = ''; return
  123.    
  124.         lines = lis_t.split('\n')
  125.        
  126.         txt = ''
  127.         for line in lines:
  128.             line = line.strip()
  129.             txt += line + ' '
  130.            
  131.         setattr(self, 'description', txt)
  132.    
  133.     def x_product(self, lis_t, pat):
  134.         if 'Specifications' not in pat: return
  135.    
  136.         lines = lis_t.split('\n')
  137.        
  138.         for line in lines:
  139.             if line is '': continue
  140.             #split line on the first digit-,. into key value pair which are
  141.             #converted to attributes of object
  142.             m = re.match(r'^([^0-9]+)([0-9-,.]+.+)', line)
  143.             if m:
  144.                 setattr(self, m.group(1).lower().strip(), m.group(2).strip())
  145.        
  146.     def x_price(self, lis_t, pat):
  147.         if 'Rs' not in pat: return
  148.    
  149.         lines = lis_t.split('\n')
  150.         for line in lines:
  151.             if 'DEL' in line:
  152.                 if getattr(self, 'price', None): return
  153.                 lis_t = re.findall(r'[0-9,]+', line)
  154.                 price_del = lis_t[0].replace(',', '')
  155.                 #price_del = re.sub(r'[^0-9,]+', '', line)
  156.                 self.price = float(price_del)
  157.             elif 'per piece' in line:
  158.                 if getattr(self, 'price', None): return
  159.                 m = re.search(r'([0-9,]+)', line)
  160.                 if m:
  161.                     price_per = m.group().replace(',', '')
  162.                     self.price = float(price_per)
  163.             elif re.match(r'\s+Rs\s* \. .+?Extra', line, flags=re.X):
  164.                 lis_t= re.findall(r'\s+Rs\s*\.\s*([0-9.,]+)', line)
  165.                 price = lis_t[0].strip()
  166.                 self.price = float(price)
  167.             else:
  168.                 pass
  169.        
  170.     items = []    
  171.     def __init__(self, item = []):
  172.         if len(item) == 0: return
  173.         if re.match(r'Drilling Machine', item):
  174.             lis_t = re.findall(r'\s*\[ [0-9]+ ]([A-Z].+?)\n', item, re.X)
  175.             self.title = lis_t[0]
  176.            
  177.             lis_t = re.findall(r'Rs\.\s+([0-9,]+)', item)
  178.             self.price = float(lis_t[0].replace(',', ''))
  179.             self.price_del = float(lis_t[0].replace(',', ''))
  180.            
  181.         #add myself to a global list
  182.         Item.items.append(self)
  183.    
  184.         self.item = item
  185.         self.x_title(item)
  186.         if not getattr(self, 'title', ''):
  187.             raise FatalError('no title for ' + item)
  188.             sys.exit(0)
  189.            
  190.         pats = [
  191.                  r'by.+?\s+ Features\n\s+  (.+?) (?=\[)',
  192.                  r'\n\s+Description (.+)  (?=Product \s Spec)',
  193.                  r'\nProduct \s Specifications\n(.+) (?=\nQuick)',
  194.                  r'(Rs.+)'
  195.                 ]
  196.        
  197.         for pat in pats:
  198.             lis_t = re.findall(pat, item, flags = re.X|re.M|re.S)
  199.             if len(lis_t) == 0: continue
  200.             #write your pat so that it always returns exactly one or zero matched object
  201.             if len(lis_t) > 1:
  202.                 print len(lis_t), ' ', pat, self.title
  203.                 sys.exit(0)
  204.                
  205.             lines = lis_t[0]
  206.            
  207.             self.x_features(lines, pat); self.x_description(lines, pat)
  208.             self.x_product(lines, pat);  self.x_price(lines, pat)
  209.                                    
  210.         self.x_brand()
  211.    
  212.     def __str__(self):
  213.         return ''
  214.  
  215.  
  216. class ImpactDrill(Item, Plot):
  217.     def x_pwr(self):
  218.         for key, value in self.__dict__.items():
  219.             if any(map( (lambda x: x in key), ('power', 'watt'))):
  220.                 tmp = re.sub(r'[^0-9-.]', '', value)
  221.                 try:
  222.                     self.power = float(tmp.split('-')[0])
  223.                 except Exception as e:
  224.                     print e, key, value, self.title
  225.                     continue
  226.                 return
  227.        
  228.         #corner case solutions if above doesn't work
  229.         m = re.search(r'([0-9]+)\s*W', self.title)
  230.         if m is not None:
  231.             self.power = m.group(1)
  232.             return
  233.        
  234.         self.power = 0
  235.                
  236.     def __init__(self, item):
  237.         super(ImpactDrill, self).__init__(item)
  238.         self.x_pwr()
  239.        
  240.  
  241. class AngleGrinder(Item, Plot):
  242.     def x_pwr(self):
  243.         for key, value in self.__dict__.items():
  244.             if any(map( (lambda x: x in key), ('power', 'watt'))):
  245.                 tmp = re.sub(r'[^0-9-.]', '', value)
  246.                 try:
  247.                     self.power = float(tmp.split('-')[0])
  248.                 except Exception as e:
  249.                     print e, key, value, self.title
  250.                     continue
  251.                 return
  252.  
  253.         #corner case solutions if above doesn't work
  254.         m = re.search(r'([0-9]+)\s*W', self.title)
  255.         if m is not None:
  256.             self.power = m.group(1)
  257.             return
  258.  
  259.         self.power = 0
  260.    
  261.     def x_dia(self):
  262.         for key, value in self.__dict__.items():
  263.             if any(map( (lambda x: x in key), ('dia', 'Wheel Size', 'Skid Length'))):
  264.                 self.dia = value.replace('mm', '').replace('max', '').strip()
  265.                 if re.search(r'Inch', value, re.I):
  266.                     value = re.sub(r'[^0-9.]', '', value)
  267.                     self.dia = float(value) * 25.4
  268.                    
  269.                 if '/' in value:
  270.                     value = value.split('/')
  271.                     self.dia = value[0]
  272.                 return
  273.        
  274.         m = re.search(r'([0-9]+)\s*mm', self.title, flags = re.I)
  275.         if m is not None:
  276.             self.dia = int(m.group(1))
  277.             return
  278.         try:
  279.             m = re.search(r'([0-9.]+)\s*(inch)', self.title, flags = re.I)
  280.             if m is not None:
  281.                 self.dia = float(m.group(1) * 25.4)
  282.                 return
  283.         except Exception as e:
  284.             print e, m.group(1), self.title
  285.        
  286.         self.dia = 0
  287.  
  288.    
  289.     def __init__(self, item):
  290.         super(AngleGrinder, self).__init__(item)
  291.         self.x_pwr()
  292.         self.x_dia()
  293.  
  294. class JigSaw(AngleGrinder, Item, Plot):
  295.     pass
  296.  
  297. class Drill(AngleGrinder, Item, Plot):
  298.     pass
  299.  
  300. class ChopSaw(Item, Plot):
  301.     pass
  302.  
  303. class HotAir(AngleGrinder,  Item, Plot):
  304.     pass
  305.  
  306. class Saw(AngleGrinder, Item, Plot):
  307.     pass
  308.            
  309. #--------------------------------------------------------------------------------
  310. class IndustryBuying(Plot, Debug, object):
  311.     f_to_cls = { 'jigsaw_pages' : JigSaw, 'angle_grinder_pages' : AngleGrinder, 'impact_drill_pages' : ImpactDrill,
  312.                  'drill_pages' : Drill, 'chopsaw_pages' : ChopSaw, 'saw_pages' : Saw, 'hotair_pages' : HotAir }
  313.    
  314.     def slurp_file(self, fname):
  315.         fh = open(fname, 'r')
  316.         txt = fh.read()
  317.         return txt
  318.  
  319.     def get_items_from(self, txt):
  320.         #extract pages from txt
  321.         pages = re.findall(r'Home \[(.+?)^Company', txt, re.M|re.S)
  322.         if not len(pages):
  323.             raise FatalError('no pages')
  324.        
  325.         #extract item, -- to -- from page, for all pages, and return all items
  326.         items = []
  327.         for count, page in enumerate(pages):
  328.             #item_list = re.findall(r'_______\n+\s+(.+)_______', page, re.M|re.S)
  329.             item_list = re.findall(r'\n([A-Z]+.+?by.+?Rs.+?)(?=ADD TO CART)', page, re.M|re.S)
  330.             if len(item_list):
  331.                 for item in item_list:
  332.                     items.append(item)
  333.         self.debug(count)
  334.         return items
  335.  
  336.     def __init__(self, fname = ''):
  337.         #read all our data and break it up into pages, then break up pages into items
  338.         txt = self.slurp_file(fname)
  339.         items = self.get_items_from(txt)
  340.        
  341.         Cls = self.f_to_cls[fname]
  342.        
  343.         #build Item objects from item-txt
  344.         for item in items:
  345.             Cls(item)
  346.            
  347.         #x_limit can be None or you have to set y_limit as well
  348.         Plot.__init__(self, item_attributes = ('price', 'power'), k_on = 0, x_limit = 25000, y_limit = 5000)
  349.            
  350. #--------------------------------------------------------------------------------
  351.  
  352. if __name__ == '__main__':
  353.     #x = IndustryBuying('drill_pages')
  354.     #x = IndustryBuying('impact_drill_pages')
  355.     x = IndustryBuying('angle_grinder_pages')
  356.     #x = IndustryBuying('saw_pages')
  357.     #x = IndustryBuying('hotair_pages')
  358.     #x = IndustryBuying('jigsaw_pages')
  359.     #x = IndustryBuying('chopsaw_pages')
Add Comment
Please, Sign In to add comment