-
Notifications
You must be signed in to change notification settings - Fork 4
/
sync_tips.py
137 lines (105 loc) · 3.64 KB
/
sync_tips.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""
Script to post new code snippets bobcodesit repo -> Codeimag.es API
"""
import datetime
import os
import pathlib
import sys
import typing
import dotenv
import requests
dotenv.load_dotenv()
CODEIMAGES_USER = os.environ["CODEIMAGES_USER"]
CODEIMAGES_PASSWORD = os.environ["CODEIMAGES_PASSWORD"]
DEBUG = os.environ.get("DEBUG", False)
LIVE_SITE = "https://pybites-codeimages.herokuapp.com"
BASE_URL = "http://localhost:8000" if DEBUG else LIVE_SITE
TOKEN_URL = f"{BASE_URL}/token"
CREATE_TIP_URL = f"{BASE_URL}/create"
TIPS_DIR = pathlib.Path("notes")
class Tip(typing.NamedTuple):
title: str
description: str
code: str
def get_token(user, password):
"""
Perform authentication against CodeImages API obtaining an access
token which can be used to make post requests (create tip images).
"""
payload = {"username": user, "password": password}
resp = requests.post(TOKEN_URL, data=payload)
data = resp.json()
if "access_token" not in data:
sys.exit(data["detail"])
return data["access_token"]
def _extract_dt(tip_file: pathlib.Path) -> datetime.datetime:
"""
Get a relative note md file path and convert the file name, which
is a timestamp, to a datetime.
"""
return datetime.datetime.strptime(tip_file.stem, "%Y%m%d%H%M%S")
def get_latest_tips(
*, look_hours_back: int = 24, tips_dir: pathlib.Path = TIPS_DIR
) -> list[pathlib.Path]:
"""
Looks at notes directory and retrieves the md note files that were
made in the last 24 hours (or whatever look_hours_back gets set to)
"""
goback_limit = datetime.datetime.now() - datetime.timedelta(hours=look_hours_back)
latest_tips = [
tip_file
for tip_file in tips_dir.glob("*.md")
if _extract_dt(tip_file) >= goback_limit
]
return latest_tips
def parse_tip_file(tip_file: pathlib.Path) -> Tip:
"""
A tip note file is structured like this:
# title
Description (usually single line, could be more) ...
```
code block
```
#one_or_more_tags
"""
lines = tip_file.read_text().splitlines()
title = lines[0].lstrip("# ")
start_description_line = 2
code_block_indices = [i for i, line in enumerate(lines) if line.strip() == "```"]
if code_block_indices:
# if there are multiple code blocks, take the first one
if len(code_block_indices) > 2:
code_block_indices = code_block_indices[:2]
start_line, end_line = code_block_indices
description = "\n".join(lines[start_description_line:start_line]).strip()
code = "\n".join(lines[start_line + 1: end_line]).strip()
else:
description = "\n".join(lines[start_description_line:len(lines)]).strip()
code = ""
return Tip(title=title, description=description, code=code)
def main(args):
try:
user, password, *_ = args
except ValueError:
user, password = CODEIMAGES_USER, CODEIMAGES_PASSWORD
token = get_token(user, password)
headers = {"Authorization": f"Bearer {token}"}
latest_tips = get_latest_tips()
for tip_file in latest_tips:
tip = parse_tip_file(tip_file)
payload = {
"title": tip.title,
"code": tip.code,
"description": tip.description,
}
print(f"Posting tip {tip.title}")
response = requests.post(CREATE_TIP_URL, json=payload, headers=headers)
print(f"API response code: {response.status_code}")
try:
response.raise_for_status()
except requests.exceptions.HTTPError as exc:
print(f"Error: {exc}")
else:
print("OK")
if __name__ == "__main__":
main(sys.argv[1:])