polarsteps-data-parser 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|