echr-extractor 1.0.44__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.
- echr_extractor/ECHR_html_downloader.py +80 -0
- echr_extractor/ECHR_metadata_harvester.py +390 -0
- echr_extractor/ECHR_nodes_edges_list_transform.py +312 -0
- echr_extractor/__init__.py +18 -0
- echr_extractor/clean_ref.py +8 -0
- echr_extractor/cli.py +132 -0
- echr_extractor/echr.py +159 -0
- echr_extractor/testing_file.py +16 -0
- echr_extractor-1.0.44.dist-info/METADATA +189 -0
- echr_extractor-1.0.44.dist-info/RECORD +14 -0
- echr_extractor-1.0.44.dist-info/WHEEL +5 -0
- echr_extractor-1.0.44.dist-info/entry_points.txt +2 -0
- echr_extractor-1.0.44.dist-info/licenses/LICENSE +201 -0
- echr_extractor-1.0.44.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
from bs4 import BeautifulSoup
|
|
5
|
+
|
|
6
|
+
base_url = 'https://hudoc.echr.coe.int/app/conversion/docx/html/body?library=ECHR&id='
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_full_text_from_html(html_text):
|
|
10
|
+
# This method turns the html code from the summary page into text
|
|
11
|
+
# It has different cases depending on the first character of the CELEX ID
|
|
12
|
+
# Should only be used for summaries extraction
|
|
13
|
+
soup = BeautifulSoup(html_text, "html.parser")
|
|
14
|
+
for script in soup(["script", "style"]):
|
|
15
|
+
script.extract() # rip it out
|
|
16
|
+
text = soup.get_text()
|
|
17
|
+
# break into lines and remove leading and trailing space on each
|
|
18
|
+
lines = (line.strip() for line in text.splitlines())
|
|
19
|
+
# break multi-headlines into a line each
|
|
20
|
+
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
|
|
21
|
+
# drop blank lines
|
|
22
|
+
text = '\n'.join(chunk for chunk in chunks if chunk)
|
|
23
|
+
text = text.replace(",", "_")
|
|
24
|
+
return text
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def download_full_text_main(df, threads):
|
|
28
|
+
item_ids = df['itemid']
|
|
29
|
+
eclis = df['ecli']
|
|
30
|
+
length = item_ids.size
|
|
31
|
+
if length > threads:
|
|
32
|
+
at_once_threads = int(length / threads)
|
|
33
|
+
else:
|
|
34
|
+
at_once_threads = length
|
|
35
|
+
all_dict = list()
|
|
36
|
+
threads = []
|
|
37
|
+
for i in range(0, length, at_once_threads):
|
|
38
|
+
curr_ids = item_ids[i:(i + at_once_threads)]
|
|
39
|
+
curr_ecli = eclis[i:(i + at_once_threads)]
|
|
40
|
+
t = threading.Thread(target=download_full_text_separate, args=(curr_ids, curr_ecli, all_dict))
|
|
41
|
+
threads.append(t)
|
|
42
|
+
for t in threads:
|
|
43
|
+
t.start()
|
|
44
|
+
for t in threads:
|
|
45
|
+
t.join()
|
|
46
|
+
|
|
47
|
+
json_file = list()
|
|
48
|
+
for l in all_dict:
|
|
49
|
+
if len(l) > 0:
|
|
50
|
+
json_file.extend(l)
|
|
51
|
+
return json_file
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def download_full_text_separate(item_ids, eclis, dict_list):
|
|
55
|
+
full_list = []
|
|
56
|
+
eclis = eclis.reset_index(drop=True)
|
|
57
|
+
item_ids = item_ids.reset_index(drop=True)
|
|
58
|
+
|
|
59
|
+
def download_html(item_ids, eclis):
|
|
60
|
+
retry_ids = []
|
|
61
|
+
retry_eclis = []
|
|
62
|
+
for i in range(len(item_ids)):
|
|
63
|
+
item_id = item_ids[i]
|
|
64
|
+
ecli = eclis[i]
|
|
65
|
+
try:
|
|
66
|
+
r = requests.get(base_url + item_id, timeout=1)
|
|
67
|
+
json_dict = {
|
|
68
|
+
'item_id': item_id,
|
|
69
|
+
'ecli': ecli,
|
|
70
|
+
'full_text': get_full_text_from_html(r.text)
|
|
71
|
+
}
|
|
72
|
+
full_list.append(json_dict)
|
|
73
|
+
except Exception:
|
|
74
|
+
retry_ids.append(item_id)
|
|
75
|
+
retry_eclis.append(ecli)
|
|
76
|
+
return retry_ids, retry_eclis
|
|
77
|
+
|
|
78
|
+
retry_ids, retry_eclis = download_html(item_ids, eclis)
|
|
79
|
+
download_html(retry_ids, retry_eclis)
|
|
80
|
+
dict_list.append(full_list)
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_r(url, timeout, retry, verbose):
|
|
9
|
+
"""
|
|
10
|
+
Get data from a URL. If this is uncuccessful it is attempted again
|
|
11
|
+
up to a number of tries
|
|
12
|
+
given by retry. If it is still unsuccessful the batch is skipped.
|
|
13
|
+
:param str url: The data source URL.
|
|
14
|
+
:param double timeout: The amount of time to wait for a response
|
|
15
|
+
each attempt.
|
|
16
|
+
:param int retry: The number of times to retry upon failure.
|
|
17
|
+
:param bool verbose: Whether or not to print extra information.
|
|
18
|
+
"""
|
|
19
|
+
count = 0
|
|
20
|
+
max_attempts = 20
|
|
21
|
+
while count < max_attempts:
|
|
22
|
+
try:
|
|
23
|
+
r = requests.get(url, timeout=timeout)
|
|
24
|
+
return r
|
|
25
|
+
except (requests.exceptions.ReadTimeout,
|
|
26
|
+
requests.exceptions.ConnectTimeout):
|
|
27
|
+
count += 1
|
|
28
|
+
if verbose:
|
|
29
|
+
logging.info(f"Timeout. Retry attempt {count}.")
|
|
30
|
+
if count > retry:
|
|
31
|
+
if verbose:
|
|
32
|
+
logging.info(f"Unable to connect to {url}."
|
|
33
|
+
f"Skipping this batch.")
|
|
34
|
+
return None
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def basic_function(term, values):
|
|
39
|
+
values = ['"' + i + '"' for i in values]
|
|
40
|
+
main_body = list()
|
|
41
|
+
cut_term = term.replace('"', "")
|
|
42
|
+
for v in values:
|
|
43
|
+
main_body.append(f"({cut_term}={v}) OR ({cut_term}:{v})")
|
|
44
|
+
query = f"({' OR '.join(main_body)})"
|
|
45
|
+
return query
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def link_to_query(link):
|
|
49
|
+
# Fixing brackets
|
|
50
|
+
link = link.replace("%7B", "{")
|
|
51
|
+
link = link.replace("%7D", "}")
|
|
52
|
+
link = link.replace("%5B", "[")
|
|
53
|
+
link = link.replace("%5D", "]")
|
|
54
|
+
link = link.replace("%22", '"')
|
|
55
|
+
link = link.replace("%27", "'")
|
|
56
|
+
|
|
57
|
+
# fixing fulltext shenanigans - happen because of people using "
|
|
58
|
+
# in the queries.
|
|
59
|
+
|
|
60
|
+
full_text_input = ""
|
|
61
|
+
fulltext_end = -1
|
|
62
|
+
fulltext_start = link.find("fulltext")
|
|
63
|
+
if fulltext_start:
|
|
64
|
+
start = link[fulltext_start:].find("[") + fulltext_start + 1
|
|
65
|
+
fulltext_end = link[fulltext_start:].find("]") + fulltext_start
|
|
66
|
+
fragment_to_fix = link[start:fulltext_end]
|
|
67
|
+
full_text_input = (
|
|
68
|
+
"("
|
|
69
|
+
+ "".join(fragment_to_fix.rsplit('"', 1)).replace('"', "", 1)
|
|
70
|
+
+ ")".replace("\\", "")
|
|
71
|
+
)
|
|
72
|
+
full_text_input = full_text_input.replace("\\", "")
|
|
73
|
+
b = 2
|
|
74
|
+
# removing first and last " elements and saving the output to
|
|
75
|
+
# put manually later
|
|
76
|
+
if fulltext_end:
|
|
77
|
+
|
|
78
|
+
if link[fulltext_end + 1] == ",":
|
|
79
|
+
to_replace = link[fulltext_start - 1 : fulltext_end + 2]
|
|
80
|
+
else:
|
|
81
|
+
to_replace = link[fulltext_start - 1 : fulltext_end + 1]
|
|
82
|
+
link = link.replace(to_replace, "")
|
|
83
|
+
|
|
84
|
+
extra_cases_map = {
|
|
85
|
+
"bodyprocedure": '("PROCEDURE" ONEAR(n=1000) terms OR "PROCÉDURE" ONEAR(n=1000) terms)',
|
|
86
|
+
"bodyfacts": '("THE FACTS" ONEAR(n=1000) terms OR "EN FAIT" ONEAR(n=1000) terms)',
|
|
87
|
+
"bodycomplaints": '("COMPLAINTS" ONEAR(n=1000) terms OR "GRIEFS" ONEAR(n=1000) terms)',
|
|
88
|
+
"bodylaw": '("THE LAW" ONEAR(n=1000) terms OR "EN DROIT" ONEAR(n=1000) terms)',
|
|
89
|
+
"bodyreasons": '("FOR THESE REASONS" ONEAR(n=1000) terms OR "PAR CES MOTIFS" ONEAR(n=1000) terms)',
|
|
90
|
+
"bodyseparateopinions": '(("SEPARATE OPINION" OR "SEPARATE OPINIONS") ONEAR(n=5000) terms OR "OPINION '
|
|
91
|
+
'SÉPARÉE" ONEAR(n=5000) terms)',
|
|
92
|
+
"bodyappendix": '("APPENDIX" ONEAR(n=1000) terms OR "ANNEXE" ONEAR(n=1000) terms)',
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
def full_text_function(term, values):
|
|
96
|
+
return f"({','.join(values)})"
|
|
97
|
+
|
|
98
|
+
def date_function(term, values):
|
|
99
|
+
values = ['"' + i + '"' for i in values]
|
|
100
|
+
query = "(kpdate>=first_term AND kpdate<=second_term)"
|
|
101
|
+
first = values[0]
|
|
102
|
+
second = values[1]
|
|
103
|
+
if first == '""':
|
|
104
|
+
first = '"1900-01-01"'
|
|
105
|
+
if second == '""':
|
|
106
|
+
second = datetime.today().date()
|
|
107
|
+
query = query.replace("first_term", first)
|
|
108
|
+
query = query.replace("second_term", second)
|
|
109
|
+
return query
|
|
110
|
+
|
|
111
|
+
def advanced_function(term, values):
|
|
112
|
+
body = extra_cases_map.get(term)
|
|
113
|
+
query = body.replace("terms", ",".join(vals))
|
|
114
|
+
return query
|
|
115
|
+
|
|
116
|
+
query_map = {
|
|
117
|
+
"docname": basic_function,
|
|
118
|
+
"appno": basic_function,
|
|
119
|
+
"scl": basic_function,
|
|
120
|
+
"rulesofcourt": basic_function,
|
|
121
|
+
"applicability": basic_function,
|
|
122
|
+
"ecli": basic_function,
|
|
123
|
+
"conclusion": basic_function,
|
|
124
|
+
"resolutionnumber": basic_function,
|
|
125
|
+
"separateopinions": basic_function,
|
|
126
|
+
"externalsources": basic_function,
|
|
127
|
+
"kpthesaurus": basic_function,
|
|
128
|
+
"advopidentifier": basic_function,
|
|
129
|
+
"documentcollectionid2": basic_function,
|
|
130
|
+
"fulltext": full_text_function,
|
|
131
|
+
"kpdate": date_function,
|
|
132
|
+
"bodyprocedure": advanced_function,
|
|
133
|
+
"bodyfacts": advanced_function,
|
|
134
|
+
"bodycomplaints": advanced_function,
|
|
135
|
+
"bodylaw": advanced_function,
|
|
136
|
+
"bodyreasons": advanced_function,
|
|
137
|
+
"bodyseparateopinions": advanced_function,
|
|
138
|
+
"bodyappendix": advanced_function,
|
|
139
|
+
"languageisocode": basic_function,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
start = link.index("{")
|
|
143
|
+
end = link.rindex("}")
|
|
144
|
+
json_str = link[start : end + 1].replace("'", '"')
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
link_dictionary = json.loads(json_str)
|
|
148
|
+
except json.JSONDecodeError:
|
|
149
|
+
|
|
150
|
+
print(f"Failed to parse JSON: {json_str}")
|
|
151
|
+
link_dictionary = {}
|
|
152
|
+
pairs = json_str.strip("{}").split(",")
|
|
153
|
+
for pair in pairs:
|
|
154
|
+
key, value = pair.split(":", 1)
|
|
155
|
+
key = key.strip().strip('"')
|
|
156
|
+
value = value.strip().strip("[]").split(",")
|
|
157
|
+
link_dictionary[key] = [v.strip().strip('"') for v in value]
|
|
158
|
+
|
|
159
|
+
base_query = (
|
|
160
|
+
"https://hudoc.echr.coe.int/app/query/results?query=contentsitename:ECHR"
|
|
161
|
+
" AND (NOT (doctype=PR OR doctype=HFCOMOLD OR doctype=HECOMOLD)) AND "
|
|
162
|
+
"inPutter&select={select}&sort=itemid%20Ascending&start={start}&length={length}"
|
|
163
|
+
)
|
|
164
|
+
query_elements = list()
|
|
165
|
+
if full_text_input:
|
|
166
|
+
query_elements.append(full_text_input)
|
|
167
|
+
date_addition = ""
|
|
168
|
+
for key in list(link_dictionary.keys()):
|
|
169
|
+
if key == "kpdate":
|
|
170
|
+
vals = link_dictionary.get(key)
|
|
171
|
+
funct = query_map.get(key)
|
|
172
|
+
date_addition = funct(key, vals)
|
|
173
|
+
elif key == "sort":
|
|
174
|
+
continue
|
|
175
|
+
else:
|
|
176
|
+
vals = link_dictionary.get(key)
|
|
177
|
+
funct = query_map.get(key)
|
|
178
|
+
query_elements.append(funct(key, vals))
|
|
179
|
+
if date_addition:
|
|
180
|
+
query_elements.append(date_addition)
|
|
181
|
+
query_total = " AND ".join(query_elements)
|
|
182
|
+
final_query = base_query.replace("inPutter", query_total)
|
|
183
|
+
|
|
184
|
+
return final_query
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def determine_meta_url(link, query_payload, start_date, end_date):
|
|
188
|
+
if query_payload:
|
|
189
|
+
META_URL = (
|
|
190
|
+
"http://hudoc.echr.coe.int/app/query/results"
|
|
191
|
+
f"?query={query_payload}"
|
|
192
|
+
"&select={select}"
|
|
193
|
+
+ "&sort=itemid Ascending"
|
|
194
|
+
+ "&start={start}&length={length}"
|
|
195
|
+
)
|
|
196
|
+
elif link:
|
|
197
|
+
META_URL = link_to_query(link)
|
|
198
|
+
else:
|
|
199
|
+
META_URL = (
|
|
200
|
+
"http://hudoc.echr.coe.int/app/query/results"
|
|
201
|
+
"?query=(contentsitename=ECHR) AND "
|
|
202
|
+
'(documentcollectionid2:"JUDGMENTS" OR '
|
|
203
|
+
'documentcollectionid2:"COMMUNICATEDCASES" OR '
|
|
204
|
+
'documentcollectionid2:"DECISIONS" OR '
|
|
205
|
+
'documentcollectionid2:"CLIN") AND '
|
|
206
|
+
"lang_inputter"
|
|
207
|
+
"&select={select}"
|
|
208
|
+
+ "&sort=itemid Ascending"
|
|
209
|
+
+ "&start={start}&length={length}"
|
|
210
|
+
)
|
|
211
|
+
if start_date and end_date:
|
|
212
|
+
addition = f'(kpdate>="{start_date}" AND kpdate<="{end_date}")'
|
|
213
|
+
elif start_date:
|
|
214
|
+
end_date = datetime.today().date()
|
|
215
|
+
addition = f'(kpdate>="{start_date}" AND kpdate<="{end_date}")'
|
|
216
|
+
elif end_date:
|
|
217
|
+
start_date = "1900-01-01"
|
|
218
|
+
addition = f'(kpdate>="{start_date}" AND kpdate<="{end_date}")'
|
|
219
|
+
else:
|
|
220
|
+
addition = ""
|
|
221
|
+
|
|
222
|
+
if addition:
|
|
223
|
+
addition = " AND " + addition
|
|
224
|
+
META_URL = META_URL.replace("&select", addition + "&select")
|
|
225
|
+
return META_URL
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def get_echr_metadata(
|
|
229
|
+
start_id,
|
|
230
|
+
end_id,
|
|
231
|
+
verbose,
|
|
232
|
+
fields,
|
|
233
|
+
start_date,
|
|
234
|
+
end_date,
|
|
235
|
+
link,
|
|
236
|
+
language,
|
|
237
|
+
query_payload,
|
|
238
|
+
):
|
|
239
|
+
"""
|
|
240
|
+
Read ECHR metadata into a Pandas DataFrame.
|
|
241
|
+
:param int start_id: The index to start the search from.
|
|
242
|
+
:param int end_id: The index to end search at, where the default
|
|
243
|
+
fetches all results.
|
|
244
|
+
:param date start_date: The point from which to save cases.
|
|
245
|
+
:param date end_date: The point before which to save cases.
|
|
246
|
+
:param bool verbose: Whether or not to print extra information.
|
|
247
|
+
"""
|
|
248
|
+
data = []
|
|
249
|
+
if not fields:
|
|
250
|
+
fields = [
|
|
251
|
+
"itemid",
|
|
252
|
+
"applicability",
|
|
253
|
+
"appno",
|
|
254
|
+
"article",
|
|
255
|
+
"conclusion",
|
|
256
|
+
"docname",
|
|
257
|
+
"doctype",
|
|
258
|
+
"doctypebranch",
|
|
259
|
+
"ecli",
|
|
260
|
+
"importance",
|
|
261
|
+
"judgementdate",
|
|
262
|
+
"languageisocode",
|
|
263
|
+
"originatingbody",
|
|
264
|
+
"violation",
|
|
265
|
+
"nonviolation",
|
|
266
|
+
"extractedappno",
|
|
267
|
+
"scl",
|
|
268
|
+
"publishedby",
|
|
269
|
+
"representedby",
|
|
270
|
+
"respondent",
|
|
271
|
+
"separateopinion",
|
|
272
|
+
"sharepointid",
|
|
273
|
+
"externalsources",
|
|
274
|
+
"issue",
|
|
275
|
+
"referencedate",
|
|
276
|
+
"rulesofcourt",
|
|
277
|
+
"DocId",
|
|
278
|
+
"WorkId",
|
|
279
|
+
"Rank",
|
|
280
|
+
"Author",
|
|
281
|
+
"Size",
|
|
282
|
+
"Path",
|
|
283
|
+
"Description",
|
|
284
|
+
"Write",
|
|
285
|
+
"CollapsingStatus",
|
|
286
|
+
"HighlightedSummary",
|
|
287
|
+
"HighlightedProperties",
|
|
288
|
+
"contentclass",
|
|
289
|
+
"PictureThumbnailURL",
|
|
290
|
+
"ServerRedirectedURL",
|
|
291
|
+
"ServerRedirectedEmbedURL",
|
|
292
|
+
"ServerRedirectedPreviewURL",
|
|
293
|
+
"FileExtension",
|
|
294
|
+
"ContentTypeId",
|
|
295
|
+
"ParentLink",
|
|
296
|
+
"ViewsLifeTime",
|
|
297
|
+
"ViewsRecent",
|
|
298
|
+
"SectionNames",
|
|
299
|
+
"SectionIndexes",
|
|
300
|
+
"SiteLogo",
|
|
301
|
+
"SiteDescription",
|
|
302
|
+
"deeplinks",
|
|
303
|
+
"SiteName",
|
|
304
|
+
"IsDocument",
|
|
305
|
+
"LastModifiedTime",
|
|
306
|
+
"FileType",
|
|
307
|
+
"IsContainer",
|
|
308
|
+
"WebTemplate",
|
|
309
|
+
"SecondaryFileExtension",
|
|
310
|
+
"docaclmeta",
|
|
311
|
+
"OriginalPath",
|
|
312
|
+
"EditorOWSUSER",
|
|
313
|
+
"DisplayAuthor",
|
|
314
|
+
"ResultTypeIdList",
|
|
315
|
+
"PartitionId",
|
|
316
|
+
"UrlZone",
|
|
317
|
+
"AAMEnabledManagedProperties",
|
|
318
|
+
"ResultTypeId",
|
|
319
|
+
"rendertemplateid",
|
|
320
|
+
]
|
|
321
|
+
|
|
322
|
+
META_URL = determine_meta_url(link, query_payload, start_date, end_date)
|
|
323
|
+
# An example url: "https://hudoc.echr.coe.int/app/query/results?query=(contentsitename=ECHR)%20AND%20(documentcollectionid2:%22JUDGMENTS%22%20OR%20documentcollectionid2:%22COMMUNICATEDCASES%22%20OR%20documentcollectionid2:%22DECISIONS%22%20OR%20documentcollectionid2:%22CLIN%22)&select=itemid,applicability,application,appno,article,conclusion,decisiondate,docname,documentcollectionid,%20documentcollectionid2,doctype,doctypebranch,ecli,externalsources,extractedappno,importance,introductiondate,%20isplaceholder,issue,judgementdate,kpdate,kpdateAsText,kpthesaurus,languageisocode,meetingnumber,%20originatingbody,publishedby,Rank,referencedate,reportdate,representedby,resolutiondate,%20resolutionnumber,respondent,respondentOrderEng,rulesofcourt,separateopinion,scl,sharepointid,typedescription,%20nonviolation,violation&sort=itemid%20Ascending&start=0&length=200"
|
|
324
|
+
|
|
325
|
+
META_URL = META_URL.replace(" ", "%20")
|
|
326
|
+
META_URL = META_URL.replace('"', "%22")
|
|
327
|
+
META_URL = META_URL.replace("%5C", "")
|
|
328
|
+
|
|
329
|
+
language_input = basic_function("languageisocode", language)
|
|
330
|
+
if not link:
|
|
331
|
+
META_URL = META_URL.replace("lang_inputter", language_input)
|
|
332
|
+
|
|
333
|
+
META_URL = META_URL.replace("{select}", ",".join(fields))
|
|
334
|
+
|
|
335
|
+
url = META_URL.format(start=0, length=1)
|
|
336
|
+
logging.info(url)
|
|
337
|
+
r = requests.get(url)
|
|
338
|
+
resultcount = r.json()["resultcount"]
|
|
339
|
+
logging.info("available results: " + str(resultcount))
|
|
340
|
+
|
|
341
|
+
if not end_id:
|
|
342
|
+
end_id = resultcount
|
|
343
|
+
if verbose:
|
|
344
|
+
logging.info(
|
|
345
|
+
f"Fetching {end_id - start_id} results from index {start_id} to index {end_id} "
|
|
346
|
+
+ f'{f" and filtering cases after {start_date}" if start_date and not link and not query_payload else ""} {f"and filtering cases before {end_date}" if end_date and not link and not query_payload else "."}'
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
timeout = 60
|
|
350
|
+
retry = 3
|
|
351
|
+
if (
|
|
352
|
+
start_id + end_id > 500
|
|
353
|
+
): # HUDOC does not let you fetch more than 500 items in one go.
|
|
354
|
+
for i in range(start_id, end_id, 500):
|
|
355
|
+
if verbose:
|
|
356
|
+
logging.info(
|
|
357
|
+
" - Fetching information from cases {} to {}.".format(i, i + 500)
|
|
358
|
+
)
|
|
359
|
+
# Format URL based on the incremented index.
|
|
360
|
+
url = META_URL.format(start=i, length=500)
|
|
361
|
+
if verbose:
|
|
362
|
+
logging.info(url)
|
|
363
|
+
|
|
364
|
+
# Get the response.
|
|
365
|
+
r = get_r(url, timeout, retry, verbose)
|
|
366
|
+
if r is not None:
|
|
367
|
+
# Get the results list
|
|
368
|
+
temp_dict = r.json()["results"]
|
|
369
|
+
# Get every document from the results list.
|
|
370
|
+
for result in temp_dict:
|
|
371
|
+
data.append(result["columns"])
|
|
372
|
+
|
|
373
|
+
else:
|
|
374
|
+
# Format URL based on start and length
|
|
375
|
+
url = META_URL.format(start=start_id, length=end_id)
|
|
376
|
+
if verbose:
|
|
377
|
+
logging.info(url)
|
|
378
|
+
|
|
379
|
+
r = get_r(url, timeout, retry, verbose)
|
|
380
|
+
if r is not None:
|
|
381
|
+
# Get the results list
|
|
382
|
+
temp_dict = r.json()["results"]
|
|
383
|
+
# Get every document from the results list.
|
|
384
|
+
for result in temp_dict:
|
|
385
|
+
data.append(result["columns"])
|
|
386
|
+
|
|
387
|
+
if len(data) == 0:
|
|
388
|
+
logging.info("Search results ended up empty")
|
|
389
|
+
return False
|
|
390
|
+
return pd.DataFrame.from_records(data)
|