ilpost-api-wrapper 0.1.0__tar.gz → 0.3.0__tar.gz
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.
- {ilpost_api_wrapper-0.1.0/ilpost_api_wrapper.egg-info → ilpost_api_wrapper-0.3.0}/PKG-INFO +7 -7
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/README.md +6 -6
- ilpost_api_wrapper-0.3.0/ilpost/cli.py +164 -0
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/ilpost/client.py +27 -12
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0/ilpost_api_wrapper.egg-info}/PKG-INFO +7 -7
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/ilpost_api_wrapper.egg-info/SOURCES.txt +2 -0
- ilpost_api_wrapper-0.3.0/ilpost_api_wrapper.egg-info/entry_points.txt +2 -0
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/pyproject.toml +4 -1
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/LICENSE +0 -0
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/ilpost/__init__.py +0 -0
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/ilpost/models.py +0 -0
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/ilpost/py.typed +0 -0
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/ilpost_api_wrapper.egg-info/dependency_links.txt +0 -0
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/ilpost_api_wrapper.egg-info/top_level.txt +0 -0
- {ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ilpost-api-wrapper
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Python wrapper for Il Post newspaper API
|
|
5
5
|
Author: Antonio Girasella
|
|
6
6
|
License-Expression: MIT
|
|
@@ -166,7 +166,7 @@ for group in result.filters:
|
|
|
166
166
|
|
|
167
167
|
## CLI
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
The `ilpost-search` command is included with the package:
|
|
170
170
|
|
|
171
171
|
```
|
|
172
172
|
usage: ilpost-search [-h] [--type {articles,podcasts,newsletters}]
|
|
@@ -179,19 +179,19 @@ usage: ilpost-search [-h] [--type {articles,podcasts,newsletters}]
|
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
181
|
# Basic search
|
|
182
|
-
|
|
182
|
+
ilpost-search berlusconi
|
|
183
183
|
|
|
184
184
|
# Most recent articles in politics
|
|
185
|
-
|
|
185
|
+
ilpost-search renzi --type articles --sort newest --category politica
|
|
186
186
|
|
|
187
187
|
# Podcast search, past 30 days
|
|
188
|
-
|
|
188
|
+
ilpost-search cacao --type podcasts --date month
|
|
189
189
|
|
|
190
190
|
# Page 2, 5 results per page, oldest first
|
|
191
|
-
|
|
191
|
+
ilpost-search sicilia --sort oldest --hits 5 --page 2
|
|
192
192
|
|
|
193
193
|
# Fetch all pages of newsletter results (up to 3 pages)
|
|
194
|
-
|
|
194
|
+
ilpost-search economia --type newsletters --all-pages --max-pages 3
|
|
195
195
|
```
|
|
196
196
|
|
|
197
197
|
## Notes
|
|
@@ -140,7 +140,7 @@ for group in result.filters:
|
|
|
140
140
|
|
|
141
141
|
## CLI
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
The `ilpost-search` command is included with the package:
|
|
144
144
|
|
|
145
145
|
```
|
|
146
146
|
usage: ilpost-search [-h] [--type {articles,podcasts,newsletters}]
|
|
@@ -153,19 +153,19 @@ usage: ilpost-search [-h] [--type {articles,podcasts,newsletters}]
|
|
|
153
153
|
|
|
154
154
|
```bash
|
|
155
155
|
# Basic search
|
|
156
|
-
|
|
156
|
+
ilpost-search berlusconi
|
|
157
157
|
|
|
158
158
|
# Most recent articles in politics
|
|
159
|
-
|
|
159
|
+
ilpost-search renzi --type articles --sort newest --category politica
|
|
160
160
|
|
|
161
161
|
# Podcast search, past 30 days
|
|
162
|
-
|
|
162
|
+
ilpost-search cacao --type podcasts --date month
|
|
163
163
|
|
|
164
164
|
# Page 2, 5 results per page, oldest first
|
|
165
|
-
|
|
165
|
+
ilpost-search sicilia --sort oldest --hits 5 --page 2
|
|
166
166
|
|
|
167
167
|
# Fetch all pages of newsletter results (up to 3 pages)
|
|
168
|
-
|
|
168
|
+
ilpost-search economia --type newsletters --all-pages --max-pages 3
|
|
169
169
|
```
|
|
170
170
|
|
|
171
171
|
## Notes
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from .client import IlPostClient
|
|
5
|
+
from .models import SortOrder, ContentType, DateRange
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
9
|
+
parser = argparse.ArgumentParser(
|
|
10
|
+
prog="ilpost-search",
|
|
11
|
+
description="Search Il Post articles, podcasts, and newsletters.",
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
parser.add_argument("query", help="Search term")
|
|
15
|
+
|
|
16
|
+
parser.add_argument(
|
|
17
|
+
"--type", "-t",
|
|
18
|
+
dest="content_type",
|
|
19
|
+
choices=["articles", "podcasts", "newsletters"],
|
|
20
|
+
default=None,
|
|
21
|
+
help="Filter by content type (default: all)",
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"--sort", "-s",
|
|
25
|
+
choices=["relevance", "newest", "oldest"],
|
|
26
|
+
default="relevance",
|
|
27
|
+
help="Sort order (default: relevance)",
|
|
28
|
+
)
|
|
29
|
+
parser.add_argument(
|
|
30
|
+
"--date", "-d",
|
|
31
|
+
dest="date_range",
|
|
32
|
+
choices=["all", "year", "month"],
|
|
33
|
+
default=None,
|
|
34
|
+
help="Publication date filter: all / year (past 12 months) / month (past 30 days)",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--category", "-c",
|
|
38
|
+
default=None,
|
|
39
|
+
metavar="CATEGORY",
|
|
40
|
+
help="Editorial category, e.g. politica, cultura, economia (articles only)",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--page", "-p",
|
|
44
|
+
type=int,
|
|
45
|
+
default=1,
|
|
46
|
+
help="Page number, 1-based (default: 1)",
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--hits", "-n",
|
|
50
|
+
type=int,
|
|
51
|
+
default=10,
|
|
52
|
+
help="Results per page (default: 10)",
|
|
53
|
+
)
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"--all-pages",
|
|
56
|
+
action="store_true",
|
|
57
|
+
help="Fetch and print all pages (ignores --page)",
|
|
58
|
+
)
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
"--max-pages",
|
|
61
|
+
type=int,
|
|
62
|
+
default=None,
|
|
63
|
+
metavar="N",
|
|
64
|
+
help="Maximum number of pages to fetch when using --all-pages",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return parser
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
_SORT_MAP = {
|
|
71
|
+
"relevance": SortOrder.RELEVANCE,
|
|
72
|
+
"newest": SortOrder.NEWEST,
|
|
73
|
+
"oldest": SortOrder.OLDEST,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
_CTYPE_MAP = {
|
|
77
|
+
"articles": ContentType.ARTICLES,
|
|
78
|
+
"podcasts": ContentType.PODCASTS,
|
|
79
|
+
"newsletters": ContentType.NEWSLETTERS,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_DATE_MAP = {
|
|
83
|
+
"all": DateRange.ALL_TIME,
|
|
84
|
+
"year": DateRange.PAST_YEAR,
|
|
85
|
+
"month": DateRange.PAST_30_DAYS,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
_TYPE_LABEL = {
|
|
89
|
+
"post": "article",
|
|
90
|
+
"episodes": "podcast",
|
|
91
|
+
"newsletter": "newsletter",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def print_result(result, *, show_header: bool = True) -> None:
|
|
96
|
+
if show_header:
|
|
97
|
+
print(f'\nQuery: "{result.query}" | '
|
|
98
|
+
f"Total: {result.total} | "
|
|
99
|
+
f"Page: {result.page}/{result.total_pages} | "
|
|
100
|
+
f"Sort: {result.sort}")
|
|
101
|
+
print("-" * 72)
|
|
102
|
+
|
|
103
|
+
if not result.docs:
|
|
104
|
+
print("No results.")
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
for doc in result.docs:
|
|
108
|
+
label = _TYPE_LABEL.get(doc.type, doc.type)
|
|
109
|
+
paywall = " [subscribers only]" if doc.is_paywalled else ""
|
|
110
|
+
score = f" score={doc.score:.2f}" if doc.score else ""
|
|
111
|
+
category = f" [{doc.category}]" if doc.category else ""
|
|
112
|
+
print(f"[{label}]{category}{paywall}{score}")
|
|
113
|
+
print(f" {doc.title}")
|
|
114
|
+
print(f" {doc.link}")
|
|
115
|
+
if doc.highlight:
|
|
116
|
+
# strip HTML span tags for plain-text display
|
|
117
|
+
snippet = doc.highlight.replace("<span>", ">>").replace("</span>", "<<")
|
|
118
|
+
print(f" ...{snippet}...")
|
|
119
|
+
print()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def main() -> None:
|
|
123
|
+
parser = build_parser()
|
|
124
|
+
args = parser.parse_args()
|
|
125
|
+
|
|
126
|
+
sort = _SORT_MAP[args.sort]
|
|
127
|
+
content_type = _CTYPE_MAP.get(args.content_type)
|
|
128
|
+
date_range = _DATE_MAP.get(args.date_range)
|
|
129
|
+
|
|
130
|
+
client = IlPostClient()
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
if args.all_pages:
|
|
134
|
+
first = True
|
|
135
|
+
for page_result in client.paginate(
|
|
136
|
+
args.query,
|
|
137
|
+
hits=args.hits,
|
|
138
|
+
sort=sort,
|
|
139
|
+
content_type=content_type,
|
|
140
|
+
category=args.category,
|
|
141
|
+
date_range=date_range,
|
|
142
|
+
max_pages=args.max_pages,
|
|
143
|
+
):
|
|
144
|
+
print_result(page_result, show_header=first)
|
|
145
|
+
first = False
|
|
146
|
+
else:
|
|
147
|
+
result = client.search(
|
|
148
|
+
args.query,
|
|
149
|
+
page=args.page,
|
|
150
|
+
hits=args.hits,
|
|
151
|
+
sort=sort,
|
|
152
|
+
content_type=content_type,
|
|
153
|
+
category=args.category,
|
|
154
|
+
date_range=date_range,
|
|
155
|
+
)
|
|
156
|
+
print_result(result)
|
|
157
|
+
|
|
158
|
+
except Exception as exc:
|
|
159
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
160
|
+
sys.exit(1)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
main()
|
|
@@ -13,17 +13,19 @@ _BASE_URL = "https://api.ilpost.org/search/api/site_search/"
|
|
|
13
13
|
|
|
14
14
|
def _build_filters(
|
|
15
15
|
content_type: Optional[ContentType] = None,
|
|
16
|
-
category: Optional[str] = None,
|
|
16
|
+
category: Optional[Union[str, list[str]]] = None,
|
|
17
17
|
date_range: Optional[DateRange] = None,
|
|
18
18
|
) -> str:
|
|
19
19
|
parts: list[str] = []
|
|
20
20
|
if content_type is not None:
|
|
21
21
|
parts.append(f"ctype:{content_type.value}")
|
|
22
22
|
if category is not None:
|
|
23
|
-
|
|
23
|
+
cats = [category] if isinstance(category, str) else category
|
|
24
|
+
for cat in cats:
|
|
25
|
+
parts.append(f"category:{cat}")
|
|
24
26
|
if date_range is not None:
|
|
25
27
|
parts.append(f"pub_date:{date_range.value}")
|
|
26
|
-
return "
|
|
28
|
+
return ";".join(parts)
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class IlPostClient:
|
|
@@ -52,7 +54,7 @@ class IlPostClient:
|
|
|
52
54
|
hits: int = 10,
|
|
53
55
|
sort: Union[SortOrder, str] = SortOrder.RELEVANCE,
|
|
54
56
|
content_type: Optional[ContentType] = None,
|
|
55
|
-
category: Optional[str] = None,
|
|
57
|
+
category: Optional[Union[str, list[str]]] = None,
|
|
56
58
|
date_range: Optional[DateRange] = None,
|
|
57
59
|
filters: Optional[str] = None,
|
|
58
60
|
) -> SearchResult:
|
|
@@ -61,7 +63,18 @@ class IlPostClient:
|
|
|
61
63
|
Parameters
|
|
62
64
|
----------
|
|
63
65
|
query:
|
|
64
|
-
Search term.
|
|
66
|
+
Search term. Supports:
|
|
67
|
+
|
|
68
|
+
- Exact phrase: ``'"goffredo fofi"'``
|
|
69
|
+
- Boolean OR: ``"fofi | berlusconi"`` (``|`` and ``OR`` both work)
|
|
70
|
+
- Boolean AND: ``"fofi AND berlusconi"`` or just ``"fofi berlusconi"``
|
|
71
|
+
- Boolean NOT: ``"berlusconi NOT fininvest"``
|
|
72
|
+
|
|
73
|
+
The following syntax does **not** work and should be avoided:
|
|
74
|
+
|
|
75
|
+
- Field prefix (``title:fofi``, ``content:fofi``) — treated as literal tokens
|
|
76
|
+
- Boost operator (``berlusconi^10``) — the numeric value becomes a search token
|
|
77
|
+
- Proximity queries (``"goffredo fofi"~5``) — inflates results unpredictably
|
|
65
78
|
page:
|
|
66
79
|
1-based page number (default: 1).
|
|
67
80
|
hits:
|
|
@@ -73,15 +86,17 @@ class IlPostClient:
|
|
|
73
86
|
Filter by content type: ``ContentType.ARTICLES``, ``ContentType.PODCASTS``,
|
|
74
87
|
or ``ContentType.NEWSLETTERS``.
|
|
75
88
|
category:
|
|
76
|
-
Filter articles by editorial category
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
Filter articles by editorial category. Pass a single string
|
|
90
|
+
(e.g. ``"politica"``) or a list to AND multiple categories together
|
|
91
|
+
(e.g. ``["cultura", "libri"]``). Only meaningful when
|
|
92
|
+
``content_type=ContentType.ARTICLES`` or no content type filter is set.
|
|
79
93
|
date_range:
|
|
80
94
|
Filter by publication date: ``DateRange.ALL_TIME``, ``DateRange.PAST_YEAR``,
|
|
81
95
|
or ``DateRange.PAST_30_DAYS``.
|
|
82
96
|
filters:
|
|
83
|
-
Raw pre-encoded filter string (e.g. ``"ctype:articoli
|
|
84
|
-
When provided, overrides ``content_type``,
|
|
97
|
+
Raw pre-encoded filter string (e.g. ``"ctype:articoli;pub_date:ultimo_anno"``).
|
|
98
|
+
Filters are separated by ``;``. When provided, overrides ``content_type``,
|
|
99
|
+
``category``, and ``date_range``.
|
|
85
100
|
|
|
86
101
|
Returns
|
|
87
102
|
-------
|
|
@@ -110,7 +125,7 @@ class IlPostClient:
|
|
|
110
125
|
page: int = 1,
|
|
111
126
|
hits: int = 10,
|
|
112
127
|
sort: Union[SortOrder, str] = SortOrder.RELEVANCE,
|
|
113
|
-
category: Optional[str] = None,
|
|
128
|
+
category: Optional[Union[str, list[str]]] = None,
|
|
114
129
|
date_range: Optional[DateRange] = None,
|
|
115
130
|
) -> SearchResult:
|
|
116
131
|
"""Search articles only. Convenience wrapper around :meth:`search`."""
|
|
@@ -169,7 +184,7 @@ class IlPostClient:
|
|
|
169
184
|
hits: int = 10,
|
|
170
185
|
sort: Union[SortOrder, str] = SortOrder.RELEVANCE,
|
|
171
186
|
content_type: Optional[ContentType] = None,
|
|
172
|
-
category: Optional[str] = None,
|
|
187
|
+
category: Optional[Union[str, list[str]]] = None,
|
|
173
188
|
date_range: Optional[DateRange] = None,
|
|
174
189
|
max_pages: Optional[int] = None,
|
|
175
190
|
):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ilpost-api-wrapper
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Python wrapper for Il Post newspaper API
|
|
5
5
|
Author: Antonio Girasella
|
|
6
6
|
License-Expression: MIT
|
|
@@ -166,7 +166,7 @@ for group in result.filters:
|
|
|
166
166
|
|
|
167
167
|
## CLI
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
The `ilpost-search` command is included with the package:
|
|
170
170
|
|
|
171
171
|
```
|
|
172
172
|
usage: ilpost-search [-h] [--type {articles,podcasts,newsletters}]
|
|
@@ -179,19 +179,19 @@ usage: ilpost-search [-h] [--type {articles,podcasts,newsletters}]
|
|
|
179
179
|
|
|
180
180
|
```bash
|
|
181
181
|
# Basic search
|
|
182
|
-
|
|
182
|
+
ilpost-search berlusconi
|
|
183
183
|
|
|
184
184
|
# Most recent articles in politics
|
|
185
|
-
|
|
185
|
+
ilpost-search renzi --type articles --sort newest --category politica
|
|
186
186
|
|
|
187
187
|
# Podcast search, past 30 days
|
|
188
|
-
|
|
188
|
+
ilpost-search cacao --type podcasts --date month
|
|
189
189
|
|
|
190
190
|
# Page 2, 5 results per page, oldest first
|
|
191
|
-
|
|
191
|
+
ilpost-search sicilia --sort oldest --hits 5 --page 2
|
|
192
192
|
|
|
193
193
|
# Fetch all pages of newsletter results (up to 3 pages)
|
|
194
|
-
|
|
194
|
+
ilpost-search economia --type newsletters --all-pages --max-pages 3
|
|
195
195
|
```
|
|
196
196
|
|
|
197
197
|
## Notes
|
{ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/ilpost_api_wrapper.egg-info/SOURCES.txt
RENAMED
|
@@ -2,10 +2,12 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
ilpost/__init__.py
|
|
5
|
+
ilpost/cli.py
|
|
5
6
|
ilpost/client.py
|
|
6
7
|
ilpost/models.py
|
|
7
8
|
ilpost/py.typed
|
|
8
9
|
ilpost_api_wrapper.egg-info/PKG-INFO
|
|
9
10
|
ilpost_api_wrapper.egg-info/SOURCES.txt
|
|
10
11
|
ilpost_api_wrapper.egg-info/dependency_links.txt
|
|
12
|
+
ilpost_api_wrapper.egg-info/entry_points.txt
|
|
11
13
|
ilpost_api_wrapper.egg-info/top_level.txt
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ilpost-api-wrapper"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Python wrapper for Il Post newspaper API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -33,5 +33,8 @@ Homepage = "https://github.com/girasella/ilpost-api-wrapper"
|
|
|
33
33
|
Repository = "https://github.com/girasella/ilpost-api-wrapper"
|
|
34
34
|
Issues = "https://github.com/girasella/ilpost-api-wrapper/issues"
|
|
35
35
|
|
|
36
|
+
[project.scripts]
|
|
37
|
+
ilpost-search = "ilpost.cli:main"
|
|
38
|
+
|
|
36
39
|
[tool.setuptools.packages.find]
|
|
37
40
|
where = ["."]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ilpost_api_wrapper-0.1.0 → ilpost_api_wrapper-0.3.0}/ilpost_api_wrapper.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|