def test_contains(self): inside_points = [Point(*i) for i in [(10.0, 20.0), (30.0, -5.0), (18.0, 15.0), (29.0, -1), (10.0, -3)]] outside_points = [Point(*i) for i in [(-10.0, -170.0), (-70.0, 0.0), (0.0, 40), (20.0, -10.0), (50.0, 0.0)]] for p in inside_points: self.assertTrue(p in self.rect) for p in outside_points: self.assertFalse(p in self.rect)
def setUp(self): self.gc = Geocaching() self.c = Cache(self.gc, "GC12345", name="Testing", type=Type.traditional, location=Point(), state=True, found=False, size=Size.micro, difficulty=1.5, terrain=5, author="human", hidden=date(2000, 1, 1), attributes={ "onehour": True, "kids": False, "available": True }, summary="text", description="long text", hint="rot13", favorites=0, pm_only=False, original_location=Point(), waypoints={}, guid="53d34c4d-12b5-4771-86d3-89318f71efb1") self.c._log_page_url = "/seek/log.aspx?ID=1234567&lcn=1"
def test_format_gc(self): """Test Geocaching point formatting.""" self.assertEqual( Point(49.73012, 13.40102).format_gc(), "N 49° 43.807, E 13° 24.061") self.assertEqual( Point(-49.73012, -13.40102).format_gc(), "S 49° 43.807, W 13° 24.061")
def test_from_tile(self): """Test coordinate creation from tile""" p = Point.from_tile(*make_tile(8800, 5574, 14, 0, 0, 256)) p_pos = Point(49.752879934150215, 13.359375, 0.0) p2 = Point.from_tile(*make_tile(8801, 5575, 14, 0, 0, 256)) p_half = Point.from_tile(*make_tile(8800, 5574, 14, 1, 1, 2)) # Check creation for att in ['latitude', 'longitude']: with self.subTest("assumed location: {}".format(att)): self.assertAlmostEqual(getattr(p, att), getattr(p_pos, att)) with self.subTest("fractional tiles: y-axis addition"): self.assertEqual( Point.from_tile(*make_tile(8800, 5574, 14, 0, 32, 32)), Point.from_tile(*make_tile(x=8800, y=5575, z=14))) with self.subTest("fractional tiles: x-axis addition"): self.assertAlmostEqual( Point.from_tile(*make_tile(8800, 5574, 14, 32, 0, 32)), Point.from_tile(*make_tile(x=8801, y=5574, z=14))) with self.subTest("fractional tiles: addition on both axes"): self.assertEqual( Point.from_tile(*make_tile(8800, 5574, 14, 32, 32, 32)), p2) with self.subTest("y increases -> latitude decreases"): self.assertGreater(p.latitude, p_half.latitude) with self.subTest("x increases -> latitude increases"): self.assertLess(p.longitude, p_half.longitude)
def test_from_tile(self): """Test coordinate creation from tile""" p = Point.from_tile(*make_tile(8800, 5574, 14, 0, 0, 256)) p_pos = Point(49.752879934150215, 13.359375, 0.0) p2 = Point.from_tile(*make_tile(8801, 5575, 14, 0, 0, 256)) p_half = Point.from_tile(*make_tile(8800, 5574, 14, 1, 1, 2)) # Check creation for att in ['latitude', 'longitude']: with self.subTest("assumed location: {}".format(att)): self.assertAlmostEqual(getattr(p, att), getattr(p_pos, att)) with self.subTest("fractional tiles: y-axis addition"): self.assertEqual(Point.from_tile(*make_tile(8800, 5574, 14, 0, 32, 32)), Point.from_tile(*make_tile(x=8800, y=5575, z=14))) with self.subTest("fractional tiles: x-axis addition"): self.assertAlmostEqual(Point.from_tile(*make_tile(8800, 5574, 14, 32, 0, 32)), Point.from_tile(*make_tile(x=8801, y=5574, z=14))) with self.subTest("fractional tiles: addition on both axes"): self.assertEqual(Point.from_tile(*make_tile(8800, 5574, 14, 32, 32, 32)), p2) with self.subTest("y increases -> latitude decreases"): self.assertGreater(p.latitude, p_half.latitude) with self.subTest("x increases -> latitude increases"): self.assertLess(p.longitude, p_half.longitude)
def test_location(self): self.assertEqual(self.c.location, Point()) with self.subTest("automatic str conversion"): self.c.location = "S 36 51.918 E 174 46.725" self.assertEqual(self.c.location, Point.from_string("S 36 51.918 E 174 46.725")) with self.subTest("filter invalid string"): with self.assertRaises(PycachingValueError): self.c.location = "somewhere" with self.subTest("filter invalid types"): with self.assertRaises(PycachingValueError): self.c.location = None
def test_to_tile(self): t = make_tile(8800, 5574, 14)[0] point_in_t = Point(49.75, 13.36) with self.subTest("from tile and back"): self.assertEqual(Point.from_tile(t).to_tile(None, t.z), t) with self.subTest("random point"): self.assertEqual(point_in_t.to_tile(None, 14), t) with self.subTest("increase in latitude: decrease in y value"): self.assertLess(Point(50., 13.36).to_tile(None, 14).y, t.y) with self.subTest("increase in longitude: increase in x value"): self.assertGreater(Point(49.75, 14.).to_tile(None, 14).x, t.x)
def _from_api_record(cls, geocaching, record): """Create a cache instance from a JSON record returned by API.""" cache = Cache( geocaching, wp=record['code'], name=record['name'], type=Type.from_number(record['geocacheType']), state=Status(record['cacheStatus']) == Status.enabled, found=record['userFound'], size=Size.from_number(record['containerType']), difficulty=record['difficulty'], terrain=record['terrain'], author=record['owner']['username'], hidden=record['placedDate'].split('T')[0], favorites=record['favoritePoints'], pm_only=record['premiumOnly'], # Not consumed attributes: # detailsUrl # hasGeotour # hasLogDraft # id # lastFoundDate # owner.code # userDidNotFind ) # NOTE: Basic Members have no access to postedCoordinates of Premium-only caches if 'postedCoordinates' in record: cache.location = Point(record['postedCoordinates']['latitude'], record['postedCoordinates']['longitude']) return cache
def location(self, location): if _type(location) is str: location = Point.from_string(location) elif _type(location) is not Point: raise errors.ValueError( "Passed object is not Point instance nor string containing coordinates.") self._location = location
def original_location(self, original_location): if _type(original_location) is str: original_location = Point.from_string(original_location) elif _type(original_location) is not Point and original_location is not None: raise errors.ValueError( "Passed object is not Point instance nor string containing coordinates.") self._original_location = original_location
def location(self, location): if isinstance(location, str): location = Point.from_string(location) elif not isinstance(location, Point): raise errors.ValueError( "Passed object is not Point instance nor string containing coordinates.") self._location = location
def test_load_by_guid(self, mock_load_quick, mock_load): with self.subTest("normal"): cache = Cache(self.gc, "GC2WXPN", guid="5f45114d-1d79-4fdb-93ae-8f49f1d27188") with self.recorder.use_cassette('cache_guidload_normal'): cache.load_by_guid() self.assertEqual(cache.name, "Der Schatz vom Luftschloss") self.assertEqual(cache.location, Point("N 49° 57.895' E 008° 12.988'")) self.assertEqual(cache.type, Type.mystery) self.assertEqual(cache.size, Size.large) self.assertEqual(cache.difficulty, 2.5) self.assertEqual(cache.terrain, 1.5) self.assertEqual(cache.author, "engelmz & Punxsutawney Phil") self.assertEqual(cache.hidden, parse_date("23/06/2011")) self.assertDictEqual( cache.attributes, { "bicycles": True, "available": True, "parking": True, "onehour": True, "kids": True, "s-tool": True, }) self.assertEqual(cache.summary, "Gibt es das Luftschloss wirklich?") self.assertIn("Seit dem 16.", cache.description) self.assertEqual(cache.hint, "Das ist nicht nötig") self.assertGreater(cache.favorites, 350) self.assertEqual(len(cache.waypoints), 2) self.assertDictEqual( cache.log_counts, { LogType.found_it: 800, LogType.note: 35, LogType.archive: 1, LogType.needs_archive: 1, LogType.temp_disable_listing: 5, LogType.enable_listing: 4, LogType.publish_listing: 1, LogType.needs_maintenance: 5, LogType.owner_maintenance: 3, LogType.post_reviewer_note: 2, }) with self.subTest("PM-only"): cache = Cache(self.gc, "GC6MKEF", guid="53d34c4d-12b5-4771-86d3-89318f71efb1") with self.recorder.use_cassette('cache_guidload_PMO'): with self.assertRaises(PMOnlyException): cache.load_by_guid() with self.subTest("calls load_quick if no guid"): cache = Cache(self.gc, "GC2WXPN") with self.recorder.use_cassette('cache_guidload_fallback'): with self.assertRaises(Exception): cache.load_by_guid( ) # Raises error since we mocked load_quick() self.assertTrue(mock_load_quick.called)
def from_html(cls, soup, table_id): """Return a dictionary of all waypoints found in the page representation :param bs4.BeautifulSoup soup: parsed html document containing the waypoints table :param str table_id: html id of the waypoints table """ waypoints_dict = {} waypoints_table = soup.find('table', id=table_id) if waypoints_table: waypoints_table = waypoints_table.find_all("tr") for r1, r2 in zip(waypoints_table[1::2], waypoints_table[2::2]): columns = r1.find_all("td") + r2.find_all("td") identifier = columns[3].text.strip() type = columns[1].find("img").get("title") location_string = columns[5].text.strip() try: loc = Point(location_string) except ValueError: loc = None logging.debug( "No valid location format in waypoint {}: {}".format( identifier, location_string)) note = columns[8].text.strip() waypoints_dict[identifier] = cls(identifier, type, loc, note) return waypoints_dict
def _from_print_page(cls, geocaching, guid, soup): """Create a cache instance from a souped print-page and a GUID.""" if soup.find("p", "Warning") is not None: raise errors.PMOnlyException() cache_info = dict() cache_info["guid"] = guid cache_info["wp"] = soup.find( class_="HalfRight").find("h1").text.strip() content = soup.find(id="Content") cache_info["name"] = content.find("h2").text.strip() cache_info["type"] = Type.from_filename( content.h2.img["src"].split("/")[-1].partition(".")[0]) cache_info["author"] = content.find( class_="Meta").text.partition(":")[2].strip() diff_terr = content.find(class_="DiffTerr").find_all("img") assert len(diff_terr) == 2 cache_info["difficulty"] = float(diff_terr[0]["alt"].split()[0]) cache_info["terrain"] = float(diff_terr[1]["alt"].split()[0]) cache_info["size"] = Size.from_string( content.find( class_="Third AlignCenter").p.img["alt"].partition(":")[2]) fav_text = content.find(class_="Third AlignRight").p.contents[2] try: cache_info["favorites"] = int(fav_text) except ValueError: # element not present when 0 favorites cache_info["favorites"] = 0 cache_info["hidden"] = parse_date( content.find(class_="HalfRight AlignRight").p.text.strip(). partition(":")[2].strip()) cache_info["location"] = Point.from_string( content.find(class_="LatLong").text.strip()) cache_info["state"] = None # not on the page attributes = [ img["src"].split("/")[-1].partition(".")[0].rpartition("-") for img in content.find(class_="sortables").find_all("img") if img.get("src") and img["src"].startswith("/images/attributes/") ] cache_info["attributes"] = { attr_name: attr_setting == "yes" for attr_name, _, attr_setting in attributes } if "attribute" in cache_info["attributes"]: # 'blank' attribute del cache_info["attributes"]["attribute"] cache_info["summary"] = content.find( "h2", text="Short Description").find_next("div").text cache_info["description"] = content.find( "h2", text="Long Description").find_next("div").text hint = content.find(id="uxEncryptedHint") cache_info["hint"] = hint.text.strip() if hint else None cache_info["waypoints"] = Waypoint.from_html(content, table_id="Waypoints") cache_info["log_counts"] = Cache._get_log_counts_from_print_page(soup) return Cache(geocaching, **cache_info)
def from_block(cls, block): """Return :class:`.Cache` instance from :class:`.Block`. Used during quick search. The Cache will have only waypoint, name and approximate location filled in. :param .Block block: Source block """ c = cls(block.tile.geocaching, block.cache_wp, name=block.cache_name) c.location = Point.from_block(block) return c
def _from_print_page(cls, geocaching, guid, soup): """Create a cache instance from a souped print-page and a GUID""" if soup.find("p", "Warning") is not None: raise errors.PMOnlyException() cache_info = dict() cache_info['guid'] = guid cache_info['wp'] = soup.find( class_='HalfRight').find('h1').text.strip() content = soup.find(id="Content") cache_info['name'] = content.find("h2").text.strip() cache_info['type'] = Type.from_filename( content.h2.img['src'].split('/')[-1].partition('.')[0]) cache_info['author'] = content.find( class_='Meta').text.partition(':')[2].strip() diff_terr = content.find(class_='DiffTerr').find_all('img') assert len(diff_terr) == 2 cache_info['difficulty'] = float(diff_terr[0]['alt'].split()[0]) cache_info['terrain'] = float(diff_terr[1]['alt'].split()[0]) cache_info['size'] = Size.from_string( content.find( class_='Third AlignCenter').p.img['alt'].partition(':')[2]) fav_text = content.find(class_='Third AlignRight').p.contents[2] try: cache_info['favorites'] = int(fav_text) except ValueError: # element not present when 0 favorites cache_info['favorites'] = 0 cache_info['hidden'] = parse_date( content.find(class_='HalfRight AlignRight').p.text.strip(). partition(':')[2].strip()) cache_info['location'] = Point.from_string( content.find(class_='LatLong').text.strip()) cache_info['state'] = None # not on the page attributes = [ img['src'].split('/')[-1].partition('.')[0].rpartition('-') for img in content.find(class_='sortables').find_all('img') if img.get('src') and img['src'].startswith('/images/attributes/') ] cache_info['attributes'] = { attr_name: attr_setting == 'yes' for attr_name, _, attr_setting in attributes } if 'attribute' in cache_info['attributes']: # 'blank' attribute del cache_info['attributes']['attribute'] cache_info['summary'] = content.find( "h2", text="Short Description").find_next("div").text cache_info['description'] = content.find( "h2", text="Long Description").find_next("div").text hint = content.find(id='uxEncryptedHint') cache_info['hint'] = hint.text.strip() if hint else None cache_info['waypoints'] = Waypoint.from_html(content, table_id="Waypoints") return Cache(geocaching, **cache_info)
def test_from_location(self): ref_point = Point(50.08746, 14.42125) with self.subTest("existing location"): with self.recorder.use_cassette('geo_location_existing'): self.assertLess(great_circle(Point.from_location(self.gc, "Prague"), ref_point).miles, 10) self.assertLess(great_circle(Point.from_location(self.gc, "Praha"), ref_point).miles, 10) self.assertLess(great_circle(Point.from_location(self.gc, "praha"), ref_point).miles, 10) with self.subTest("non-existing location"): with self.recorder.use_cassette('geo_location_nonexisting'): with self.assertRaises(GeocodeError): Point.from_location(self.gc, "qwertzuiop") with self.subTest("empty request"): with self.recorder.use_cassette('geo_location_empty'): with self.assertRaises(GeocodeError): Point.from_location(self.gc, "")
def test_precision(self): with self.subTest("with point coorection"): t1 = make_tile(0, 0, 14)[0] p = Point(49.75, 13.36) self.assertAlmostEqual(t1.precision(p), 6.173474613462484) with self.subTest("precision is larger on greater z values"): t1 = make_tile(0, 0, 13)[0] t2 = make_tile(0, 0, 14)[0] self.assertGreater(t1.precision(), t2.precision()) with self.subTest("precision is larger when tile is divided to smaller pieces"): t1 = make_tile(0, 0, 14)[0] t1.size = 10 t2 = make_tile(0, 0, 14)[0] t2.size = 100 self.assertGreater(t1.precision(), t2.precision())
def test_load_by_guid(self, mock_load_quick, mock_load): with self.subTest("normal"): cache = Cache(self.gc, "GC2WXPN", guid="5f45114d-1d79-4fdb-93ae-8f49f1d27188") cache.load_by_guid() self.assertEqual(cache.name, "Der Schatz vom Luftschloss") self.assertEqual(cache.location, Point("N 49° 57.895' E 008° 12.988'")) self.assertEqual(cache.type, Type.mystery) self.assertEqual(cache.size, Size.large) self.assertEqual(cache.difficulty, 2.5) self.assertEqual(cache.terrain, 1.5) self.assertEqual(cache.author, "engelmz & Punxsutawney Phil") self.assertEqual(cache.hidden, parse_date("23/06/2011")) self.assertDictEqual( cache.attributes, { "bicycles": True, "available": True, "firstaid": True, "parking": True, "onehour": True, "kids": True, "s-tool": True, }) self.assertEqual(cache.summary, "Gibt es das Luftschloss wirklich?") self.assertIn("Seit dem 16.", cache.description) self.assertEqual(cache.hint, "Das ist nicht nötig") self.assertGreater(cache.favorites, 380) self.assertEqual(len(cache.waypoints), 2) with self.subTest("PM-only"): cache = Cache(self.gc, "GC6MKEF", guid="53d34c4d-12b5-4771-86d3-89318f71efb1") with self.assertRaises(PMOnlyException): cache.load_by_guid() with self.subTest("calls load_quick if no guid"): cache = Cache(self.gc, "GC2WXPN") with self.assertRaises(Exception): cache.load_by_guid( ) # Raises error since we mocked load_quick() self.assertTrue(mock_load_quick.called)
def test_from_location(self): gc = Geocaching() gc.login(_username, _password) ref_point = Point(49.74774, 13.37752) with self.subTest("existing location"): self.assertLess(great_circle(Point.from_location(gc, "Pilsen"), ref_point).miles, 10) self.assertLess(great_circle(Point.from_location(gc, "Plzeň"), ref_point).miles, 10) self.assertLess(great_circle(Point.from_location(gc, "plzen"), ref_point).miles, 10) with self.subTest("non-existing location"): with self.assertRaises(GeocodeError): Point.from_location(gc, "qwertzuiop") with self.subTest("empty request"): with self.assertRaises(GeocodeError): Point.from_location(gc, "")
def _from_print_page(cls, geocaching, guid, soup): """Create a cache instance from a souped print-page and a GUID""" if soup.find("p", "Warning") is not None: raise errors.PMOnlyException() cache_info = dict() cache_info['guid'] = guid cache_info['wp'] = soup.find(class_='HalfRight').find('h1').text.strip() content = soup.find(id="Content") cache_info['name'] = content.find("h2").text.strip() cache_info['type'] = Type.from_filename(content.h2.img['src'].split('/')[-1].partition('.')[0]) cache_info['author'] = content.find(class_='Meta').text.partition(':')[2].strip() diff_terr = content.find(class_='DiffTerr').find_all('img') assert len(diff_terr) == 2 cache_info['difficulty'] = float(diff_terr[0]['alt'].split()[0]) cache_info['terrain'] = float(diff_terr[1]['alt'].split()[0]) cache_info['size'] = Size.from_string(content.find(class_='Third AlignCenter').p.img['alt'].partition(':')[2]) fav_text = content.find(class_='Third AlignRight').p.contents[2] try: cache_info['favorites'] = int(fav_text) except ValueError: # element not present when 0 favorites cache_info['favorites'] = 0 cache_info['hidden'] = parse_date( content.find(class_='HalfRight AlignRight').p.text.strip().partition(':')[2].strip()) cache_info['location'] = Point.from_string(content.find(class_='LatLong').text.strip()) cache_info['state'] = None # not on the page attributes = [img['src'].split('/')[-1].partition('.')[0].rpartition('-') for img in content.find(class_='sortables').find_all('img') if img.get('src') and img['src'].startswith('/images/attributes/')] cache_info['attributes'] = {attr_name: attr_setting == 'yes' for attr_name, _, attr_setting in attributes} if 'attribute' in cache_info['attributes']: # 'blank' attribute del cache_info['attributes']['attribute'] cache_info['summary'] = content.find("h2", text="Short Description").find_next("div").text cache_info['description'] = content.find("h2", text="Long Description").find_next("div").text hint = content.find(id='uxEncryptedHint') cache_info['hint'] = hint.text.strip() if hint else None cache_info['waypoints'] = Waypoint.from_html(content, table_id="Waypoints") return Cache(geocaching, **cache_info)
def test_from_location(self): gc = Geocaching() gc.login(_username, _password) ref_point = Point(49.74774, 13.37752) with self.subTest("existing location"): self.assertLess( great_circle(Point.from_location(gc, "Pilsen"), ref_point).miles, 10) self.assertLess( great_circle(Point.from_location(gc, "Plzeň"), ref_point).miles, 10) self.assertLess( great_circle(Point.from_location(gc, "plzen"), ref_point).miles, 10) with self.subTest("non-existing location"): with self.assertRaises(GeocodeError): Point.from_location(gc, "qwertzuiop") with self.subTest("empty request"): with self.assertRaises(GeocodeError): Point.from_location(gc, "")
def load(self): """Load all possible cache details. Use full cache details page. Therefore all possible properties are filled in, but the loading is a bit slow. If you want to load basic details about a PM only cache, the :class:`.PMOnlyException` is still thrown, but avaliable details are filled in. If you know, that the cache you are loading is PM only, please consider using :meth:`load_quick` as it will load the same details, but quicker. .. note:: This method is called automatically when you access a property which isn't yet filled in (so-called "lazy loading"). You don't have to call it explicitly. :raise .PMOnlyException: If cache is PM only and current user is basic member. :raise .LoadError: If cache loading fails (probably because of not existing cache). """ try: # pick url based on what info we have right now if hasattr(self, "url"): root = self.geocaching._request(self.url) elif hasattr(self, "_wp"): root = self.geocaching._request("seek/cache_details.aspx", params={"wp": self._wp}) else: raise errors.LoadError("Cache lacks info for loading") except errors.Error as e: # probably 404 during cache loading - cache not exists raise errors.LoadError("Error in loading cache") from e # check for PM only caches if using free account self.pm_only = root.find("section", "pmo-banner") is not None cache_details = root.find(id="ctl00_divContentMain") if self.pm_only else root.find(id="cacheDetails") # details also avaliable for basic members for PM only caches ----------------------------- if self.pm_only: self.wp = cache_details.find("li", "li__gccode").text.strip() self.name = cache_details.find("h1").text.strip() author = cache_details.find(id="ctl00_ContentBody_uxCacheBy").text self.author = author[len("A cache by "):] # parse cache detail list into a python list details = cache_details.find("ul", "ul__hide-details").text.split("\n") self.difficulty = float(details[2]) self.terrain = float(details[5]) self.size = Size.from_string(details[8]) self.favorites = int(details[11]) else: # parse from <title> - get first word try: self.wp = root.title.string.split(" ")[0] except: raise errors.LoadError self.name = cache_details.find("h2").text self.author = cache_details("a")[1].text size = root.find("div", "CacheSize") D_and_T_img = root.find("div", "CacheStarLabels").find_all("img") size = size.find("img").get("src") # size img src size = size.split("/")[-1].rsplit(".", 1)[0] # filename w/o extension self.size = Size.from_filename(size) self.difficulty, self.terrain = [float(img.get("alt").split()[0]) for img in D_and_T_img] type = cache_details.find("img").get("src") # type img src type = type.split("/")[-1].rsplit(".", 1)[0] # filename w/o extension self.type = Type.from_filename(type) if self.pm_only: raise errors.PMOnlyException() # details not avaliable for basic members for PM only caches ------------------------------ pm_only_warning = root.find("p", "Warning NoBottomSpacing") self.pm_only = pm_only_warning and ("Premium Member Only" in pm_only_warning.text) or False attributes_widget, inventory_widget, *_ = root.find_all("div", "CacheDetailNavigationWidget") hidden = cache_details.find("div", "minorCacheDetails").find_all("div")[1].text self.hidden = parse_date(hidden.split(":")[-1]) self.location = Point.from_string(root.find(id="uxLatLon").text) self.state = root.find("ul", "OldWarning") is None found = root.find("div", "FoundStatus") self.found = found and ("Found It!" or "Attended" in found.text) or False attributes_raw = attributes_widget.find_all("img") attributes_raw = [_.get("src").split("/")[-1].rsplit("-", 1) for _ in attributes_raw] self.attributes = {attribute_name: appendix.startswith("yes") for attribute_name, appendix in attributes_raw if not appendix.startswith("blank")} user_content = root.find_all("div", "UserSuppliedContent") self.summary = user_content[0].text self.description = str(user_content[1]) self.hint = rot13(root.find(id="div_hint").text.strip()) favorites = root.find("span", "favorite-value") self.favorites = 0 if favorites is None else int(favorites.text) self._log_page_url = root.find(id="ctl00_ContentBody_GeoNav_logButton")["href"] js_content = "\n".join(map(lambda i: i.text, root.find_all("script"))) self._logbook_token = re.findall("userToken\\s*=\\s*'([^']+)'", js_content)[0] # find original location if any if "oldLatLng\":" in js_content: old_lat_long = js_content.split("oldLatLng\":")[1].split(']')[0].split('[')[1] self.original_location = Point(old_lat_long) else: self.original_location = None # if there are some trackables if len(inventory_widget.find_all("a")) >= 3: trackable_page_url = inventory_widget.find(id="ctl00_ContentBody_uxTravelBugList_uxViewAllTrackableItems") self._trackable_page_url = trackable_page_url.get("href")[3:] # has "../" on start else: self._trackable_page_url = None logging.debug("Cache loaded: {}".format(self))
def test_from_string(self): with self.subTest("normal"): self.assertEqual(Point.from_string("N 49 45.123 E 013 22.123"), Point(49.75205, 13.36872)) with self.subTest("south and west"): self.assertEqual(Point.from_string("S 49 45.123 W 013 22.123"), Point(-49.75205, -13.36872)) with self.subTest("letter together"): self.assertEqual(Point.from_string("N49 45.123 E013 22.123"), Point(49.75205, 13.36872)) with self.subTest("letter after"): self.assertEqual(Point.from_string("49N 45.123 013E 22.123"), Point(49.75205, 13.36872)) with self.subTest("south and west letter after"): self.assertEqual(Point.from_string("49S 45.123 013W 22.123"), Point(-49.75205, -13.36872)) with self.subTest("decimal separator: comma"): self.assertEqual(Point.from_string("N 49 45,123 E 013 22,123"), Point(49.75205, 13.36872)) with self.subTest("degree symbol"): self.assertEqual(Point.from_string("N 49° 45.123 E 013° 22.123"), Point(49.75205, 13.36872)) with self.subTest("coma between lat and lon"): self.assertEqual(Point.from_string("N 49 45.123, E 013 22.123"), Point(49.75205, 13.36872)) with self.subTest("marginal values: zeroes"): self.assertEqual(Point.from_string("N 49 45.000 E 13 0.0"), Point(49.75, 13.0)) with self.subTest("include precision"): self.assertIn("precision", Point(49.75, 13.0).__dict__) with self.assertRaises(ValueError): Point.from_string("123")
def setUp(self): self.rect = Rectangle(Point(10.0, 20.0), Point(30.0, -5.0))
def geocode(self, location): """Return a :class:`.Point` object from geocoded location. :param str location: Location to geocode. """ return Point.from_location(self, location)
def load_by_guid(self): """Load cache details using the GUID to request and parse the caches 'print-page'. Loading as many properties as possible except the following ones, since they are not present on the 'print-page': + original_location + state + found + pm_only :raise .PMOnlyException: If the PM only warning is shown on the page """ # If GUID has not yet been set, load it using the "tiles_server" # utilizing `load_quick()` if not self.guid: self.load_quick() res = self.geocaching._request(self._urls["print_page"], params={"guid": self.guid}) if res.find("p", "Warning") is not None: raise errors.PMOnlyException() content = res.find(id="Content") self.name = content.find("h2").text self.location = Point.from_string( content.find("p", "LatLong Meta").text) type_img = os.path.basename(content.find("img").get("src")) self.type = Type.from_filename(os.path.splitext(type_img)[0]) size_img = content.find("img", src=re.compile("\/icons\/container\/")) self.size = Size.from_string(size_img.get("alt").split(": ")[1]) D_and_T_img = content.find("p", "Meta DiffTerr").find_all("img") self.difficulty, self.terrain = [ float(img.get("alt").split()[0]) for img in D_and_T_img ] # TODO do NOT use English phrases like "Placed by" to search for attributes self.author = content.find( "p", text=re.compile("Placed by:")).text.split("\r\n")[2].strip() hidden_p = content.find("p", text=re.compile("Placed Date:")) self.hidden = hidden_p.text.replace("Placed Date:", "").strip() attr_img = content.find_all("img", src=re.compile("\/attributes\/")) attributes_raw = [ os.path.basename(_.get("src")).rsplit("-", 1) for _ in attr_img ] self.attributes = { name: appendix.startswith("yes") for name, appendix in attributes_raw if not appendix.startswith("blank") } self.summary = content.find( "h2", text="Short Description").find_next("div").text self.description = content.find( "h2", text="Long Description").find_next("div").text self.hint = content.find(id="uxEncryptedHint").text self.favorites = content.find( "strong", text=re.compile("Favorites:")).parent.text.split()[-1] self.waypoints = Waypoint.from_html(content, "Waypoints")
def setUp(self): self.p = Polygon(*[ Point(*i) for i in [(10., 20.), (30., -5.), (-10., -170.), (-70., 0.), (0., 40)] ])
def setUp(self): self.w = Waypoint("id", "Parking", Point("N 56° 50.006′ E 13° 56.423′"), "This is a test")
def setUp(self): self.rect = Rectangle(Point(10., 20.), Point(30., -5.))
def test_from_string(self): with self.subTest("normal"): self.assertEqual(Point.from_string("N 49 45.123 E 013 22.123"), Point(49.75205, 13.36872)) with self.subTest("south and west"): self.assertEqual(Point.from_string("S 49 45.123 W 013 22.123"), Point(-48.24795, -12.63128)) with self.subTest("letter together"): self.assertEqual(Point.from_string("N49 45.123 E013 22.123"), Point(49.75205, 13.36872)) with self.subTest("letter after"): self.assertEqual(Point.from_string("49N 45.123 013E 22.123"), Point(49.75205, 13.36872)) with self.subTest("south and west letter after"): self.assertEqual(Point.from_string("49S 45.123 013W 22.123"), Point(-48.24795, -12.63128)) with self.subTest("decimal separator: coma"): self.assertEqual(Point.from_string("N 49 45,123 E 013 22,123"), Point(49.75205, 13.36872)) with self.subTest("degree symbol"): self.assertEqual(Point.from_string("N 49° 45.123 E 013° 22.123"), Point(49.75205, 13.36872)) with self.subTest("coma between lat and lon"): self.assertEqual(Point.from_string("N 49 45.123, E 013 22.123"), Point(49.75205, 13.36872)) with self.subTest("marginal values: zeroes"): self.assertEqual(Point.from_string("N 49 45.000 E 13 0.0"), Point(49.75, 13.0)) with self.subTest("include precision"): self.assertIn("precision", Point(49.75, 13.0).__dict__) with self.assertRaises(ValueError): Point.from_string("123")