コード例 #1
0
    def check_page(self, title: str):
        """ Make sure we are on the right page by checking the title"""
        try:
            assert title in self.browser.driver.title
        except AssertionError:
            raise NameError(_("Page with wrong title"))

        print80(_("Connected to Wunderground"))
コード例 #2
0
 def wait_until_page_is_loaded(self):
     try:
         WebDriverWait(self.browser.driver, self.TIMEOUT).until(
             ec.presence_of_element_located(
                 (By.ID, "hourly-forecast-table")))
         print80(_("Page is ready"))
     except TimeoutException:
         raise TimeoutException(_("Page took too much time to load"))
コード例 #3
0
 def get_atm_pressure_at_station(self):
     _elem = self.browser.driver.find_element_by_xpath(
         '//*[@id="inner-content"]/div[3]/div[2]/div/div[1]'
         "/div[1]/lib-additional-conditions/lib-item-box/div/"
         "div[2]/div/div[1]/div[2]/lib-display-unit/span/span[1]")
     self.P_INITIAL = float(_elem.text)
     print80(
         self.register_info(
             _("Current atmospheric pressure : {} hPa (ISA={:0.1f}m)").
             format(self.P_INITIAL, isa.altitude(pressure=self.P_INITIAL))))
     return self.P_INITIAL
コード例 #4
0
 def switch_to_metric(self):
     print80(_("Switching to metric"))
     self.browser.driver.find_element_by_id("wuSettings").click()
     self.browser.driver.find_element_by_css_selector(
         "[title^='Switch to Metric'").click()
     try:
         WebDriverWait(self.browser.driver, self.TIMEOUT_LONG).until(
             ec.text_to_be_present_in_element(
                 (By.ID, "hourly-forecast-table"), "hPa"))
     except TimeoutException:
         raise TimeoutException(_("Unable to switch to metric"))
コード例 #5
0
 def get_obs_time(self):
     _elem = self.browser.driver.find_element(By.XPATH,
                                              '//*[@id="app-root-state"]')
     _st = search(
         "obsTimeLocal&q;:&q;(....-..-.. ..:..:..)&q;",
         _elem.get_attribute("innerHTML"),
     )
     if _st is not None:
         return datetime.strptime(_st.group(1), "%Y-%m-%d %H:%M:%S")
     else:
         print80(self.register_error(_("Observation time not found.")))
         return datetime.now()
コード例 #6
0
    def hourly_forecast_url(self):
        if self.GEOLOCATION_ALWAYS_ON or (self.MISSING_LATLONG
                                          and not self.OVERRIDE_URL_EXISTS):

            self.browser.go_to(webpage=self.ANY_HTTPS_PAGE, geolocation=True)
            position = self.browser.get_lat_lon()
            self.save_lat_lon(pos=position)
            self.browser.quit()

            #  decreased precision (3 digits instead of 7) to partially preserve anonymity
            _hourly_forecast_url = self.GEOLOCATED_URL + "{:.3f},{:.3f}".format(
                position["latitude"], position["longitude"])

        elif self.OVERRIDE_URL_EXISTS:

            print80(_("Using override URL"))
            _hourly_forecast_url = self.OVERRIDE_URL

        elif not self.MISSING_LATLONG:

            if args.latitude is None:
                print80(
                    _("Using Lat/Lon found in {}").format(
                        self.CONFIG_FILENAME))
            print80(_("Latitude: {}").format(self.LATITUDE))
            print80(_("Longitude: {}").format(self.LONGITUDE))
            print()
            #  decreased precision (3 digits instead of 7) to partially preserve anonymity
            _hourly_forecast_url = self.GEOLOCATED_URL + "{:.3f},{:.3f}".format(
                self.LATITUDE, self.LONGITUDE)

        else:  # if everything fails, an example url
            _hourly_forecast_url = "https://www.wunderground.com/hourly/ca/montreal/IMONTR15"

        return _hourly_forecast_url
コード例 #7
0
 def get_station_elevation(self):
     try:
         _elem = self.browser.driver.find_element(
             By.XPATH,
             '//*[@id="inner-content"]/div[2]'
             "/lib-city-header/div[1]/div/span/span/strong",
         )
         self.ELEVATION = int(_elem.text)
     except NoSuchElementException:
         print80(
             self.register_error(
                 _("Elevation not found. Assuming it to be zero")))
         self.ELEVATION = 0
     print80(
         self.register_info(
             _("Weather station elevation : {}m (ISA={:7.2f}hPa)").format(
                 self.ELEVATION, isa.pressure(altitude=self.ELEVATION))))
コード例 #8
0
 def get_station_name(self):
     try:
         _elem = self.browser.driver.find_element_by_xpath(
             '//*[@id="inner-content"]/div[2]/lib-city-header/div[1]/div/div/a[1]'
         )
         _st = search("^-?[0-9]* (.*)$", _elem.text)
         self.STATION_NAME = _st.group(1).strip()
     except NoSuchElementException:
         print80(self.register_error(_("Station name not found")))
     if self.STATION_NAME is None or self.STATION_NAME == "STATION":
         try:
             _elem = self.browser.driver.find_element_by_xpath(
                 '//*[@id="inner-content"]/div[2]/lib-city-header/div[1]/div/h1'
             )
             self.STATION_NAME = _elem.text[:20].lstrip(", ") + "..."
         except NoSuchElementException:
             self.STATION_NAME = _("UNKNOWN")
     logging.info(self.STATION_NAME)
     print()
     print80(colored(self.STATION_NAME, attrs=["bold"]))
コード例 #9
0
 def send_to_slack(self, _fix_hour):
     _txt = self.result.display_table()
     _title = "{}-{:}".format(self.STATION_NAME,
                              _fix_hour.strftime("%Y%m%d-%H%M")).replace(
                                  " ", "_")
     _comment = "{} ({}m)\n\n".format(self.STATION_NAME, self.ELEVATION)
     try:
         print80(
             _("Sending timetable to Slack channel {}").format(args.slack))
         _response = self.slack.files_upload(
             content=_txt,
             channels=args.slack,
             title=_title,
             filename=_title + ".txt",
             initial_comment=_comment,
         )
         assert _response["ok"]
         print80(
             _("Sending {} to Slack channel {}").format(
                 program.GRAPH_FILENAME, args.slack))
         _response = self.slack.files_upload(
             file=program.GRAPH_FILENAME,
             channels=args.slack,
             title=_title,
             filename=_title + "." + program.GRAPH_FILENAME.split(".")[-1],
             initial_comment=_comment,
         )
         assert _response["ok"]
     except AssertionError:
         print80(self.register_error(_("Sending to Slack failed")))
コード例 #10
0
    def save_lat_lon(self, pos):
        self.cfg.set(self.CS, self.LATITUDE_T, str(pos["latitude"]))
        self.cfg.set(self.CS, self.LONGITUDE_T, str(pos["longitude"]))
        self.save_ini()

        print80(_("Latitude : {:.7f}").format(pos["latitude"]))
        print80(_("Longitude : {:.7f}").format(pos["longitude"]))
        print80(_("Accuracy : {}m").format(pos["accuracy"]))
        logging.info("{:.7f} {:.7f} ±{}m".format(pos["latitude"],
                                                 pos["longitude"],
                                                 pos["accuracy"]))
        print()
コード例 #11
0
    def start_console(self):
        """
        Open OS console for stdout
        :return: 
        """
        system("title {} {} (Python {})".format(self.NAME, self.VERSION,
                                                python_version()))

        print80(
            colored(
                "{} {}".format(self.NAME, self.VERSION),
                attrs=["bold"],
            ))
        print80(self.DESCRIPTION)
        print()

        major, minor, patchlevel = map(int, python_version_tuple())
        if major != 3 or minor < 6:
            print80(
                _("{} works best with Python version 3.6 and above. Please consider updating."
                  ).format(self.NAME))
            print()
コード例 #12
0
    def __init__(self, fullname: str, version: str, description: str,
                 shortname: str):
        """
        :param fullname: program name
        :param version: program version
        :param description: short description
        :param shortname: short name, mainly for logs
        """

        self.NAME = fullname
        self.SHORTNAME = shortname
        self.VERSION = version
        self.DESCRIPTION = description

        self.STATION_NAME = None
        self.ELEVATION = None
        self.P_INITIAL = None

        self.start_console()

        # starts logging
        self.LOG_FILENAME = self.SHORTNAME + ".log"
        logging.basicConfig(
            filename=self.LOG_FILENAME,
            level=logging.INFO,
            filemode="w",
            format="%(asctime)s %(levelname)s : %(message)s",
            datefmt="%Y-%m-%d %H:%M",
        )

        self.result = PredictionTable()
        self.forecast = Forecast()
        self.slack = slack.WebClient(token=environ["SLACK_API_TOKEN"])

        # reads configuration file and recreates missing values
        self.CONFIG_FILENAME = "config.ini"
        self.CS = "USER SETTINGS"
        self.cfg = ConfigParser()
        self.cfg.read(self.CONFIG_FILENAME)

        if self.CS not in self.cfg.sections():
            print80(_("Regenerating {}").format(self.CONFIG_FILENAME))
            print()
            self.cfg.add_section(self.CS)

        self.TIMEOUT_T = "short timeout"
        self.TIMEOUT = int(self.cfg.get(self.CS, self.TIMEOUT_T, fallback="5"))

        self.TIMEOUT_LONG_T = "long timeout"
        self.TIMEOUT_LONG = int(
            self.cfg.get(self.CS, self.TIMEOUT_LONG_T, fallback="10"))

        self.GEOLOCATION_ALWAYS_ON_T = "geolocation always on"
        self.GEOLOCATION_ALWAYS_ON = bool(
            int(
                self.cfg.get(self.CS,
                             self.GEOLOCATION_ALWAYS_ON_T,
                             fallback="0")))

        self.WAIT_FOR_KEY_T = "press any key"
        self.WAIT_FOR_KEY = bool(
            int(self.cfg.get(self.CS, self.WAIT_FOR_KEY_T, fallback="1")))
        self.PAUSE = (not args.no_key) and self.WAIT_FOR_KEY

        self.OVERRIDE_URL_T = "override url"
        self.OVERRIDE_URL_ = self.cfg.get(self.CS,
                                          self.OVERRIDE_URL_T,
                                          fallback="")
        if args.override_url is not None:
            self.OVERRIDE_URL = args.override_url
        else:
            self.OVERRIDE_URL = self.OVERRIDE_URL_

        self.OVERRIDE_URL_EXISTS = self.OVERRIDE_URL is not None and self.OVERRIDE_URL.startswith(
            "https://www.wunderground.com/hourly/")

        self.ANY_HTTPS_PAGE_T = "https page"
        self.ANY_HTTPS_PAGE = self.cfg.get(self.CS,
                                           self.ANY_HTTPS_PAGE_T,
                                           fallback="https://blank.org")

        self.GEOLOCATED_URL_T = "wunderground hourly url"
        self.GEOLOCATED_URL = self.cfg.get(
            self.CS,
            self.GEOLOCATED_URL_T,
            fallback="https://www.wunderground.com/hourly/ca/location/",
        )

        self.GRAPH_FILENAME_T = "autosave png-pdf-eps filename"
        self.GRAPH_FILENAME = self.cfg.get(self.CS,
                                           self.GRAPH_FILENAME_T,
                                           fallback="graph.png")

        self.GRAPH_DPI_T = "autosave dpi"
        self.GRAPH_DPI = int(
            self.cfg.get(self.CS, self.GRAPH_DPI_T, fallback="600"))

        self.GRAPH_ORIENTATION_T = "autosave orientation"
        self.GRAPH_ORIENTATION = self.cfg.get(self.CS,
                                              self.GRAPH_ORIENTATION_T,
                                              fallback="landscape")

        self.GRAPH_PAPERTYPE_T = "autosave papertype"
        self.GRAPH_PAPERTYPE = self.cfg.get(self.CS,
                                            self.GRAPH_PAPERTYPE_T,
                                            fallback="letter")

        self.VERBOSE_T = "verbose"
        self.VERBOSE_ = bool(
            int(self.cfg.get(self.CS, self.VERBOSE_T, fallback="0")))
        self.VERBOSE = self.VERBOSE_ or args.verbose

        self.SHOW_X_HOURS_T = "display x hours"
        self.SHOW_X_HOURS = max(
            int(self.cfg.get(self.CS, self.SHOW_X_HOURS_T, fallback="6")), 1)

        self.MIN_HOURS_T = "minimum hours"
        self.MIN_HOURS = max(
            int(self.cfg.get(self.CS, self.MIN_HOURS_T, fallback="8")),
            self.SHOW_X_HOURS,
        )

        self.save_ini()  # save immediately to renew all required values

        # optional values that can be missing
        self.LATITUDE_T = "latitude"
        self.LONGITUDE_T = "longitude"
        self.LATITUDE = None
        self.LONGITUDE = None

        if args.latitude is not None:
            self.LATITUDE = args.latitude
        elif self.cfg.has_option(self.CS, self.LATITUDE_T):
            self.LATITUDE = float(self.cfg.get(self.CS, self.LATITUDE_T))

        if args.longitude is not None:
            self.LONGITUDE = args.longitude
        elif self.cfg.has_option(self.CS, self.LONGITUDE_T):
            self.LONGITUDE = float(self.cfg.get(self.CS, self.LONGITUDE_T))

        self.MISSING_LATLONG = self.LATITUDE is None or self.LONGITUDE is None

        self.browser = ChromeBrowser()
コード例 #13
0
    first_day = datetime.today()
    same_day = 1
    if datetime.now(
    ).hour == 23:  # https://github.com/Wlodarski/DR-Altimeter/issues/6
        first_day += timedelta(days=1)
        same_day = 0
    last_day = first_day + timedelta(hours=program.MIN_HOURS)
    nth_days = range(0, same_day + nb_date_changes(first_day, last_day))
    dates = [first_day.date() + timedelta(days=day) for day in nth_days]

    first_page = True
    for d in dates:
        date_str = d.strftime("%Y-%m-%d")
        url = hourly_forecast_url + "/date/" + date_str
        if program.VERBOSE:
            print80(url)
        if first_page:
            program.browser.go_to(webpage=url, hidden=True)
            program.check_page(
                title="Hourly Weather Forecast | Weather Underground")
            program.wait_until_page_is_loaded()
            program.switch_to_metric()
            first_page = False
        else:
            program.click_next()

        if program.STATION_NAME is None:
            program.get_station_name()
        if program.ELEVATION is None:
            program.get_station_elevation()
        if program.P_INITIAL is None: