Skip to content

Commit

Permalink
weeknotes
Browse files Browse the repository at this point in the history
  • Loading branch information
honzajavorek committed Jan 19, 2024
1 parent 66e4551 commit bbd1e30
Show file tree
Hide file tree
Showing 3 changed files with 313 additions and 104 deletions.
254 changes: 150 additions & 104 deletions blog/weeknotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,95 +23,146 @@


STRAVA_ACTIVITY_TYPES = {
'run': 'naběhal',
'walk': 'při procházkách nachodil',
'hike': 'na túrách nachodil',
'ride': 'ujel na kole',
'alpineski': 'sjel na lyžích',
"run": "naběhal",
"walk": "při procházkách nachodil",
"hike": "na túrách nachodil",
"ride": "ujel na kole",
"alpineski": "sjel na lyžích",
}

TITLES = {
'www.facebook.com': '(něco na Facebooku)',
'facebook.com': '(něco na Facebooku)',
'twitter.com': '(něco na Twitteru)',
'mobile.twitter.com': '(něco na Twitteru)'
"www.facebook.com": "(něco na Facebooku)",
"facebook.com": "(něco na Facebooku)",
"twitter.com": "(něco na Twitteru)",
"mobile.twitter.com": "(něco na Twitteru)",
}


@click.command(context_settings={'ignore_unknown_options': True})
@click.argument('title')
@click.option('--path', 'content_path', default='content', type=click.Path(exists=True, path_type=Path))
@click.option('--title-prefix', default='Týdenní poznámky')
@click.option('--jobs-api-url', default='https://junior.guru/api/jobs.csv')
@click.option('--settings-module', default='pelicanconf', type=importlib.import_module)
@click.option('--links-path', default='content/data/toots-links.json', type=click.Path(exists=True, path_type=Path))
@click.option('--jg-toots-path', default='content/data/toots-jg.json', type=click.Path(exists=True, path_type=Path))
@click.option('--strava-skip-sync', is_flag=True, default=False)
@click.option('--strava-client-id', envvar='STRAVA_CLIENT_ID', prompt=True, hide_input=True)
@click.option('--strava-client-secret', envvar='STRAVA_CLIENT_SECRET', prompt=True, hide_input=True)
@click.option('--mastodon-client_id', envvar='MASTODON_CLIENT_ID')
@click.option('--mastodon-client_secret', envvar='MASTODON_CLIENT_SECRET')
@click.option('--mastodon-access_token', envvar='MASTODON_ACCESS_TOKEN')
@click.option('--debug/--no-debug', default=False)
@click.option('--open/--no-open', default=True)
@click.command(context_settings={"ignore_unknown_options": True})
@click.argument("title")
@click.option(
"--path",
"content_path",
default="content",
type=click.Path(exists=True, path_type=Path),
)
@click.option("--title-prefix", default="Týdenní poznámky")
@click.option("--jobs-api-url", default="https://junior.guru/api/jobs.csv")
@click.option("--settings-module", default="pelicanconf", type=importlib.import_module)
@click.option(
"--links-path",
default="content/data/toots-links.json",
type=click.Path(exists=True, path_type=Path),
)
@click.option(
"--jg-toots-path",
default="content/data/toots-jg.json",
type=click.Path(exists=True, path_type=Path),
)
@click.option("--strava-skip-sync", is_flag=True, default=False)
@click.option(
"--strava-client-id", envvar="STRAVA_CLIENT_ID", prompt=True, hide_input=True
)
@click.option(
"--strava-client-secret",
envvar="STRAVA_CLIENT_SECRET",
prompt=True,
hide_input=True,
)
@click.option("--mastodon-client_id", envvar="MASTODON_CLIENT_ID")
@click.option("--mastodon-client_secret", envvar="MASTODON_CLIENT_SECRET")
@click.option("--mastodon-access_token", envvar="MASTODON_ACCESS_TOKEN")
@click.option("--debug/--no-debug", default=False)
@click.option("--open/--no-open", default=True)
@click.pass_context
def main(context, title, content_path, title_prefix, jobs_api_url, settings_module,
links_path, jg_toots_path,
strava_skip_sync, strava_client_id, strava_client_secret,
mastodon_client_id, mastodon_client_secret, mastodon_access_token,
debug, open):
def main(
context,
title,
content_path,
title_prefix,
jobs_api_url,
settings_module,
links_path,
jg_toots_path,
strava_skip_sync,
strava_client_id,
strava_client_secret,
mastodon_client_id,
mastodon_client_secret,
mastodon_access_token,
debug,
open,
):
context.invoke(update_command)
context.invoke(toots_command, client_id=mastodon_client_id,
client_secret=mastodon_client_secret,
access_token=mastodon_access_token)
context.invoke(
toots_command,
client_id=mastodon_client_id,
client_secret=mastodon_client_secret,
access_token=mastodon_access_token,
)

today = date.today()
today_cz = f'{today:%-d}{today:%-m}.'
today_cz = f"{today:%-d}{today:%-m}."
today_iso = today.isoformat()

is_weeknotes = lambda path: slugify(title_prefix) in path.name
weeknotes_paths = sorted(filter(is_weeknotes, content_path.glob('*.md')))
weeknotes_paths = sorted(filter(is_weeknotes, content_path.glob("*.md")))
last_weeknotes_path = weeknotes_paths[-1]
last_weeknotes_date = date.fromisoformat(last_weeknotes_path.stem[:10])
last_weeknotes_date_cz = f'{last_weeknotes_date:%-d}{last_weeknotes_date:%-m}.'
prefix = f'{title_prefix}: '
last_weeknotes_date_cz = f"{last_weeknotes_date:%-d}{last_weeknotes_date:%-m}."
prefix = f"{title_prefix}: "

# jobs
res = requests.get(jobs_api_url, stream=True)
res.raise_for_status()
jobs = [job for job in csv.DictReader(res.iter_lines(decode_unicode=True))
if job['url'].startswith('https://junior.guru')]
jobs = sorted(jobs, key=itemgetter('company_name'))
jobs = {company_name: random.choice(list(company_jobs))
for company_name, company_jobs
in itertools.groupby(jobs, key=itemgetter('company_name'))}
jobs = [
job
for job in csv.DictReader(res.iter_lines(decode_unicode=True))
if job["url"].startswith("https://junior.guru")
]
jobs = sorted(jobs, key=itemgetter("company_name"))
jobs = {
company_name: random.choice(list(company_jobs))
for company_name, company_jobs in itertools.groupby(
jobs, key=itemgetter("company_name")
)
}
jobs_text = "Aktuální nabídky práce pro juniory: "
jobs_text += ', '.join([f"[{job['company_name']}]({job['url']})" for job in jobs.values()])
jobs_text += ", ".join(
[f"[{job['company_name']}]({job['url']})" for job in jobs.values()]
)

# mastodon links
links = get_links(last_weeknotes_date, json.loads(links_path.read_text()))

# mastodon jg
jg_toots = get_jg_toots(last_weeknotes_date, json.loads(jg_toots_path.read_text()))
jg_toots_text = '\n '.join([f"- {toot['url']}" for toot in jg_toots])
jg_toots_text = "\n ".join([f"- {toot['url']}" for toot in jg_toots])

# strava
strava_defaults = {param.name: param.default for param
in strava_to_sqlite.get_params(context)}
strava_defaults = {
param.name: param.default for param in strava_to_sqlite.get_params(context)
}
if not strava_skip_sync:
context.invoke(strava_to_sqlite,
strava_client_id=strava_client_id,
strava_client_secret=strava_client_secret)
strava_activities = get_strava_activities(strava_defaults['strava_sqlite_database'],
last_weeknotes_date, today)
context.invoke(
strava_to_sqlite,
strava_client_id=strava_client_id,
strava_client_secret=strava_client_secret,
)
strava_activities = get_strava_activities(
strava_defaults["strava_sqlite_database"], last_weeknotes_date, today
)
strava_stats = calc_strava_stats(strava_activities)
strava_text = strava_stats_to_text(last_weeknotes_date, today, strava_stats)

# generate weeknotes
title = f'{prefix}{title}'
path = content_path / f'{today_iso}_{slugify(title)}.md'
last_weeknotes_path = '{filename}' + str(last_weeknotes_path.relative_to(content_path))
content = dedent(f'''
title = f"{prefix}{title}"
path = content_path / f"{today_iso}_{slugify(title)}.md"
last_weeknotes_path = "{filename}" + str(
last_weeknotes_path.relative_to(content_path)
)
content = dedent(
f"""
Title: {title}
Image: images/markus-spiske-RiSAjGsa0vg-unsplash.jpg
Lang: cs
Expand All @@ -126,9 +177,6 @@ def main(context, title, content_path, title_prefix, jobs_api_url, settings_modu
<div class="alert alert-warning" role="alert" markdown="1">
**Čísla:** Finanční výsledky, návštěvnost a další čísla k junior.guru [mám přímo na webu](https://junior.guru/open/).
{jobs_text}
**Plány:** Četli jste, co [teď plánuji]({{filename}}2023-08-07_letni-pit-stop.md)?
Svůj postup zaznamenávám do [tabulky na GitHubu](https://github.com/orgs/juniorguru/projects/3/).
</div>
{jg_toots_text}
Expand All @@ -139,12 +187,6 @@ def main(context, title, content_path, title_prefix, jobs_api_url, settings_modu
- {strava_text}
Detaily na [Strava](https://www.strava.com/athletes/31242569), jediné sociální síti, kde si napsání statusu musíte zasloužit.
<div class="alert alert-warning" role="alert" markdown="1">
**Okénko duševního zdraví:**
Máte dojem, že na rozdíl ode mně nic nestíháte?
Buďte v klidu, [není to závod]({{filename}}2020-06-04_neni-to-zavod.md)!
</div>
## Plánuji
1.
Expand All @@ -156,15 +198,16 @@ def main(context, title, content_path, title_prefix, jobs_api_url, settings_modu
Když na něco narazím a líbí se mi to, sdílím to [na Mastodonu](https://mastodonczech.cz/@honzajavorek).
Od posledních poznámek jsem sdílel:
''').lstrip()
"""
).lstrip()
for link in links:
content += f"- [{link['title']}]({link['url']})"
content += f"<br>{link['comment']}" if link['comment'] else ''
content += '\n'
content += f"<br>{link['comment']}" if link["comment"] else ""
content += "\n"

if debug:
click.secho(path.name, bold=True)
click.echo('')
click.echo("")
click.echo(content)
else:
path.write_text(content)
Expand All @@ -174,27 +217,26 @@ def main(context, title, content_path, title_prefix, jobs_api_url, settings_modu

def get_jg_toots(since_date: date, toots: list):
for toot in toots:
if datetime.fromisoformat(toot['created_at']).date() < since_date:
if datetime.fromisoformat(toot["created_at"]).date() < since_date:
continue
yield dict(content=toot['content'],
url=toot['url'])
yield dict(content=toot["content"], url=toot["url"])


def get_links(since_date: date, links: list):
for link in links:
if datetime.fromisoformat(link['created_at']).date() < since_date:
if datetime.fromisoformat(link["created_at"]).date() < since_date:
continue

html_tree = html_soup.fromstring(link['content'])
html_tree = html_soup.fromstring(link["content"])
title = None

if card := link.get('card'):
link_url = card['url']
title = card['title']
if card := link.get("card"):
link_url = card["url"]
title = card["title"]
else:
link_url = html_tree.cssselect('a')[0].get('href')
link_url = html_tree.cssselect("a")[0].get("href")

if 'overcast.fm' in link_url:
if "overcast.fm" in link_url:
url = get_canonical_overcast_url(link_url)
else:
url = link_url
Expand All @@ -208,9 +250,7 @@ def get_links(since_date: date, links: list):
element.getparent().remove(element)
comment = html_tree.text_content().strip()

yield dict(title=title,
comment=comment,
url=url)
yield dict(title=title, comment=comment, url=url)


def get_title_from_webpage(webpage):
Expand All @@ -221,55 +261,59 @@ def get_title_from_webpage(webpage):


def get_title_from_url(url):
response = requests.get(url, stream=True, headers={'User-Agent': 'HonzaJavorekBot (+https://honzajavorek.cz)'})
response = requests.get(
url,
stream=True,
headers={"User-Agent": "HonzaJavorekBot (+https://honzajavorek.cz)"},
)
try:
response.raise_for_status()
except requests.exceptions.HTTPError:
pass
else:
for line in response.iter_lines(decode_unicode=True):
match = re.search(r'<title>([^<]+)', str(line), re.I)
match = re.search(r"<title>([^<]+)", str(line), re.I)
if match:
return match.group(1).strip()
return '(bez titulku)'
return "(bez titulku)"


def get_canonical_overcast_url(url):
response = requests.get(url, stream=True)
response.raise_for_status()
for line in response.iter_lines(decode_unicode=True):
if 'rel="canonical"' in line:
canonical_url = re.search(r'rel="canonical"\s+href="([^"]+)"', line).group(1)
canonical_url = re.search(r'rel="canonical"\s+href="([^"]+)"', line).group(
1
)
parts = urlparse(canonical_url)
if (
parts.query or
parts.params or
parts.fragment or
parts.path != '/'
):
if parts.query or parts.params or parts.fragment or parts.path != "/":
return canonical_url
return url


def get_strava_activities(sqlite, start_date, end_date):
db = sqlite_utils.Database(sqlite)
return db.query('''
return db.query(
"""
select * from activity
where start_date >= ? and start_date <= ?
''', [start_date, end_date])
""",
[start_date, end_date],
)


def calc_strava_stats(activities):
stats = {}
for activity in activities:
key = activity['type'].lower()
key = activity["type"].lower()
stats.setdefault(key, dict(count=0, time=0, distance=0))
stats[key]['count'] += 1
stats[key]['time'] += (activity['elapsed_time'] / 60 / 60) # h
stats[key]['distance'] += (activity['distance'] / 1000) # km
stats[key]["count"] += 1
stats[key]["time"] += activity["elapsed_time"] / 60 / 60 # h
stats[key]["distance"] += activity["distance"] / 1000 # km
for substats in stats.values():
substats['time'] = math.ceil(substats['time'])
substats['distance'] = math.ceil(substats['distance'])
substats["time"] = math.ceil(substats["time"])
substats["distance"] = math.ceil(substats["distance"])

ordering = list(STRAVA_ACTIVITY_TYPES.keys())
return dict(sorted(stats.items(), key=lambda item: ordering.index(item[0])))
Expand All @@ -280,16 +324,18 @@ def strava_stats_to_text(start_date, end_date, stats):
text = f"Za {total_days} dní "

if not stats:
return text + 'jsem se nevěnoval žádné sportovní aktivitě.'
return text + "jsem se nevěnoval žádné sportovní aktivitě."

parts = [
f"{STRAVA_ACTIVITY_TYPES[activity_type]} {substats['distance']} km"
for activity_type, substats in stats.items()
]

total_distance = sum(int(substats['distance']) for substats in stats.values())
total_time = sum(int(substats['time']) for substats in stats.values())
total_distance = sum(int(substats["distance"]) for substats in stats.values())
total_time = sum(int(substats["time"]) for substats in stats.values())

text += f"jsem {', '.join(parts)}."
text += f" Celkem jsem se hýbal {total_time} h a zdolal při tom {total_distance} km."
text += (
f" Celkem jsem se hýbal {total_time} h a zdolal při tom {total_distance} km."
)
return text
Loading

0 comments on commit bbd1e30

Please sign in to comment.