def from_string(cls, string): """Parses the coords in Degree Minutes format. Expecting: S 36 51.918 E 174 46.725 or N 6 52.861 W174 43.327 Spaces do not matter. Neither does having the degree symbol. Returns a geopy.Point instance.""" # Make it uppercase for consistency coords = string.upper().replace("N", " ").replace("S", " ") \ .replace("E", " ").replace("W", " ").replace("+", " ") try: m = re.match(r"\s*(-?\s*\d+)\D+(\d+[\.,]\d+)\D?\s*(-?\s*\d+)\D+(\d+[\.,]\d+)", coords) latDeg, latMin, lonDeg, lonMin = [float(part.replace(" ", "").replace(",", ".")) for part in m.groups()] if "S" in string: latDeg *= -1 if "W" in string: lonDeg *= -1 return Point(Util.to_decimal(latDeg, latMin), Util.to_decimal(lonDeg, lonMin)) except AttributeError: pass # fallback try: return super(Point, cls).from_string(string) except ValueError as e: # wrap possible error to pycaching.errors.ValueError raise errors.ValueError() from e
def _search_parse_cache(self, root): """Returns a Cache object parsed from BeautifulSoup Tag.""" assert isinstance(root, bs4.Tag) # parse raw data favorites = root.find("span", "favorite-rank") typeLink, nameLink = root.find_all("a", "lnk") pm_only = root.find("img", title="Premium Member Only Cache") is not None direction, info, D_T, placed, last_found = root.find_all( "span", "small") found = root.find("img", title="Found It!") is not None size = root.find("td", "AlignCenter").find("img") author, wp, area = [t.strip() for t in info.text.split("|")] # create cache object c = Cache(wp, self) # prettify data c.cache_type = typeLink.find("img").get("alt") c.name = nameLink.span.text.strip() c.found = found c.state = "Strike" not in nameLink.get("class") c.size = " ".join(size.get("alt").split()[1:]) c.difficulty, c.terrain = list(map(float, D_T.text.split("/"))) c.hidden = Util.parse_date(placed.text) c.author = author[3:] # delete "by " c.favorites = int(favorites.text) c.pm_only = pm_only logging.debug("Cache parsed: %s", c) return c
def _search_parse_cache(self, root): """Returns a Cache object parsed from BeautifulSoup Tag.""" assert isinstance(root, bs4.Tag) # parse raw data favorites = root.find("span", "favorite-rank") typeLink, nameLink = root.find_all("a", "lnk") pm_only = root.find("img", title="Premium Member Only Cache") is not None direction, info, D_T, placed, last_found = root.find_all("span", "small") found = root.find("img", title="Found It!") is not None size = root.find("td", "AlignCenter").find("img") author, wp, area = [t.strip() for t in info.text.split("|")] # create cache object c = Cache(wp, self) # prettify data c.cache_type = typeLink.find("img").get("alt") c.name = nameLink.span.text.strip() c.found = found c.state = "Strike" not in nameLink.get("class") c.size = " ".join(size.get("alt").split()[1:]) c.difficulty, c.terrain = list(map(float, D_T.text.split("/"))) c.hidden = Util.parse_date(placed.text) c.author = author[3:] # delete "by " c.favorites = int(favorites.text) c.pm_only = pm_only logging.debug("Cache parsed: %s", c) return c
def hidden(self, hidden): if type(hidden) is str: hidden = Util.parse_date(hidden) elif type(hidden) is not datetime.date: raise ValueError( "Passed object is not datetime.date instance nor string containing a date." ) self._hidden = hidden
def from_string(cls, string): """Parses the coords in Degree Minutes format. Expecting: S 36 51.918 E 174 46.725 or N 6 52.861 W174 43.327 Spaces do not matter. Neither does having the degree symbol. Returns a geopy.Point instance.""" # Make it uppercase for consistency coords = string.upper().replace("N", " ").replace("S", " ") \ .replace("E", " ").replace("W", " ").replace("+", " ") try: m = re.match( r"\s*(-?\s*\d+)\D+(\d+[\.,]\d+)\D?\s*(-?\s*\d+)\D+(\d+[\.,]\d+)", coords) latDeg, latMin, lonDeg, lonMin = [ float(part.replace(" ", "").replace(",", ".")) for part in m.groups() ] if "S" in string: latDeg *= -1 if "W" in string: lonDeg *= -1 return Point(Util.to_decimal(latDeg, latMin), Util.to_decimal(lonDeg, lonMin)) except AttributeError: pass # fallback try: return super(Point, cls).from_string(string) except ValueError as e: # wrap possible error to pycaching.errors.ValueError raise errors.ValueError() from e
def search(self, point, limit=0): """Returns a generator object of caches around some point.""" assert isinstance(point, Point) assert type(limit) is int logging.info("Searching at %s...", point) start_index = 0 while True: # get one page page = self._search_get_page(point, start_index) if not page: # result is empty - no more caches raise StopIteration() # parse caches in result for start_index, row in enumerate(BeautifulSoup(page).find_all("tr"), start_index): if limit > 0 and start_index == limit: raise StopIteration() # parse raw data cache_details = row.find("span", "cache-details").text.split("|") wp = cache_details[1].strip() # create and fill cache object c = Cache(wp, self) c.cache_type = cache_details[0].strip() c.name = row.find("span", "cache-name").text c.found = row.find("img", title="Found It!") is not None c.favorites = int(row.find(attrs={"data-column": "FavoritePoint"}).text) c.state = not (row.get("class") and "disabled" in row.get("class")) c.pm_only = row.find("td", "pm-upsell") is not None if c.pm_only: # PM only caches doesn't have other attributes filled in yield c continue c.size = row.find(attrs={"data-column": "ContainerSize"}).text c.difficulty = float(row.find(attrs={"data-column": "Difficulty"}).text) c.terrain = float(row.find(attrs={"data-column": "Terrain"}).text) c.hidden = Util.parse_date(row.find(attrs={"data-column": "PlaceDate"}).text) c.author = row.find("span", "owner").text[3:] # delete "by " logging.debug("Cache parsed: %s", c) yield c start_index += 1
def load_cache_quick(self, wp, destination=None): """Loads details from map server. Loads just basic cache details, but very quickly.""" assert type(wp) is str and wp.startswith("GC") logging.info("Loading quick details about %s...", wp) # assemble request params = urlencode({"i": wp}) url = self._urls["map"] + "?" + params try: res = self._browser.get(url).json() except requests.exceptions.ConnectionError as e: raise Error("Cannot load quick cache details page.") from e if res["status"] == "failed" or len(res["data"]) != 1: raise LoadError("Waypoint '{}' cannot be loaded: {}".format( wp, res["msg"])) data = res["data"][0] # create cache object c = destination or Cache(wp, self) assert isinstance(c, Cache) # prettify data c.name = data["name"] c.cache_type = data["type"]["text"] c.state = data["available"] c.size = data["container"]["text"] c.difficulty = data["difficulty"]["text"] c.terrain = data["terrain"]["text"] c.hidden = Util.parse_date(data["hidden"]) c.author = data["owner"]["text"] c.favorites = int(data["fp"]) c.pm_only = data["subrOnly"] logging.debug("Cache loaded: %r", c) return c
def load_cache_quick(self, wp, destination=None): """Loads details from map server. Loads just basic cache details, but very quickly.""" assert type(wp) is str and wp.startswith("GC") logging.info("Loading quick details about %s...", wp) # assemble request params = urlencode({"i": wp}) url = self._urls["map"] + "?" + params try: res = self._browser.get(url).json() except requests.exceptions.ConnectionError as e: raise Error("Cannot load quick cache details page.") from e if res["status"] == "failed" or len(res["data"]) != 1: raise LoadError("Waypoint '{}' cannot be loaded: {}".format(wp, res["msg"])) data = res["data"][0] # create cache object c = destination or Cache(wp, self) assert isinstance(c, Cache) # prettify data c.name = data["name"] c.cache_type = data["type"]["text"] c.state = data["available"] c.size = data["container"]["text"] c.difficulty = data["difficulty"]["text"] c.terrain = data["terrain"]["text"] c.hidden = Util.parse_date(data["hidden"]) c.author = data["owner"]["text"] c.favorites = int(data["fp"]) c.pm_only = data["subrOnly"] logging.debug("Cache loaded: %r", c) return c
def load_cache(self, wp, destination=None): """Loads details from cache page. Loads all cache details and return fully populated cache object.""" assert type(wp) is str and wp.startswith("GC") logging.info("Loading details about %s...", wp) # assemble request params = urlencode({"wp": wp}) url = self._urls["cache_details"] + "?" + params try: root = self._browser.get(url).soup except requests.exceptions.ConnectionError as e: raise Error("Cannot load cache details page.") from e cache_details = root.find(id="cacheDetails") # check for PM only caches if using free account if cache_details is None: if root.select(".PMOWarning") is not None: raise PMOnlyException("Premium Members only.") # parse raw data name = cache_details.find("h2") cache_type = cache_details.find("img").get("alt") author = cache_details("a")[1] hidden = cache_details.find("div", "minorCacheDetails").find_all("div")[1] location = root.find(id="uxLatLon") state = root.find("ul", "OldWarning") found = root.find("div", "FoundStatus") D_T = root.find("div", "CacheStarLabels").find_all("img") size = root.find("div", "CacheSize").find("img") attributes_raw = root.find_all( "div", "CacheDetailNavigationWidget")[0].find_all("img") user_content = root.find_all("div", "UserSuppliedContent") hint = root.find(id="div_hint") favorites = root.find("span", "favorite-value") # create cache object c = destination or Cache(wp, self) assert isinstance(c, Cache) # prettify data c.name = name.text c.cache_type = cache_type c.author = author.text c.hidden = Util.parse_date(hidden.text.split()[2]) c.location = Point.from_string(location.text) c.state = state is None c.found = found and "Found It!" in found.text or False c.difficulty, c.terrain = [float(_.get("alt").split()[0]) for _ in D_T] c.size = " ".join(size.get("alt").split()[1:]) attributes_raw = [ _.get("src").split('/')[-1].rsplit("-", 1) for _ in attributes_raw ] c.attributes = { attribute_name: appendix.startswith("yes") for attribute_name, appendix in attributes_raw if not appendix.startswith("blank") } c.summary = user_content[0].text c.description = str(user_content[1]) c.hint = Util.rot13(hint.text.strip()) c.favorites = int(favorites.text) logging.debug("Cache loaded: %r", c) return c
def hidden(self, hidden): if type(hidden) is str: hidden = Util.parse_date(hidden) elif type(hidden) is not datetime.date: raise ValueError("Passed object is not datetime.date instance nor string containing a date.") self._hidden = hidden
def load_cache(self, wp, destination=None): """Loads details from cache page. Loads all cache details and return fully populated cache object.""" assert type(wp) is str and wp.startswith("GC") logging.info("Loading details about %s...", wp) # assemble request params = urlencode({"wp": wp}) url = self._urls["cache_details"] + "?" + params try: root = self._browser.get(url).soup except requests.exceptions.ConnectionError as e: raise Error("Cannot load cache details page.") from e cache_details = root.find(id="cacheDetails") # check for PM only caches if using free account if cache_details is None: if root.select(".PMOWarning") is not None: raise PMOnlyException("Premium Members only.") # parse raw data name = cache_details.find("h2") cache_type = cache_details.find("img").get("alt") author = cache_details("a")[1] hidden = cache_details.find("div", "minorCacheDetails").find_all("div")[1] location = root.find(id="uxLatLon") state = root.find("ul", "OldWarning") found = root.find("div", "FoundStatus") D_T = root.find("div", "CacheStarLabels").find_all("img") size = root.find("div", "CacheSize").find("img") attributes_raw = root.find_all("div", "CacheDetailNavigationWidget")[0].find_all("img") user_content = root.find_all("div", "UserSuppliedContent") hint = root.find(id="div_hint") favorites = root.find("span", "favorite-value") # create cache object c = destination or Cache(wp, self) assert isinstance(c, Cache) # prettify data c.name = name.text c.cache_type = cache_type c.author = author.text c.hidden = Util.parse_date(hidden.text.split()[2]) c.location = Point.from_string(location.text) c.state = state is None c.found = found and "Found It!" in found.text or False c.difficulty, c.terrain = [float(_.get("alt").split()[0]) for _ in D_T] c.size = " ".join(size.get("alt").split()[1:]) attributes_raw = [_.get("src").split('/')[-1].rsplit("-", 1) for _ in attributes_raw] c.attributes = {attribute_name: appendix.startswith("yes") for attribute_name, appendix in attributes_raw if not appendix.startswith("blank")} c.summary = user_content[0].text c.description = str(user_content[1]) c.hint = Util.rot13(hint.text.strip()) c.favorites = int(favorites.text) logging.debug("Cache loaded: %r", c) return c
def load_cache_by_url(self, url, destination=None): try: root = self._browser.get(url).soup except requests.exceptions.ConnectionError as e: raise Error("Cannot load cache details page.") from e cache_details = root.find(id="cacheDetails") # check for PM only caches if using free account if cache_details is None: if root.select(".PMOWarning") is not None: raise PMOnlyException("Premium Members only.") # parse raw data wp = root.title.string.split(' ')[0] name = cache_details.find("h2") cache_type = cache_details.find("img").get("src") author = cache_details("a")[1] hidden = cache_details.find("div", "minorCacheDetails").find_all("div")[1] location = root.find(id="uxLatLon") state = root.find("ul", "OldWarning") found = root.find("div", "FoundStatus") D_T = root.find("div", "CacheStarLabels").find_all("img") size = root.find("div", "CacheSize").find("img") attributes_raw = root.find_all( "div", "CacheDetailNavigationWidget")[0].find_all("img") user_content = root.find_all("div", "UserSuppliedContent") hint = root.find(id="div_hint") favorites = root.find("span", "favorite-value") # check for trackables inventory_raw = root.find_all("div", "CacheDetailNavigationWidget") inventory_links = inventory_raw[1].find_all("a") if len(inventory_links) >= 3: trackable_page = self._urls['trackable_base'] + inventory_links[ -3].get("href") else: trackable_page = None # create cache object c = destination or Cache(wp, self) assert isinstance(c, Cache) # prettify data c.name = name.text c.cache_type = Cache.get_cache_type_by_img(cache_type) c.author = author.text c.hidden = Util.parse_date(hidden.text.split(":")[-1]) c.location = Point.from_string(location.text) c.state = state is None c.found = found and "Found It!" in found.text or False c.difficulty, c.terrain = [float(_.get("alt").split()[0]) for _ in D_T] c.size = size.get("src").split("/")[-1].rsplit( ".", 1)[0] # filename of img[src] attributes_raw = [ _.get("src").split('/')[-1].rsplit("-", 1) for _ in attributes_raw ] c.attributes = { attribute_name: appendix.startswith("yes") for attribute_name, appendix in attributes_raw if not appendix.startswith("blank") } c.summary = user_content[0].text c.description = str(user_content[1]) c.hint = Util.rot13(hint.text.strip()) if favorites is None: c.favorites = 0 else: c.favorites = int(favorites.text) if trackable_page is not None: c.trackables = self.load_trackable_list(trackable_page) else: c.trackables = [] logging.debug("Cache loaded: %r", c) return c
def search(self, point, limit=0): """Returns a generator object of caches around some point.""" assert isinstance(point, Point) assert type(limit) is int logging.info("Searching at %s...", point) start_index = 0 while True: # get one page page = self._search_get_page(point, start_index) if not page: # result is empty - no more caches raise StopIteration() # parse caches in result for start_index, row in enumerate( BeautifulSoup(page).find_all("tr"), start_index): if limit > 0 and start_index == limit: raise StopIteration() # parse raw data cache_details = row.find("span", "cache-details").text.split("|") wp = cache_details[1].strip() # create and fill cache object c = Cache(wp, self) c.cache_type = cache_details[0].strip() c.name = row.find("span", "cache-name").text c.found = row.find("img", title="Found It!") is not None c.favorites = int( row.find(attrs={ "data-column": "FavoritePoint" }).text) c.state = not (row.get("class") and "disabled" in row.get("class")) c.pm_only = row.find("td", "pm-upsell") is not None if c.pm_only: # PM only caches doesn't have other attributes filled in yield c continue c.size = row.find(attrs={"data-column": "ContainerSize"}).text c.difficulty = float( row.find(attrs={ "data-column": "Difficulty" }).text) c.terrain = float( row.find(attrs={ "data-column": "Terrain" }).text) c.hidden = Util.parse_date( row.find(attrs={ "data-column": "PlaceDate" }).text) c.author = row.find("span", "owner").text[3:] # delete "by " logging.debug("Cache parsed: %s", c) yield c start_index += 1
def load_cache_by_url(self, url, destination=None): try: root = self._browser.get(url).soup except requests.exceptions.ConnectionError as e: raise Error("Cannot load cache details page.") from e cache_details = root.find(id="cacheDetails") # check for PM only caches if using free account if cache_details is None: if root.select(".PMOWarning") is not None: raise PMOnlyException("Premium Members only.") # parse raw data wp = root.title.string.split(' ')[0] name = cache_details.find("h2") cache_type = cache_details.find("img").get("src") author = cache_details("a")[1] hidden = cache_details.find("div", "minorCacheDetails").find_all("div")[1] location = root.find(id="uxLatLon") state = root.find("ul", "OldWarning") found = root.find("div", "FoundStatus") D_T = root.find("div", "CacheStarLabels").find_all("img") size = root.find("div", "CacheSize").find("img") attributes_raw = root.find_all("div", "CacheDetailNavigationWidget")[0].find_all("img") user_content = root.find_all("div", "UserSuppliedContent") hint = root.find(id="div_hint") favorites = root.find("span", "favorite-value") # check for trackables inventory_raw = root.find_all("div", "CacheDetailNavigationWidget") inventory_links = inventory_raw[1].find_all("a") if len(inventory_links) >= 3: trackable_page = self._urls['trackable_base'] + inventory_links[-3].get("href") else: trackable_page = None # create cache object c = destination or Cache(wp, self) assert isinstance(c, Cache) # prettify data c.name = name.text c.cache_type = Cache.get_cache_type_by_img(cache_type) c.author = author.text c.hidden = Util.parse_date(hidden.text.split(":")[-1]) c.location = Point.from_string(location.text) c.state = state is None c.found = found and "Found It!" in found.text or False c.difficulty, c.terrain = [float(_.get("alt").split()[0]) for _ in D_T] c.size = size.get("src").split("/")[-1].rsplit(".", 1)[0] # filename of img[src] attributes_raw = [_.get("src").split('/')[-1].rsplit("-", 1) for _ in attributes_raw] c.attributes = {attribute_name: appendix.startswith("yes") for attribute_name, appendix in attributes_raw if not appendix.startswith("blank")} c.summary = user_content[0].text c.description = str(user_content[1]) c.hint = Util.rot13(hint.text.strip()) if favorites is None: c.favorites = 0 else: c.favorites = int(favorites.text) if trackable_page is not None: c.trackables = self.load_trackable_list(trackable_page) else: c.trackables = [] logging.debug("Cache loaded: %r", c) return c