Пример #1
0
class EanSearch(BarcodeDecoder):
    def __init__(self):
        BarcodeDecoder.__init__(self)
        self.logger = Logger('EanSearch')

    @staticmethod
    def url(barcode):
        return 'https://www.ean-search.org/perl/ean-search.pl?q=' + quote(
            barcode.encode('utf-8'))

    def item(self, barcode):
        url = EanSearch.url(barcode)
        try:
            blob = BeautifulSoup(get(url).text, "html.parser")
            if 'Excessive use' in blob.text:
                self.logger.warn("Overuse")
                return None

            name = blob.find('a', {'href': "/perl/ean-info.pl?ean=" + barcode})
            if name:
                return Item(name=name.text, url=url)
            else:
                return None
        except Exception as e:
            self.logger.warn("Exception while searching for " + barcode +
                             "\n" + str(e))
            return None
Пример #2
0
 def __init__(self,
              shop,
              shop_list,
              web_hook_url=None,
              web_server_ip=None,
              web_server_port=8080,
              asynchron=True):
     self.logger = Logger('ShopSync')
     self.shop = shop
     self.wu_list = shop_list
     self.meta = MetaShop(self, self.wu_list)
     self.shop_list_rev = 0
     self.shop_task_revs = {}
     self.shop_items = {}
     self.choice = Choice("choices-" + shop.__class__.__name__ + ".db")
     if web_hook_url:
         self.web_hook_url = web_hook_url
         self.web_server_ip = web_server_ip
         self.web_server_port = web_server_port
         function = self.start_hook
     else:
         function = self.listen
     if asynchron:
         start_new_thread(function, ())
     else:
         function()
Пример #3
0
    def __init__(self, email, password, cookie_file="ayn_cookies"):
        self.logger = Logger('AllYouNeed')
        self.base_url = 'https://www.allyouneedfresh.de'
        self.basket_url = 'https://www.allyouneedfresh.de/warenkorbuebersicht'
        self.take_url = 'https://www.allyouneedfresh.de/responsive/pages/checkout1.jsf'

        self.take_page_view_state = None
        self.driver = webdriver.PhantomJS(executable_path='/usr/local/bin/phantomjs')
        # self.driver = webdriver.Chrome('./chromedriver')
        self.driver.set_window_size(1280, 1024)
        Shop.__init__(self, email, password, cookie_file)
Пример #4
0
    def __init__(self, email, password, captcha_service, cookie_file="kl_cookies"):
        self.logger = Logger('Kaufland')
        self.captcha_service = captcha_service
        self.base_url = 'https://shop.kaufland.de'
        self.login_url = "https://shop.kaufland.de/login"
        self.account_url = "https://shop.kaufland.de/my-account"
        self.take_url = 'https://shop.kaufland.de/cart/modify'
        self.basket_url = 'https://shop.kaufland.de/cart'

        self.driver = webdriver.PhantomJS(executable_path='/usr/local/bin/phantomjs')
        # self.driver = webdriver.Chrome('./chromedriver')
        self.driver.set_window_size(1280, 1024)

        Shop.__init__(self, email, password, cookie_file)
Пример #5
0
    def __init__(self, device):
        self.logger = Logger("Barcode scan")

        devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()]
        for d in devices:
            if device in d.name:
                self.logger.info("Found device " + d.name)
                self.device = d
                break

        if self.device is None:
            raise Exception("No barcode device found")

        self.q = queue.Queue()
        start_new_thread(self.scan, ())
Пример #6
0
 def __new__(cls, *args, **kwds):
     #Singleton interface for logger
     thisSingleton = cls.__dict__.get("__thisSingleton__")
     if thisSingleton is not None:
         return thisSingleton
     cls.__thisSingleton__ = thisSingleton = LOG.__new__(cls)
     return thisSingleton
Пример #7
0
    def __init__(self, barcode_descriptor, config, shopList, asynchron=True):
        self.logger = Logger('BarcodeSync')
        self.barcode_descriptor = barcode_descriptor
        self.barcode_reader = BarcodeReader(config['barcode_device_name'])
        self.shop_list = shopList
        self.file_name = "barcode.db"
        self.matches = self.load()
        if 'notification_homeassistant_bearer_token' in config:
            self.notification_url = config['notification_homeassistant_url']
            self.bearer_token = config[
                'notification_homeassistant_bearer_token']

        self.notify("Booted")
        if asynchron:
            start_new_thread(self.listen, ())
        else:
            self.listen()
Пример #8
0
class Geizhals(BarcodeDecoder):
    def __init__(self):
        BarcodeDecoder.__init__(self)
        self.logger = Logger('Geizhalz')

    @staticmethod
    def url(barcode):
        return 'https://geizhals.de/?fs=' + quote(barcode.encode('utf-8'))

    def item(self, barcode):
        url = Geizhals.url(barcode)
        try:
            blob = BeautifulSoup(get(url).text, "html.parser")
            blob = blob.find('div', {'id': 'gh_artbox'})
            if not blob:
                return None

            return Item(name=self.parse_name(blob),
                        price=self.parse_price(blob),
                        url=url)
        except Exception as e:
            self.logger.warn("Exception while searching for " + barcode +
                             "\n" + str(e))
            return None

    def parse_name(self, blob):
        header = blob.find('h1', {'class': 'arthdr'})
        if header:
            return header.find('span', {'itemprop': 'name'}).text
        else:
            return blob.find('b', {'itemprop': 'name'}).__next__

    def parse_price(self, blob):
        header = blob.find('h1', {'class': 'arthdr'})
        if header:
            price = header.find('span', {'class': 'gh_price'})
            price = price.text.replace('€', '').strip()
            p = price.split(',')

            return int(p[0]) * 100 + int(p[1])
        else:
            return None
Пример #9
0
class BarcodeReader:
    def __init__(self, device):
        self.logger = Logger("Barcode scan")

        devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()]
        for d in devices:
            if device in d.name:
                self.logger.info("Found device " + d.name)
                self.device = d
                break

        if self.device is None:
            raise Exception("No barcode device found")

        self.q = queue.Queue()
        start_new_thread(self.scan, ())

    def scan(self):
        while True:
            try:
                self.logger.info('Waiting for scanner data')
                barcode = self.read()
                self.logger.info("Scanned barcode '{0}'".format(barcode))
                self.q.put(barcode)
            except Exception:
                traceback.print_exc()

    def read(self):
        current_barcode = ""
        for event in self.device.read_loop():
            if event.type == evdev.ecodes.EV_KEY and event.value == 1:
                keycode = categorize(event).keycode
                if keycode == 'KEY_ENTER':
                    return current_barcode
                else:
                    current_barcode += keycode[4:]
Пример #10
0
class BarcodeSync:
    def __init__(self, barcode_descriptor, config, shopList, asynchron=True):
        self.logger = Logger('BarcodeSync')
        self.barcode_descriptor = barcode_descriptor
        self.barcode_reader = BarcodeReader(config['barcode_device_name'])
        self.shop_list = shopList
        self.file_name = "barcode.db"
        self.matches = self.load()
        if 'notification_homeassistant_bearer_token' in config:
            self.notification_url = config['notification_homeassistant_url']
            self.bearer_token = config[
                'notification_homeassistant_bearer_token']

        self.notify("Booted")
        if asynchron:
            start_new_thread(self.listen, ())
        else:
            self.listen()

    def save(self):
        with open(self.file_name, 'wb') as f:
            pickle.dump(self.matches, f)

    def load(self):
        try:
            with open(self.file_name, 'rb') as f:
                return pickle.load(f)
        except IOError:
            return {}

    def remember_choice(self, barcode, item):
        self.matches[barcode] = item
        self.save()

    def match(self, barcode):
        if barcode in self.matches:
            self.logger.info("Use old match for: " + barcode)
            return self.matches[barcode]
        else:
            return None

    def listen(self):
        while True:
            try:
                barcode = self.barcode_reader.q.get()
                self.logger.info("Work on: " + barcode)
                item = self.match(barcode)
                if not item:
                    item = self.barcode_descriptor.item(barcode)
                    if item:
                        self.remember_choice(barcode, item)
                if item:
                    self.logger.info("Detected: " + item.name)
                    self.add_barcode(item)
                else:
                    self.logger.info("Could not id: " + barcode)
            except Exception:
                traceback.print_exc()

    def add_barcode(self, item):
        self.notify(item.name)

        for task, existing in self.shop_list.list_items():
            self.logger.info("Existing item: " +
                             existing.selected_shop_item().name)
            if existing.synced() and item.name.lower(
            ) in existing.selected_shop_item().name.lower():
                existing.inc_amount()
                self.shop_list.update_item(task, existing)
                self.logger.info("Updated existing item: " +
                                 existing.selected_shop_item().name)
                return

        self.shop_list.create_item(item)

    def notify(self, message):
        if not self.bearer_token:
            self.logger.debug("no notification credentials provided")
            return

        headers = {
            "Content-Type": "application/json",
            "Authorization": "Bearer " + self.bearer_token,
        }

        data = {
            "title": "Barcode scan",
            "message": message,
            "data": {
                "group": "barcode_scan",
                "push": {
                    "interruption-level": "passive",
                    "sound": "none"
                }
            }
        }

        response = requests.request("POST",
                                    self.notification_url,
                                    headers=headers,
                                    data=json.dumps(data))
        self.logger.info("Notification " + message + "sent. Response: " +
                         str(response.status_code))
        self.logger.info("Create new item")
Пример #11
0
 def __init__(self):
     SubItem.__init__(self)
     self.logger = Logger('CheckCartAction')
     self.notes = None
Пример #12
0
class CheckCartAction(SubItem):
    def __init__(self):
        SubItem.__init__(self)
        self.logger = Logger('CheckCartAction')
        self.notes = None

    # noinspection SpellCheckingInspection
    def title(self):
        return "Einkaufswagen und Liste vergleichen"

    def selected(self):
        return False

    def action(self, meta_shop):
        cart_items = meta_shop.shop_sync.shop.cart()
        shop_items = []

        msg0 = ""
        msg1 = ""
        for task, item in meta_shop.wu_list.list_items():
            shop_item = item.selected_shop_item()
            if shop_item:
                shop_items.append(shop_item)
                if shop_item in cart_items:
                    for c in cart_items:
                        if c.name == shop_item.name:
                            if shop_item.amount != c.amount:
                                self.logger.warn(
                                    "Task item and shop item amounts differ: "
                                    + shop_item.name.encode('utf-8'))
                                msg0 += shop_item.name.encode(
                                    'utf-8') + ": " + str(
                                        shop_item.amount) + " vs. " + str(
                                            c.amount) + "\n"
                            break
                else:
                    self.logger.warn("Task item without shop item: " +
                                     shop_item.name.encode('utf-8'))
                    msg1 += " - " + shop_item.name.encode('utf-8') + "\n"

        msg2 = ""
        for cart_item in cart_items:
            if cart_item not in shop_items:
                self.logger.warn("Cart item without task: " +
                                 cart_item.name.encode('utf-8'))
                msg2 += " + " + cart_item.name.encode('utf-8') + "\n"

        msg = ""
        if msg0:
            msg += "Mengenunterschiede: Liste vs. Einkaufswagen\n" + msg0

        if msg1:
            msg += "\nNicht im Einkaufswagen gefunden:\n" + msg1

        if msg2:
            msg += "\nNicht auf der Liste gefunden:\n" + msg2

        self.notes = msg

    def note(self):
        return self.notes

    @classmethod
    def parse(cls, string):
        pass
Пример #13
0
 def __init__(self):
     SubItem.__init__(self)
     self.logger = Logger('ClearAction')
Пример #14
0
 def __init__(self):
     BarcodeDecoder.__init__(self)
     self.logger = Logger('EanSearch')
Пример #15
0
class Kaufland(Shop):

    search_url_prefix = 'https://shop.kaufland.de/search?pageSize=48&sort=relevance&text='

    def __init__(self, email, password, captcha_service, cookie_file="kl_cookies"):
        self.logger = Logger('Kaufland')
        self.captcha_service = captcha_service
        self.base_url = 'https://shop.kaufland.de'
        self.login_url = "https://shop.kaufland.de/login"
        self.account_url = "https://shop.kaufland.de/my-account"
        self.take_url = 'https://shop.kaufland.de/cart/modify'
        self.basket_url = 'https://shop.kaufland.de/cart'

        self.driver = webdriver.PhantomJS(executable_path='/usr/local/bin/phantomjs')
        # self.driver = webdriver.Chrome('./chromedriver')
        self.driver.set_window_size(1280, 1024)

        Shop.__init__(self, email, password, cookie_file)

    @staticmethod
    def search_url(name):
        return Kaufland.search_url_prefix + quote(name.encode('utf-8'))

    def login(self):
        self.logger.info("Logging in...")
        self.session = Session()

        self.driver.get(self.account_url)
        time.sleep(2)

        x = self.driver.find_element_by_id('kLoginForm')
        x.find_element_by_id('j_username').send_keys(self.email)
        x.find_element_by_id('j_password').send_keys(self.password)
        x.find_element_by_tag_name('button').click()
        time.sleep(3)
        self.new_session_with_cookies(self.driver.get_cookies())
        self.save_session()

    def is_logged_in(self, html=None):
        if not html:
            html = self.session.get(self.account_url).text
        return html.find('Abmelden') > 0

    def save_session(self):
        with open(self.cookie_file, 'w') as f:
            pickle.dump(self.session.cookies, f)

    def load_session(self):
        try:
            with open(self.cookie_file) as f:
                cookies = pickle.load(f)
                self.session = Session()
                self.session.cookies = cookies
                return self.is_logged_in()
        except IOError:
            return False

    def get(self, url):
        html = self.session.get(url).text
        if self.is_logged_in(html):
            self.save_session()
            return html

        self.login()
        html = self.session.get(url).text
        if self.is_logged_in(html):
            self.save_session()
            return html

        self.logger.error("Can not log in")
        exit(1)

    def cart(self):
        blob = BeautifulSoup(self.get(self.basket_url), "html.parser")
        r = blob.select('section.product-list')
        if len(r) == 0:
            return []

        r = r[0]
        ids = []
        for i in r.findAll('article'):
            a = i.find('a')
            link = urllib.parse.urljoin(self.base_url, a['href'])
            title = i.find('p', {'class': 'product-list__title'}).text.strip()
            amount = i.find('div', {'class': 'product-list__amount'})
            article_id = amount['data-dynamicblock']
            amount = int(amount.find('input', {'name': 'quantity'}).get('value'))
            price = i.find('div', {'data-dynamiccontent': 'prices'})
            red = price.find('span', {'class': 'product-list__reduced-price'})
            if red:
                price = red
            price = price.text.replace('€', '').strip()
            price = int(float(price) * 100)

            title = unicodedata.normalize('NFKC', title)

            item = ShopItem(article_id, amount, title, price, link)
            ids.append(item)

        return ids

    def search(self, term, sub_term=None):
        html = self.get(Kaufland.search_url(term))
        ids = self.parse_search(html)

        split_terms = [x for x in re.split('-| |\n', term) if len(x) > 1]

        if 0 < len(ids) < 48:
            return self.order_by_matches(split_terms, ids)

        if sub_term and len(ids) == 0:
            return self.search(term + " " + sub_term)

        if len(split_terms) > 1:
            ids = []
            for criteria in split_terms:
                if len(criteria) > 1:
                    ids += self.search(criteria)

        return self.order_by_matches(split_terms, ids, max=20, perfect=0.6, cut_off=0.25)

    def parse_search(self, html):
        blob = BeautifulSoup(html, "html.parser")
        ids = []
        r = blob.select('div.productmatrix')
        if len(r) > 0:
            r = r[0]
            for i in r.findAll('article'):
                a = i.find('a')
                article_id = i['data-dynamicblock'].split('_')[0]
                link = urllib.parse.urljoin(self.base_url, a['href'])
                title = a.find('p', {'class': 'product-tile__infos--title'}).text.strip()
                price = a.find('div', {'class': 'product-tile__price--regular'})
                if not price:
                    price = a.find('div', {'class': 'product-tile__price--reduced'})
                price = price.text.replace('€', '').strip()
                price = int(float(price) * 100)

                title = unicodedata.normalize('NFKC', title)

                item = ShopItem(article_id, 1, title, price, link)
                ids.append(item)
        return ids

    def order_by_matches(self, terms, ids, max=None, perfect=None, cut_off=None):
        if len(ids) == 0:
            return []
        normal_fit = {}
        perfect_fit = {}
        normal_ids = []
        perfect_ids = []
        for item in ids:
            if item in normal_ids or item in perfect_ids:
                continue
            match = len([x for x in terms if x.lower() in item.name.lower()])
            if not cut_off or match > len(terms) * cut_off:
                normal_ids.append(item)
                normal_fit[item] = match
            if perfect and match > len(terms) * perfect:
                perfect_ids.append(item)
                perfect_fit[item] = match

        if len(perfect_fit) > 0:
            normal_ids = perfect_ids
            normal_fit = perfect_fit

        ordered = sorted(normal_ids, key=normal_fit.__getitem__, reverse=True)
        if max:
            ordered = ordered[:max]
        return ordered

    def take(self, item):
        html = self.get(Kaufland.search_url(item.name))
        blob = BeautifulSoup(html, "html.parser")
        token = blob.find('input', {'name': 'CSRFToken'}).get('value')

        self.session.post(self.take_url, data=[
            ('qty', item.amount),
            ('productCodePost', item.article_id),
            ('pageTemplate', 'producttile'),
            ('CSRFToken', token),
        ])
        self.save_session()

    def shelf_life(self, item_link):
        pass
Пример #16
0
 def __init__(self, config):
     ShopList.__init__(self)
     self.logger = Logger('Wunderlist')
     self.client = wunderpy2.WunderApi().get_client(config['wunderlist_token'], config['wunderlist_client_id'])
     self.list_id = config['wunderlist_list_id']
Пример #17
0
class AllYouNeed(Shop):

    search_url_prefix = 'https://www.allyouneedfresh.de/suchen?term='

    def __init__(self, email, password, cookie_file="ayn_cookies"):
        self.logger = Logger('AllYouNeed')
        self.base_url = 'https://www.allyouneedfresh.de'
        self.basket_url = 'https://www.allyouneedfresh.de/warenkorbuebersicht'
        self.take_url = 'https://www.allyouneedfresh.de/responsive/pages/checkout1.jsf'

        self.take_page_view_state = None
        self.driver = webdriver.PhantomJS(executable_path='/usr/local/bin/phantomjs')
        # self.driver = webdriver.Chrome('./chromedriver')
        self.driver.set_window_size(1280, 1024)
        Shop.__init__(self, email, password, cookie_file)

    @staticmethod
    def search_url(name):
        return AllYouNeed.search_url_prefix + quote(name.encode('utf-8'))

    def j_faces_view_state(self, url):
        res = self.session.get(url)
        blob = BeautifulSoup(res.text, "html.parser")
        return blob.find('input', {'name': 'javax.faces.ViewState'}).get('value')

    def login(self):
        self.logger.info("Logging in...")
        self.session = Session()
        self.driver.get(self.base_url)
        time.sleep(1)
        self.driver.find_element_by_link_text("Anmelden").click()
        time.sleep(1)
        self.driver.find_element_by_id('zipCodeForm:loginEmail').send_keys(self.email)
        self.driver.find_element_by_id('zipCodeForm:loginPassword').send_keys(self.password)
        self.driver.find_element_by_id('zipCodeForm:loginButton').click()
        time.sleep(1)
        self.new_session_with_cookies(self.driver.get_cookies())
        self.take_page_view_state = self.j_faces_view_state(self.basket_url)
        self.save_session()

    def is_logged_in(self, html=None):
        if not html:
            html = self.session.get(self.base_url).text
        x = html.find('Anmelden/Registrieren')
        return x < 0

    def save_session(self):
        with open(self.cookie_file, 'w') as f:
            pickle.dump(self.driver.get_cookies(), f)

    def load_session(self):
        try:
            with open(self.cookie_file) as f:
                cookies = pickle.load(f)
                self.new_session_with_cookies(cookies)
                for cookie in cookies:
                    for k in ('name', 'value', 'domain', 'path', 'expiry'):
                        if k not in list(cookie.keys()):
                            if k == 'expiry':
                                cookie[k] = 1475825481
                    self.driver.add_cookie({k: cookie[k] for k in ('name', 'value', 'domain', 'path', 'expiry') if k in cookie})
                return self.is_logged_in()
        except IOError:
            return False

    def cart(self):
        html = self.session.get(self.basket_url).text
        x = html.find('Sie haben leider')
        if x > 0:
            return []

        blob = BeautifulSoup(html, "html.parser")
        r = blob.find('div', {'class': "panel-body table"})
        amount_col = r.findAll('span', {'class': 'td col-sm-1 nopadding text-nowrap text-center'})
        name_col = r.findAll('span', {'class': 'td col-sm-7 nopadding'})
        price_col = []
        for p in r.findAll('span', class_='td col-sm-3 text-center nopadding'):
            pr = p.find('span', class_='styledPrice')
            if pr:
                price_col.append(pr)

        article_col = []
        for d in r.findAll('div', class_="row"):
            for clazz in d.get('class'):  # article_col[0] is the table header
                if 'artId' in clazz:
                    article_col.append(int(clazz[5:]))
                    break

        res = []
        for i in range(len(amount_col)):
            article_id = article_col[i]
            amount = int(amount_col[i].text.replace('x', '').strip())
            item = name_col[i]
            link = urllib.parse.urljoin(self.base_url, item.find('a')['href'])
            price = extract_price(price_col[i])

            item.span.clear()
            name = item.text.strip()
            res.append(ShopItem(article_id, amount, name, price, link))
        return res

    def search(self, term, sub_term=None):
        html = self.session.get(AllYouNeed.search_url(term)).text
        blob = BeautifulSoup(html, "html.parser")
        r = blob.select('div.product-box.item')

        ids = []
        perfect = []
        for i in r:
            link = i.find('a', class_='article-link')['href']
            link = urllib.parse.urljoin(self.base_url, link)
            price = extract_price(i.find('span', class_='product-price'))

            desc = i.find('div', class_='product-description')
            details = desc.find('span', class_='product-story').find('span')

            if details:
                details.extract()

            span = desc.find('span', class_='text-nowrap')
            if span:
                span.replace_with('')

            desc = desc.text.strip().replace('\n', ', ')
            for c in i['class']:
                if 'artId' in c:
                    article_id = int(c[5:])
                    item = ShopItem(article_id, 1, desc, price, link)
                    ids.append(item)
                    match = True
                    for criteria in term.split():
                        if criteria.lower() not in desc.lower():
                            match = False
                            break
                    if match:
                        perfect.append(item)

                    break

        return perfect if perfect else ids

    def take(self, item):
        self.driver.get(AllYouNeed.search_url(item.name))
        time.sleep(2)

        ccc = self.driver.find_element_by_css_selector('.artId' + str(item.article_id))
        ccc = ccc.find_element_by_class_name('product-cart')
        inps = ccc.find_elements_by_tag_name('input')
        if len(inps) == 0 or not inps[0].is_displayed():
            self.logger.info("taking..")
            ccc = ccc.find_element_by_tag_name('a')
            ccc.click()
            time.sleep(3)

            ccc = self.driver.find_element_by_css_selector('.artId' + str(item.article_id))
            ccc = ccc.find_element_by_class_name('product-cart')
            inp = ccc.find_element_by_tag_name('input')
        else:
            inp = inps[0]
        inp.click()
        # inp.send_keys(Keys.CONTROL, 'a')
        inp.send_keys(str(item.amount))
        inp.send_keys(Keys.ENTER)
        time.sleep(1)

    def shelf_life(self, item_link):
        html = self.session.get(item_link).text
        blob = BeautifulSoup(html, "html.parser")
        for desc in blob.findAll('div', class_='product-card'):
            for more in desc.findAll('div', class_='product-story'):
                for td in more.findAll('div', class_='td'):
                    date = td.find("strong").text.strip()
                    return datetime.strptime(date, '%d.%m.%Y')
        return datetime.max
Пример #18
0
    '--rmsd_threshold',
    dest="rmsd_threshold",
    action="store",
    default=0.3,
    type=float,
    help=
    "If set, the RMSD threshold for considering a superimposition as correct will take this value. If not, it will be 0.3 by default. The output of the program is very sensitive to this value, we advise to be careful when modifying it."
)

parser.add_argument(
    '-cl',
    '--clashes_threshold',
    dest="clashes",
    action="store",
    default=30,
    type=int,
    help=
    "If set, the threshold of the number of clashes will take this value. If not, it will be 30 by default. The output of the program is very sensitive to this value, we advise to be careful when modifying it."
)
parser.add_argument(
    '-opt',
    '--optimisation',
    dest="optimisation",
    action="store_true",
    default=False,
    help=
    "If set, it runs an optimisation on your output structure and saves it in a separate PDB file. It is only available for structures with less than 99.999 residues"
)

l = Logger()
Пример #19
0
class CodeCheck(BarcodeDecoder):

    def __init__(self):
        BarcodeDecoder.__init__(self)
        self.logger = Logger('Codecheck')

    @staticmethod
    def url(barcode):
        base_url = 'http://www.codecheck.info/product.search'
        return '{0}?q={1}&OK=Suchen'.format(base_url, quote(barcode.encode('utf-8')))

    def item(self, barcode):
        url = CodeCheck.url(barcode)
        try:
            blob = BeautifulSoup(get(url).text, "html.parser")
            if not blob.find("meta", property="og:title"):
                return None

            ratings, numeric = self.parse_ratings(blob)

            return Item(name=self.parse_name(blob),
                        sub_name=self.parse_sub_name(blob),
                        price=self.parse_price(blob),
                        url=url,
                        ingredients=self.parse_ingredients(blob),
                        ratings=ratings,
                        num_rating=numeric)
        except Exception as e:
            self.logger.warn("Exception while searching for " + barcode + "\n" + str(e))
            return None

    def parse_name(self, blob):
        return blob.find("meta", property="og:title")['content']

    def parse_sub_name(self, blob):
        return blob.find('h3', {'class': 'page-title-subline'}).text

    def parse_ingredients(self, blob):
        return blob.find('span', {'class': 'rated-ingredients'}).text

    def parse_price(self, blob):
        r = blob.find('div', {'class': "area", 'id': "price-container"})
        r = r.find('div', {'class': "lighter-right"})
        if not r:
            return None

        r.span.clear()
        price = r.text.replace('EUR', '').strip()
        p = price.split(',')

        return int(p[0]) * 100 + int(p[1])

    def parse_ratings(self, blob):
        r = blob.find('div', {'class': "area spacing ingredient-rating"})
        if not r:
            return [], 0

        ratings = r.findAll('div', {'class': 'c c-2 rating-color'})
        rate = r.findAll('div', {'class': 'c c-3 rating-color'})
        numbers = r.findAll('div', {'class': 'rating-group-header'})

        rate_list = []
        agg = 0
        num = 0
        for i in range(len(ratings)):
            val = self.map_rating_class(numbers[i].get('class', [])[1])
            if val > 0:
                agg += val
                num += 1
                rate_list.append(ratings[i].text + ": " + rate[i].text)

        return rate_list, agg / num if num else 0

    def map_rating_class(self, clazz):
        num = int(clazz.replace('pr-rating-', '')) / 100
        if num in [1,8]:
            return 0
        else:
            return num
Пример #20
0
 def __init__(self):
     BarcodeDecoder.__init__(self)
     self.logger = Logger('Geizhalz')
Пример #21
0
 def __init__(self):
     BarcodeDecoder.__init__(self)
     self.logger = Logger('Codecheck')
Пример #22
0
class ShopSync:
    def __init__(self,
                 shop,
                 shop_list,
                 web_hook_url=None,
                 web_server_ip=None,
                 web_server_port=8080,
                 asynchron=True):
        self.logger = Logger('ShopSync')
        self.shop = shop
        self.wu_list = shop_list
        self.meta = MetaShop(self, self.wu_list)
        self.shop_list_rev = 0
        self.shop_task_revs = {}
        self.shop_items = {}
        self.choice = Choice("choices-" + shop.__class__.__name__ + ".db")
        if web_hook_url:
            self.web_hook_url = web_hook_url
            self.web_server_ip = web_server_ip
            self.web_server_port = web_server_port
            function = self.start_hook
        else:
            function = self.listen
        if asynchron:
            start_new_thread(function, ())
        else:
            function()

    def start_hook(self):
        self.sync_shop_list()
        self.sync_shop_list()
        self.wu_list.create_web_hook(self.web_hook_url, self.web_server_port)
        run_simple(self.web_server_ip, self.web_server_port, self.hook)

    @Request.application
    def hook(self, _):
        self.timed_sync()
        return Response()

    def timed_sync(self):
        try:
            self.sync_shop_list()
        except:
            Timer(10 * 60.0, self.timed_sync).start()  # 10 Minutes
        return Response()

    def listen(self):
        wait = [10, 30, 60, 120, 240, 480]
        wait_index = 0
        while True:
            try:
                change = self.sync_shop_list()
                if change:
                    wait_index = 0
                else:
                    sleep(wait[wait_index])
                    wait_index = min(wait_index + 1, len(wait) - 1)
            except Exception:
                traceback.print_exc()

    def sync_shop_list(self):
        if self.shop_list_rev == self.wu_list.list_revision():
            return False

        new, changed, deleted_ids, meta_changed = self.detect_changed_tasks()
        for iid in deleted_ids:
            self.remove_item_by_id(iid)

        for task in new:
            self.new_item(task)

        for task in changed:
            self.update_item(task)

        if meta_changed:
            self.meta.sync()

        self.update_meta()

        return len(new) + len(changed) + len(deleted_ids) > 0 or meta_changed

    def update_meta(self):
        shop_items = [
            item.selected_shop_item()
            for item in list(self.shop_items.values()) if item.synced()
        ]
        price = 0
        for s in shop_items:
            price += s.amount * s.price

        self.meta.set_price(price)

    def detect_changed_tasks(self):
        self.shop_list_rev = self.wu_list.list_revision()
        new_tasks = self.wu_list.list_tasks()

        meta_changed = self.meta.detect_changes(new_tasks)

        changed = []
        new = []
        for new_task in new_tasks:
            if self.wu_list.is_meta_item(new_task):
                continue
            iid = new_task['id']
            revision = new_task['revision']
            if iid in self.shop_task_revs:
                if self.shop_task_revs[iid] != revision:
                    self.shop_task_revs[iid] = revision
                    changed.append(new_task)
            else:
                self.shop_task_revs[iid] = revision
                new.append(new_task)

        deleted_ids = []
        for iid in self.shop_task_revs:
            found = False
            for new_task in new_tasks:
                if iid == new_task['id']:
                    found = True
                    break
            if not found:
                deleted_ids.append(iid)

        for iid in deleted_ids:
            self.shop_task_revs.pop(iid)

        return new, changed, deleted_ids, meta_changed

    def remove_item_by_id(self, iid):
        item = self.shop_items.pop(iid)
        self.logger.info("delete - " + item.name.encode('utf-8'))

        if item.synced():
            self.shop.delete(item.selected_shop_item())

    def new_item(self, task):
        self.logger.info("new - " + task['title'].encode('utf-8'))
        iid = task['id']
        item = self.wu_list.item_from_task(task)

        shop_items = self.shop.search(item.name, item.sub_name)
        item.set_shop_items(shop_items)
        if item.selected_item:
            self.choice.remember_choice(item)
        else:
            self.choice.match(item)

        if item.selected_item:
            self.shop.take(item.selected_shop_item())

        self.shop_items[iid] = item
        self.wu_list.update_item(task,
                                 item,
                                 rebuild_notes=True,
                                 rebuild_subs=True)

    def update_item(self, task):
        self.logger.info("Update - " + task['title'].encode('utf-8'))
        iid = task['id']
        item = self.wu_list.item_from_task(task)
        existing = self.shop_items[iid]

        if item != existing:
            self.remove_item_by_id(iid)
            self.new_item(task)
        else:
            update = False
            if item.synced() and not existing.synced():
                existing.select_shop_item(item.selected_shop_item())
                self.shop.take(existing.selected_shop_item())
                update = True
            if not item.synced() and existing.synced():
                self.shop.delete(existing.selected_shop_item())
                existing.select_shop_item(None)
                update = True

            if existing.amount != item.amount:
                existing.amount = item.amount
                if existing.synced():
                    self.shop.take(existing.selected_shop_item())
                    update = True
            if update:
                self.choice.remember_choice(existing)
                self.shop_task_revs[iid] = self.wu_list.update_item(
                    task, existing)