Skip to content

Commit

Permalink
Merge pull request #9 from lordyavin/feature/export_sector
Browse files Browse the repository at this point in the history
Feature/export sector
  • Loading branch information
lordyavin authored Jun 14, 2020
2 parents cb97ada + d6667ac commit fab370c
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 33 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ Since the restart of 8a.nu you can no longer export your ascents. This project a
# Usage

```
cli.py [-h] -b {Firefox,Chrome} [-v] [-V] username
cli.py [-h] -b {Firefox} [-v] [-V] username
positional arguments:
username Your 8a user name
optional arguments:
-h, --help show this help message and exit
-b {Firefox,Chrome}, --browser {Firefox,Chrome}
-b {Firefox}, --browser {Firefox}
The browser to use [default: Firefox]
-v, --verbose set verbosity level [default: 0]
-V, --version show program's version number and exit
```
```
9 changes: 5 additions & 4 deletions src/eightanu/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@

from eightanu import webdriver
from eightanu.export import export
from eightanu.webdriver import FIREFOX

__all__ = []
__version__ = 0.2
__version__ = 0.3
__date__ = '2020-05-27'
__updated__ = '2020-06-01'
__updated__ = '2020-06-08'

DEBUG = 0
TESTRUN = 0
Expand Down Expand Up @@ -72,8 +73,8 @@ def main(argv=None): # IGNORE:C0111
try:
# Setup argument parser
parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter)
parser.add_argument("-b", "--browser", dest="browser", type=str, choices=webdriver.SUPPORTED_BROWSER, required=True)
parser.add_argument("-v", "--verbose", dest="verbose", action="count", help="set verbosity level [default: %(default)s]", default=0)
parser.add_argument("-b", "--browser", dest="browser", type=str, choices=webdriver.SUPPORTED_BROWSER, default=FIREFOX, help="The browser to use [default: %(default)s]")
parser.add_argument("-v", "--verbose", dest="verbose", action="count", default=0, help="set verbosity level [default: %(default)s]")
parser.add_argument('-V', '--version', action='version', version=program_version_message)

parser.add_argument("username", type=str, help="Your 8a user name")
Expand Down
142 changes: 117 additions & 25 deletions src/eightanu/export.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# encoding=utf8
'''
eightanu.export -- The 8a.nu logbook export
Expand All @@ -12,16 +13,73 @@
from eightanu import webdriver
from selenium.webdriver.remote.webdriver import WebDriver
from eightanu.webdriver import SUPPORTED_BROWSER
import re

FILLED_STAR_STYLE = "fill: rgb(42, 42, 47);"

COLIDX_STYLE = 0
COLIDX_NAME = 1
COLIDX_CRAG = 2
COLIDX_DATE = 3
COLIDX_GRADE = 2
COLIDX_CRAG = 3
COLIDX_NOTES = 4
COLIDX_RATING = 5

RE_ROUTE_URL = re.compile(r"\/crags\/sportclimbing\/(?P<country>\S+)\/(?P<crag>\S+)\/sectors\/(?P<sector>\S+)\/routes\/(?P<route>\S+)")


class Ascent8a:
""" The ascent data to export. """

MAX_RATING = 5

def __init__(self, date, style, route, grade, sector, crag, country, notes, rating):
"""
@param date: American date
@param style: The ascent style
@param route: The route name
@param grade: The route grade
@param sector: The sector name
@param crag: The crag name
@param country: The country name
@param notes: Ascent notes
@param rating: The 5 stars based rating
"""
self.date = date
self.style = style
self.route = Ascent8a._fix_route_name(route)
self.grade = grade
self.sector = sector
self.crag = crag
self.country = country
self.notes = notes
self.rating = rating

@staticmethod
def headers():
return ("DATE", "STYLE", "ROUTE", "GRADE", "SECTOR", "CRAG", "COUNTRY", "NOTES", "RATING")

def as_csv(self, separator=";"):
return separator.join((self.date,
self.style,
self.route,
self.grade,
self.sector,
self.crag,
self.country,
self.notes,
self.rating_as_stars()))

def rating_as_stars(self):
stars = "★" * self.rating
stars += "☆" * (Ascent8a.MAX_RATING - self.rating)
return stars

@staticmethod
def _fix_route_name(name):
if "\n" in name:
return name[:name.index("\n")]
return name


def _select_alltime_ascents(driver):
assert isinstance(driver, WebDriver)
Expand Down Expand Up @@ -50,7 +108,7 @@ def _read_table_headers(table):

thead = table.find_element_by_tag_name("thead")
table_headers = thead.find_elements_by_tag_name("th")[1:] # skip first empty cell
headers = ["DATE", "STYLE"] + [cell.text.strip() for cell in table_headers]
headers = ["STYLE"] + [cell.text.strip() for cell in table_headers]
return headers


Expand All @@ -64,46 +122,76 @@ def _determine_rating(cells):
return rating


def _fix_route_name(name):
if "\n" in name:
return name[:name.index("\n")]
return name
def _determine_sector(cells):
routeurl = cells[COLIDX_NAME].find_element_by_tag_name("a").get_attribute("href")
m = re.search(RE_ROUTE_URL, routeurl)
if m:
return m.group("sector")
else:
return "unknown-sector"


def _determine_country(cells):
routeurl = cells[COLIDX_NAME].find_element_by_tag_name("a").get_attribute("href")
m = re.search(RE_ROUTE_URL, routeurl)
if m:
return m.group("country")
else:
return "unknown-country"


def _determine_style(cells):
return cells[COLIDX_STYLE].find_element_by_tag_name("svg").get_attribute("title")


def _get_routename(cells):
return cells[COLIDX_NAME].text


def _get_grade(cells):
return cells[COLIDX_GRADE].text


def _get_notes(cells):
return cells[COLIDX_NOTES].text


def _get_crag(cells):
return cells[COLIDX_CRAG].text


def _read_ascents(table, headers):
assert isinstance(table, WebElement)
assert isinstance(headers, (list, tuple))

ascents = []
name_idx = headers.index("NAME")
ascents = []
groups = table.find_elements_by_tag_name("tbody")
for group in groups:
date = group.find_element_by_tag_name("th").text
rows = group.find_elements_by_tag_name("tr")
for row in rows:
cells = row.find_elements_by_tag_name("td") # skip first empty cell
if len(cells) == len(headers) - 1:
style = cells[COLIDX_STYLE].find_element_by_tag_name("svg").get_attribute("title")
ascent = [date, style]
ascent += [cell.text.strip() for cell in cells[1:-1]]

rating = _determine_rating(cells)
ascent.append("%s stars" % rating)

ascent[name_idx] = _fix_route_name(ascent[name_idx])
if len(cells) == len(headers):
ascent = Ascent8a(date=date,
style=_determine_style(cells),
route=_get_routename(cells),
grade=_get_grade(cells),
sector=_determine_sector(cells),
crag=_get_crag(cells),
country=_determine_country(cells),
notes=_get_notes(cells),
rating=_determine_rating(cells))
ascents.append(ascent)

return ascents


def _print_logbook(headers, ascents):
assert isinstance(headers, (list, tuple))
def _print_logbook(ascents):
assert isinstance(ascents, (list, tuple))

print(";".join(headers))
print(";".join(Ascent8a.headers()))
for ascent in ascents:
print(";".join(ascent))
print(ascent.as_csv())


def export(browser, username, verbose=0):
Expand All @@ -112,7 +200,11 @@ def export(browser, username, verbose=0):
assert isinstance(verbose, int)

username = username.replace(" ", "-").lower()
url = "https://www.8a.nu/user/{username}/sportclimbing".format(username=username)
url = "https://www.8a.nu/user/{username}/sportclimbing".format(username=username)

if verbose:
print("Exporting ascents from %s at" % username)
print("%s ..." % url)

driver = webdriver.get(browser, verbose)
try:
Expand All @@ -123,6 +215,6 @@ def export(browser, username, verbose=0):
table = driver.find_element_by_class_name("user-ascents")
headers = _read_table_headers(table)
ascents = _read_ascents(table, headers)
_print_logbook(headers, ascents)
_print_logbook(ascents)
finally:
driver.close()
2 changes: 1 addition & 1 deletion src/eightanu/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
CHROME = "Chrome"

SUPPORTED_BROWSER = (
FIREFOX
FIREFOX,
)

DOWNLOAD = {
Expand Down

0 comments on commit fab370c

Please sign in to comment.