Beispiel #1
0
 def _send_notification(self, site):
     site_item = site.get_item_name()
     logger.info("Sending notification for '%s'", site_item)
     self._notifier.notify(
         site.get_url(friendly=True),
         "Watcher signaled for item '{item}'".format(item=site_item),
         site.get_url())
Beispiel #2
0
    def refresh_sites(self):
        logger.info("Refreshing sites")

        for handle in self._driver.window_handles:
            self._driver.switch_to_window(handle)
            self._driver.refresh()
        self._switch_to_main_window()
Beispiel #3
0
 def __init__(self, browser, interval, notifier):
     logger.info("Initializing watcher")
     super().__init__()
     self._shutdown = False
     self._browser = browser
     self._interval = interval
     self._notifier = notifier
Beispiel #4
0
 def __init__(self, remote_notifications=False):
     self._local_notifier = LocalNotify()
     if remote_notifications:
         self._remote_notifier = RemoteNotify()
         logger.info(
             "Registering a new Remote Push Notification (RPN) endpoint")
         remote_notify_info = self._remote_notifier.register()
         self._display_remote_notify_info(str(remote_notify_info))
     else:
         self._remote_notifier = None
Beispiel #5
0
    def _display_remote_notify_info(cls, remote_notify_info):
        if os.name == 'nt':
            # Windows cmd/powershell does not display QR code properly - stripping it off
            remote_notify_info = remote_notify_info[:remote_notify_info.index(
                "Or scan this QR code")]

        logger.info(
            """\n\n****************** REMOTE PUSH NOTIFICATIONS ********************\n
            %s
            \nNOTE: iOS and Safari NOT supported
            \n*****************************************************************\n""",
            remote_notify_info
        )
Beispiel #6
0
 def _evaluate_baseline(self, site, n_elements):
     site_notify_type = site.get_notify_type()
     element_baseline = None
     if (n_elements == 0 and site_notify_type == NotifyOnType.APPEAR) or \
             (n_elements > 0 and site_notify_type == NotifyOnType.DISAPPEAR):
         logger.info("Tracking '%s:%s[%s]' for site '%s'",
                     site.get_watch_type().value, site.get_text(),
                     site_notify_type.value, site.get_url(friendly=True))
         element_baseline = n_elements
         logger.debug("'%s': [baseline=%d]", element_baseline,
                      site.get_url(friendly=True))
     else:
         logger.warning(
             "Unable to establish an accurate baseline with '%s:%s[%s]' for site '%s' - "
             "Action may have already occurred?",
             site.get_watch_type().value, site.get_text(),
             site_notify_type.value, site.get_url(friendly=True))
     site.set_element_baseline(element_baseline)
     return element_baseline
Beispiel #7
0
    def scrape_sites_by_xpath(self):
        logger.info("Scraping sites for data...")

        scrape_results = []
        for i in range(len(self._driver.window_handles)):
            self._driver.switch_to_window(self._driver.window_handles[i])
            site = self._sites[i]
            n_elements = 0
            for xpath in site.get_xpaths():
                tmp_n_elements = len(
                    self._driver.find_elements_by_xpath(xpath))
                if tmp_n_elements and n_elements > 0:
                    # Multiple elements with same text - too ambiguous to refine search
                    raise ValueError(
                        "Multiple elements found containing '{text}' - is ambiguous"
                        .format(text=site.get_text()))
                elif n_elements == 0:
                    n_elements = tmp_n_elements
            scrape_results.append((site, n_elements))
        self._switch_to_main_window()
        return scrape_results
Beispiel #8
0
    def start(self):
        """
        Starts the pyww application.

        Returns
        -------
        None
        """

        signal.signal(signal.SIGINT, self._shut_down)
        logger.info(self._ascii_art)
        try:
            self._watcher = Watcher(
                Chrome(site_parser.parse(self._sites),
                       headless=(ENVIRON_DEBUG_KEY not in environ)),
                self._interval,
                Notifier(remote_notifications=self._remote_notifications))
            self._watcher.start()
        except ValueError as err:
            logger.error(str(err))
        finally:
            if self._watcher:
                self._wait_on_watcher()
Beispiel #9
0
    def load_sites(self):
        if not self._sites:
            return False

        for i in range(len(self._sites)):
            site = self._sites[i]
            logger.info("Loading '%s' for '%s'", site.get_url(friendly=True),
                        site.get_item_name())
            if i == 0:
                self._driver.get(site.get_url())
            else:
                self._driver.execute_script(
                    "window.open('{url}', '_blank');".format(
                        url=site.get_url()))

            # Make sure the driver window handles are updated before loading another site
            expected_handles = i + 1
            while len(self._driver.window_handles) != expected_handles:
                logger.debug("Waiting for %d window handles from driver...",
                             expected_handles)
                sleep(0.1)
        self._switch_to_main_window()
        return True
Beispiel #10
0
    def _track_changes(self, scrape_results):
        for site, n_elements in scrape_results:
            site_url = site.get_url(friendly=True)
            site_notify_type = site.get_notify_type()
            element_baseline = site.get_element_baseline()
            if element_baseline is None:
                element_baseline = self._evaluate_baseline(site, n_elements)
                # Unable to track changes if a baseline is not established
                if element_baseline is None:
                    logger.warning("Skipping tracking '%s'...", site_url)
                    continue

            if (n_elements > element_baseline and site_notify_type == NotifyOnType.APPEAR) or \
                    (n_elements < element_baseline and site_notify_type == NotifyOnType.DISAPPEAR):
                # Action occurred - alert user
                logger.info("ALERT '%s:%s[%s]' in '%s'",
                            site.get_watch_type().value, site.get_text(),
                            site_notify_type.value, site_url)
                logger.debug("'%s': [baseline=%d, [n_elements=%d]", site_url,
                             element_baseline, n_elements)
                self._send_notification(site)
            else:
                # No changes found yet
                logger.info("No changes found in '%s'", site_url)
Beispiel #11
0
 def _shut_down(self, *_):
     logger.info("Shutdown signaled for pyww...")
     self._watcher.shut_down()
Beispiel #12
0
 def _sleep_on_interval(self):
     logger.info("Waiting %ds before refresh", self._interval)
     for _ in range(self._interval):
         if self._shutdown:
             break
         sleep(1)
Beispiel #13
0
 def _teardown(self):
     logger.info("Tearing down watcher")
     self._browser.close()