polarsteps-data-parser 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- polarsteps_data_parser/__main__.py +74 -74
- polarsteps_data_parser/model.py +147 -147
- polarsteps_data_parser/pdf_generator.py +155 -155
- polarsteps_data_parser/retrieve_step_comments.py +135 -135
- polarsteps_data_parser/utils.py +71 -71
- {polarsteps_data_parser-0.1.0.dist-info → polarsteps_data_parser-0.1.1.dist-info}/METADATA +15 -25
- polarsteps_data_parser-0.1.1.dist-info/RECORD +10 -0
- polarsteps_data_parser-0.1.1.dist-info/licenses/LICENSE +21 -0
- polarsteps_data_parser-0.1.0.dist-info/RECORD +0 -10
- polarsteps_data_parser-0.1.0.dist-info/licenses/LICENSE +0 -674
- {polarsteps_data_parser-0.1.0.dist-info → polarsteps_data_parser-0.1.1.dist-info}/WHEEL +0 -0
- {polarsteps_data_parser-0.1.0.dist-info → polarsteps_data_parser-0.1.1.dist-info}/entry_points.txt +0 -0
@@ -1,155 +1,155 @@
|
|
1
|
-
from pathlib import Path
|
2
|
-
|
3
|
-
from reportlab.lib.pagesizes import letter
|
4
|
-
from reportlab.lib.utils import ImageReader
|
5
|
-
from reportlab.pdfbase.pdfmetrics import stringWidth
|
6
|
-
from reportlab.pdfgen.canvas import Canvas
|
7
|
-
|
8
|
-
from polarsteps_data_parser.model import Trip, Step
|
9
|
-
from tqdm import tqdm
|
10
|
-
|
11
|
-
|
12
|
-
class PDFGenerator:
|
13
|
-
"""Generates a PDF for Polarsteps Trip objects."""
|
14
|
-
|
15
|
-
MAIN_FONT = ("Helvetica", 12)
|
16
|
-
BOLD_FONT = ("Helvetica-Bold", 12)
|
17
|
-
HEADING_FONT = ("Helvetica-Bold", 16)
|
18
|
-
TITLE_HEADING_FONT = ("Helvetica-Bold", 36)
|
19
|
-
|
20
|
-
def __init__(self, output: str) -> None:
|
21
|
-
self.filename = output
|
22
|
-
self.canvas = None
|
23
|
-
self.width, self.height = letter
|
24
|
-
self.y_position = self.height - 30
|
25
|
-
|
26
|
-
def generate_pdf(self, trip: Trip) -> None:
|
27
|
-
"""Generate a PDF for a given trip."""
|
28
|
-
self.canvas = Canvas(self.filename, pagesize=letter)
|
29
|
-
|
30
|
-
self.canvas.setTitle(trip.name)
|
31
|
-
|
32
|
-
self.generate_title_page(trip)
|
33
|
-
|
34
|
-
for i, step in tqdm(enumerate(trip.steps), desc="Generating pages", total=len(trip.steps), ncols=80):
|
35
|
-
if i == 5:
|
36
|
-
break
|
37
|
-
self.generate_step_pages(step)
|
38
|
-
|
39
|
-
self.canvas.save()
|
40
|
-
|
41
|
-
def generate_title_page(self, trip: Trip) -> None:
|
42
|
-
"""Generate title page."""
|
43
|
-
self.title_heading(trip.name)
|
44
|
-
self.y_position -= 20
|
45
|
-
self.short_text(
|
46
|
-
f"{trip.start_date.strftime('%d-%m-%Y')} - {trip.end_date.strftime('%d-%m-%Y')}", bold=True, centered=True
|
47
|
-
)
|
48
|
-
self.photo(trip.cover_photo_path, centered=True, photo_width=400)
|
49
|
-
|
50
|
-
def generate_step_pages(self, step: Step) -> None:
|
51
|
-
"""Add a step to the canvas."""
|
52
|
-
self.new_page()
|
53
|
-
|
54
|
-
self.heading(step.name)
|
55
|
-
|
56
|
-
self.short_text(f"Location: {step.location.name}, {step.location.country}")
|
57
|
-
self.short_text(f"Date: {step.date.strftime('%d-%m-%Y')}")
|
58
|
-
|
59
|
-
self.long_text(step.description)
|
60
|
-
|
61
|
-
for comment in step.comments:
|
62
|
-
self.short_text(comment.follower.name, bold=True)
|
63
|
-
self.long_text(comment.text)
|
64
|
-
|
65
|
-
for photo in step.photos:
|
66
|
-
self.photo(photo)
|
67
|
-
|
68
|
-
def new_page(self) -> None:
|
69
|
-
"""Add a new page to the canvas."""
|
70
|
-
self.canvas.showPage()
|
71
|
-
self.width, self.height = letter
|
72
|
-
self.y_position = self.height - 30
|
73
|
-
|
74
|
-
def heading(self, text: str) -> None:
|
75
|
-
"""Add heading to canvas."""
|
76
|
-
if self.y_position < 50:
|
77
|
-
self.new_page()
|
78
|
-
self.canvas.setFont(*self.HEADING_FONT)
|
79
|
-
self.canvas.drawString(30, self.y_position, text)
|
80
|
-
self.y_position -= 30
|
81
|
-
|
82
|
-
def title_heading(self, text: str) -> None:
|
83
|
-
"""Add heading to canvas."""
|
84
|
-
self.y_position -= 100
|
85
|
-
self.canvas.setFont(*self.TITLE_HEADING_FONT)
|
86
|
-
self.canvas.drawString(self.calc_width_centered(text, self.TITLE_HEADING_FONT), self.y_position, text)
|
87
|
-
self.y_position -= 30
|
88
|
-
|
89
|
-
def calc_width_centered(self, text: str, font: tuple) -> float:
|
90
|
-
"""Calculate the width location to center the text."""
|
91
|
-
return (self.width - stringWidth(text, *font)) / 2.0
|
92
|
-
|
93
|
-
def short_text(self, text: str, bold: bool = False, centered: bool = False) -> None:
|
94
|
-
"""Add short text to canvas."""
|
95
|
-
if self.y_position < 50:
|
96
|
-
self.new_page()
|
97
|
-
font = self.BOLD_FONT if bold else self.MAIN_FONT
|
98
|
-
self.canvas.setFont(*font)
|
99
|
-
width = self.calc_width_centered(text, self.MAIN_FONT) if centered else 30
|
100
|
-
self.canvas.drawString(width, self.y_position, text)
|
101
|
-
self.y_position -= 20
|
102
|
-
|
103
|
-
def long_text(self, text: str) -> None:
|
104
|
-
"""Add long text to canvas."""
|
105
|
-
if text is None:
|
106
|
-
return
|
107
|
-
|
108
|
-
self.y_position -= 10
|
109
|
-
lines = self.wrap_text(text, self.width - 60)
|
110
|
-
for line in lines:
|
111
|
-
if self.y_position < 50:
|
112
|
-
self.new_page()
|
113
|
-
self.canvas.setFont(*self.MAIN_FONT)
|
114
|
-
self.canvas.drawString(30, self.y_position, line)
|
115
|
-
self.y_position -= 20
|
116
|
-
self.y_position -= 20
|
117
|
-
|
118
|
-
def photo(self, photo_path: Path | str, centered: bool = False, photo_width: int = 250) -> None:
|
119
|
-
"""Add photo to canvas."""
|
120
|
-
image = ImageReader(photo_path)
|
121
|
-
img_width, img_height = image.getSize()
|
122
|
-
aspect = img_height / float(img_width)
|
123
|
-
new_height = photo_width * aspect
|
124
|
-
if self.y_position - new_height < 50:
|
125
|
-
self.canvas.showPage()
|
126
|
-
self.y_position = self.height - 30
|
127
|
-
self.canvas.drawImage(
|
128
|
-
image,
|
129
|
-
(self.width - photo_width) / 2.0 if centered else 30,
|
130
|
-
self.y_position - new_height,
|
131
|
-
width=photo_width,
|
132
|
-
height=new_height,
|
133
|
-
)
|
134
|
-
self.y_position = self.y_position - new_height - 20
|
135
|
-
|
136
|
-
def wrap_text(self, text: str, max_width: int) -> list:
|
137
|
-
"""Wrap text to fit within max_width."""
|
138
|
-
self.canvas.setFont(*self.MAIN_FONT)
|
139
|
-
lines = []
|
140
|
-
words = text.replace("\n", " <newline> ").split()
|
141
|
-
current_line = ""
|
142
|
-
for word in words:
|
143
|
-
if word == "<newline>":
|
144
|
-
lines.append(current_line)
|
145
|
-
current_line = ""
|
146
|
-
continue
|
147
|
-
|
148
|
-
test_line = f"{current_line} {word}".strip()
|
149
|
-
if self.canvas.stringWidth(test_line) <= max_width:
|
150
|
-
current_line = test_line
|
151
|
-
else:
|
152
|
-
lines.append(current_line)
|
153
|
-
current_line = word
|
154
|
-
lines.append(current_line)
|
155
|
-
return lines
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
from reportlab.lib.pagesizes import letter
|
4
|
+
from reportlab.lib.utils import ImageReader
|
5
|
+
from reportlab.pdfbase.pdfmetrics import stringWidth
|
6
|
+
from reportlab.pdfgen.canvas import Canvas
|
7
|
+
|
8
|
+
from polarsteps_data_parser.model import Trip, Step
|
9
|
+
from tqdm import tqdm
|
10
|
+
|
11
|
+
|
12
|
+
class PDFGenerator:
|
13
|
+
"""Generates a PDF for Polarsteps Trip objects."""
|
14
|
+
|
15
|
+
MAIN_FONT = ("Helvetica", 12)
|
16
|
+
BOLD_FONT = ("Helvetica-Bold", 12)
|
17
|
+
HEADING_FONT = ("Helvetica-Bold", 16)
|
18
|
+
TITLE_HEADING_FONT = ("Helvetica-Bold", 36)
|
19
|
+
|
20
|
+
def __init__(self, output: str) -> None:
|
21
|
+
self.filename = output
|
22
|
+
self.canvas = None
|
23
|
+
self.width, self.height = letter
|
24
|
+
self.y_position = self.height - 30
|
25
|
+
|
26
|
+
def generate_pdf(self, trip: Trip) -> None:
|
27
|
+
"""Generate a PDF for a given trip."""
|
28
|
+
self.canvas = Canvas(self.filename, pagesize=letter)
|
29
|
+
|
30
|
+
self.canvas.setTitle(trip.name)
|
31
|
+
|
32
|
+
self.generate_title_page(trip)
|
33
|
+
|
34
|
+
for i, step in tqdm(enumerate(trip.steps), desc="Generating pages", total=len(trip.steps), ncols=80):
|
35
|
+
if i == 5:
|
36
|
+
break
|
37
|
+
self.generate_step_pages(step)
|
38
|
+
|
39
|
+
self.canvas.save()
|
40
|
+
|
41
|
+
def generate_title_page(self, trip: Trip) -> None:
|
42
|
+
"""Generate title page."""
|
43
|
+
self.title_heading(trip.name)
|
44
|
+
self.y_position -= 20
|
45
|
+
self.short_text(
|
46
|
+
f"{trip.start_date.strftime('%d-%m-%Y')} - {trip.end_date.strftime('%d-%m-%Y')}", bold=True, centered=True
|
47
|
+
)
|
48
|
+
self.photo(trip.cover_photo_path, centered=True, photo_width=400)
|
49
|
+
|
50
|
+
def generate_step_pages(self, step: Step) -> None:
|
51
|
+
"""Add a step to the canvas."""
|
52
|
+
self.new_page()
|
53
|
+
|
54
|
+
self.heading(step.name)
|
55
|
+
|
56
|
+
self.short_text(f"Location: {step.location.name}, {step.location.country}")
|
57
|
+
self.short_text(f"Date: {step.date.strftime('%d-%m-%Y')}")
|
58
|
+
|
59
|
+
self.long_text(step.description)
|
60
|
+
|
61
|
+
for comment in step.comments:
|
62
|
+
self.short_text(comment.follower.name, bold=True)
|
63
|
+
self.long_text(comment.text)
|
64
|
+
|
65
|
+
for photo in step.photos:
|
66
|
+
self.photo(photo)
|
67
|
+
|
68
|
+
def new_page(self) -> None:
|
69
|
+
"""Add a new page to the canvas."""
|
70
|
+
self.canvas.showPage()
|
71
|
+
self.width, self.height = letter
|
72
|
+
self.y_position = self.height - 30
|
73
|
+
|
74
|
+
def heading(self, text: str) -> None:
|
75
|
+
"""Add heading to canvas."""
|
76
|
+
if self.y_position < 50:
|
77
|
+
self.new_page()
|
78
|
+
self.canvas.setFont(*self.HEADING_FONT)
|
79
|
+
self.canvas.drawString(30, self.y_position, text)
|
80
|
+
self.y_position -= 30
|
81
|
+
|
82
|
+
def title_heading(self, text: str) -> None:
|
83
|
+
"""Add heading to canvas."""
|
84
|
+
self.y_position -= 100
|
85
|
+
self.canvas.setFont(*self.TITLE_HEADING_FONT)
|
86
|
+
self.canvas.drawString(self.calc_width_centered(text, self.TITLE_HEADING_FONT), self.y_position, text)
|
87
|
+
self.y_position -= 30
|
88
|
+
|
89
|
+
def calc_width_centered(self, text: str, font: tuple) -> float:
|
90
|
+
"""Calculate the width location to center the text."""
|
91
|
+
return (self.width - stringWidth(text, *font)) / 2.0
|
92
|
+
|
93
|
+
def short_text(self, text: str, bold: bool = False, centered: bool = False) -> None:
|
94
|
+
"""Add short text to canvas."""
|
95
|
+
if self.y_position < 50:
|
96
|
+
self.new_page()
|
97
|
+
font = self.BOLD_FONT if bold else self.MAIN_FONT
|
98
|
+
self.canvas.setFont(*font)
|
99
|
+
width = self.calc_width_centered(text, self.MAIN_FONT) if centered else 30
|
100
|
+
self.canvas.drawString(width, self.y_position, text)
|
101
|
+
self.y_position -= 20
|
102
|
+
|
103
|
+
def long_text(self, text: str) -> None:
|
104
|
+
"""Add long text to canvas."""
|
105
|
+
if text is None:
|
106
|
+
return
|
107
|
+
|
108
|
+
self.y_position -= 10
|
109
|
+
lines = self.wrap_text(text, self.width - 60)
|
110
|
+
for line in lines:
|
111
|
+
if self.y_position < 50:
|
112
|
+
self.new_page()
|
113
|
+
self.canvas.setFont(*self.MAIN_FONT)
|
114
|
+
self.canvas.drawString(30, self.y_position, line)
|
115
|
+
self.y_position -= 20
|
116
|
+
self.y_position -= 20
|
117
|
+
|
118
|
+
def photo(self, photo_path: Path | str, centered: bool = False, photo_width: int = 250) -> None:
|
119
|
+
"""Add photo to canvas."""
|
120
|
+
image = ImageReader(photo_path)
|
121
|
+
img_width, img_height = image.getSize()
|
122
|
+
aspect = img_height / float(img_width)
|
123
|
+
new_height = photo_width * aspect
|
124
|
+
if self.y_position - new_height < 50:
|
125
|
+
self.canvas.showPage()
|
126
|
+
self.y_position = self.height - 30
|
127
|
+
self.canvas.drawImage(
|
128
|
+
image,
|
129
|
+
(self.width - photo_width) / 2.0 if centered else 30,
|
130
|
+
self.y_position - new_height,
|
131
|
+
width=photo_width,
|
132
|
+
height=new_height,
|
133
|
+
)
|
134
|
+
self.y_position = self.y_position - new_height - 20
|
135
|
+
|
136
|
+
def wrap_text(self, text: str, max_width: int) -> list:
|
137
|
+
"""Wrap text to fit within max_width."""
|
138
|
+
self.canvas.setFont(*self.MAIN_FONT)
|
139
|
+
lines = []
|
140
|
+
words = text.replace("\n", " <newline> ").split()
|
141
|
+
current_line = ""
|
142
|
+
for word in words:
|
143
|
+
if word == "<newline>":
|
144
|
+
lines.append(current_line)
|
145
|
+
current_line = ""
|
146
|
+
continue
|
147
|
+
|
148
|
+
test_line = f"{current_line} {word}".strip()
|
149
|
+
if self.canvas.stringWidth(test_line) <= max_width:
|
150
|
+
current_line = test_line
|
151
|
+
else:
|
152
|
+
lines.append(current_line)
|
153
|
+
current_line = word
|
154
|
+
lines.append(current_line)
|
155
|
+
return lines
|
@@ -1,135 +1,135 @@
|
|
1
|
-
import json
|
2
|
-
import os
|
3
|
-
from pathlib import Path
|
4
|
-
|
5
|
-
import requests
|
6
|
-
|
7
|
-
from polarsteps_data_parser.model import Trip, StepComment
|
8
|
-
|
9
|
-
# Define the headers used for the request to polarsteps.com
|
10
|
-
headers = {
|
11
|
-
"Accept": "*/*",
|
12
|
-
"Accept-Encoding": "gzip, deflate, br, zstd",
|
13
|
-
"Accept-Language": "en-NL,en;q=0.9,nl-NL;q=0.8,nl;q=0.7,en-US;q=0.6",
|
14
|
-
"Connection": "keep-alive",
|
15
|
-
"Cookie": "", # Will be retrieved from environment variables
|
16
|
-
"Host": "www.polarsteps.com",
|
17
|
-
"Polarsteps-Api-Version": "13",
|
18
|
-
"Sec-Fetch-Dest": "empty",
|
19
|
-
"Sec-Fetch-Mode": "cors",
|
20
|
-
"Sec-Fetch-Site": "same-origin",
|
21
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 "
|
22
|
-
"Safari/537.36",
|
23
|
-
"sec-ch-ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
|
24
|
-
"sec-ch-ua-mobile": "?0",
|
25
|
-
"sec-ch-ua-platform": '"Windows"',
|
26
|
-
}
|
27
|
-
|
28
|
-
|
29
|
-
class StepCommentsEnricher:
|
30
|
-
"""Enriches steps with comments retrieved using the Polarsteps API."""
|
31
|
-
|
32
|
-
def __init__(self, path: Path) -> None:
|
33
|
-
self.comment_data_path = path / "comments.json"
|
34
|
-
headers["Cookie"] = os.getenv("COOKIE")
|
35
|
-
|
36
|
-
def enrich(self, trip: Trip) -> None:
|
37
|
-
"""Enrich trip data with comments.
|
38
|
-
|
39
|
-
Args:
|
40
|
-
----
|
41
|
-
trip: trip data
|
42
|
-
|
43
|
-
"""
|
44
|
-
comment_data = self.retrieve_comments(trip)
|
45
|
-
self.add_comments_to_steps(trip, comment_data)
|
46
|
-
|
47
|
-
def retrieve_comments(self, trip: Trip) -> dict:
|
48
|
-
"""Retrieve comments from Polarsteps API or local file storage.
|
49
|
-
|
50
|
-
Args:
|
51
|
-
----
|
52
|
-
trip: data of the trip
|
53
|
-
|
54
|
-
Returns:
|
55
|
-
-------
|
56
|
-
dict: comment data
|
57
|
-
|
58
|
-
"""
|
59
|
-
# Check if there is comment data and give the option to download/use existing data
|
60
|
-
if self.comment_data_path.exists():
|
61
|
-
comment_data = self.load_comments_from_file()
|
62
|
-
return comment_data
|
63
|
-
|
64
|
-
# Retrieve data from the API
|
65
|
-
comment_data = {"steps": []}
|
66
|
-
for step in trip.steps:
|
67
|
-
comments = self.get_comments_for_step(step.step_id)
|
68
|
-
comment_data["steps"].append({"id": step.step_id, "comments": comments["comments"]})
|
69
|
-
|
70
|
-
self.write_comments_to_file(comment_data)
|
71
|
-
|
72
|
-
return comment_data
|
73
|
-
|
74
|
-
def write_comments_to_file(self, comment_data: dict) -> None:
|
75
|
-
"""Write comments data to file.
|
76
|
-
|
77
|
-
Args:
|
78
|
-
----
|
79
|
-
comment_data: comment data retrieved from the API
|
80
|
-
|
81
|
-
"""
|
82
|
-
with open(self.comment_data_path, "w") as file:
|
83
|
-
json.dump(comment_data, file, indent=4)
|
84
|
-
|
85
|
-
def load_comments_from_file(self) -> dict:
|
86
|
-
"""Load comments from data file.
|
87
|
-
|
88
|
-
Returns:
|
89
|
-
-------
|
90
|
-
dict: comment data
|
91
|
-
|
92
|
-
"""
|
93
|
-
with open(self.comment_data_path, "r") as file:
|
94
|
-
return json.load(file)
|
95
|
-
|
96
|
-
@staticmethod
|
97
|
-
def get_comments_for_step(step_id: str) -> dict:
|
98
|
-
"""Retrieve all comments for a step.
|
99
|
-
|
100
|
-
Args:
|
101
|
-
----
|
102
|
-
step_id: id of the step (e.g. 82089888)
|
103
|
-
|
104
|
-
Returns:
|
105
|
-
-------
|
106
|
-
dict: response parsed to JSON
|
107
|
-
|
108
|
-
"""
|
109
|
-
url = f"https://www.polarsteps.com/api/social/steps/{step_id}/comments"
|
110
|
-
|
111
|
-
response = requests.get(url, headers=headers)
|
112
|
-
response.raise_for_status()
|
113
|
-
|
114
|
-
return response.json()
|
115
|
-
|
116
|
-
@staticmethod
|
117
|
-
def add_comments_to_steps(trip: Trip, comment_data: dict) -> Trip:
|
118
|
-
"""Parse the comment data to the model.
|
119
|
-
|
120
|
-
Args:
|
121
|
-
----
|
122
|
-
trip: trip data
|
123
|
-
comment_data: comment data
|
124
|
-
|
125
|
-
Returns:
|
126
|
-
-------
|
127
|
-
trip: trip data including comments
|
128
|
-
|
129
|
-
"""
|
130
|
-
for step, comments in zip(trip.steps, comment_data["steps"]):
|
131
|
-
if step.step_id != comments["id"]:
|
132
|
-
raise ValueError("Steps in trip and comment data are not in the same order.")
|
133
|
-
step.comments = [StepComment.from_json(c) for c in comments["comments"]]
|
134
|
-
|
135
|
-
return trip
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
import requests
|
6
|
+
|
7
|
+
from polarsteps_data_parser.model import Trip, StepComment
|
8
|
+
|
9
|
+
# Define the headers used for the request to polarsteps.com
|
10
|
+
headers = {
|
11
|
+
"Accept": "*/*",
|
12
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
13
|
+
"Accept-Language": "en-NL,en;q=0.9,nl-NL;q=0.8,nl;q=0.7,en-US;q=0.6",
|
14
|
+
"Connection": "keep-alive",
|
15
|
+
"Cookie": "", # Will be retrieved from environment variables
|
16
|
+
"Host": "www.polarsteps.com",
|
17
|
+
"Polarsteps-Api-Version": "13",
|
18
|
+
"Sec-Fetch-Dest": "empty",
|
19
|
+
"Sec-Fetch-Mode": "cors",
|
20
|
+
"Sec-Fetch-Site": "same-origin",
|
21
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 "
|
22
|
+
"Safari/537.36",
|
23
|
+
"sec-ch-ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
|
24
|
+
"sec-ch-ua-mobile": "?0",
|
25
|
+
"sec-ch-ua-platform": '"Windows"',
|
26
|
+
}
|
27
|
+
|
28
|
+
|
29
|
+
class StepCommentsEnricher:
|
30
|
+
"""Enriches steps with comments retrieved using the Polarsteps API."""
|
31
|
+
|
32
|
+
def __init__(self, path: Path) -> None:
|
33
|
+
self.comment_data_path = path / "comments.json"
|
34
|
+
headers["Cookie"] = os.getenv("COOKIE")
|
35
|
+
|
36
|
+
def enrich(self, trip: Trip) -> None:
|
37
|
+
"""Enrich trip data with comments.
|
38
|
+
|
39
|
+
Args:
|
40
|
+
----
|
41
|
+
trip: trip data
|
42
|
+
|
43
|
+
"""
|
44
|
+
comment_data = self.retrieve_comments(trip)
|
45
|
+
self.add_comments_to_steps(trip, comment_data)
|
46
|
+
|
47
|
+
def retrieve_comments(self, trip: Trip) -> dict:
|
48
|
+
"""Retrieve comments from Polarsteps API or local file storage.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
----
|
52
|
+
trip: data of the trip
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
-------
|
56
|
+
dict: comment data
|
57
|
+
|
58
|
+
"""
|
59
|
+
# Check if there is comment data and give the option to download/use existing data
|
60
|
+
if self.comment_data_path.exists():
|
61
|
+
comment_data = self.load_comments_from_file()
|
62
|
+
return comment_data
|
63
|
+
|
64
|
+
# Retrieve data from the API
|
65
|
+
comment_data = {"steps": []}
|
66
|
+
for step in trip.steps:
|
67
|
+
comments = self.get_comments_for_step(step.step_id)
|
68
|
+
comment_data["steps"].append({"id": step.step_id, "comments": comments["comments"]})
|
69
|
+
|
70
|
+
self.write_comments_to_file(comment_data)
|
71
|
+
|
72
|
+
return comment_data
|
73
|
+
|
74
|
+
def write_comments_to_file(self, comment_data: dict) -> None:
|
75
|
+
"""Write comments data to file.
|
76
|
+
|
77
|
+
Args:
|
78
|
+
----
|
79
|
+
comment_data: comment data retrieved from the API
|
80
|
+
|
81
|
+
"""
|
82
|
+
with open(self.comment_data_path, "w") as file:
|
83
|
+
json.dump(comment_data, file, indent=4)
|
84
|
+
|
85
|
+
def load_comments_from_file(self) -> dict:
|
86
|
+
"""Load comments from data file.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
-------
|
90
|
+
dict: comment data
|
91
|
+
|
92
|
+
"""
|
93
|
+
with open(self.comment_data_path, "r") as file:
|
94
|
+
return json.load(file)
|
95
|
+
|
96
|
+
@staticmethod
|
97
|
+
def get_comments_for_step(step_id: str) -> dict:
|
98
|
+
"""Retrieve all comments for a step.
|
99
|
+
|
100
|
+
Args:
|
101
|
+
----
|
102
|
+
step_id: id of the step (e.g. 82089888)
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
-------
|
106
|
+
dict: response parsed to JSON
|
107
|
+
|
108
|
+
"""
|
109
|
+
url = f"https://www.polarsteps.com/api/social/steps/{step_id}/comments"
|
110
|
+
|
111
|
+
response = requests.get(url, headers=headers)
|
112
|
+
response.raise_for_status()
|
113
|
+
|
114
|
+
return response.json()
|
115
|
+
|
116
|
+
@staticmethod
|
117
|
+
def add_comments_to_steps(trip: Trip, comment_data: dict) -> Trip:
|
118
|
+
"""Parse the comment data to the model.
|
119
|
+
|
120
|
+
Args:
|
121
|
+
----
|
122
|
+
trip: trip data
|
123
|
+
comment_data: comment data
|
124
|
+
|
125
|
+
Returns:
|
126
|
+
-------
|
127
|
+
trip: trip data including comments
|
128
|
+
|
129
|
+
"""
|
130
|
+
for step, comments in zip(trip.steps, comment_data["steps"]):
|
131
|
+
if step.step_id != comments["id"]:
|
132
|
+
raise ValueError("Steps in trip and comment data are not in the same order.")
|
133
|
+
step.comments = [StepComment.from_json(c) for c in comments["comments"]]
|
134
|
+
|
135
|
+
return trip
|