Skip to content

Commit 04480e0

Browse files
committed
Implement API version 1
1 parent a686ff6 commit 04480e0

File tree

4 files changed

+214
-0
lines changed

4 files changed

+214
-0
lines changed

README.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<center>
2+
<h1>pons.py</h1>
3+
<h3>An API wrapper for the PONS dictionary</h3>
4+
</center>
5+
6+
## Installation
7+
```sh
8+
$ pip install pons.py
9+
```
10+
11+
## Usage
12+
```py
13+
from pons import Client
14+
15+
pons = Client("YOUR_SECRET") # see PONS API reference
16+
17+
# get dictionaries
18+
# params: language (de|el|en|es|fr|it|pl|pt|ru|sl|tr|zh)
19+
dictionaries = pons.get_dictionaries("en") # returns a list of Dictionary objects
20+
21+
# get translations
22+
# required params: term, dictionary, source language
23+
# optional params: output language, fuzzy (bool), references (bool)
24+
entries = pons.query("term", "deen", "en")["en"] # returns a list of EntryHit objects
25+
entries[0].translations # returns a list of translations (strings)
26+
```
27+
28+
## References
29+
- [PONS API Reference](https://en.pons.com/p/online-dictionary/developers/api)
30+
- [API Documentation](https://en.pons.com/p/files/uploads/pons/api/api-documentation.pdf)

pons/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .client import *
2+
from .models import *

pons/client.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from typing import Any, Dict, List, Optional
2+
3+
import requests
4+
5+
from .models import Dictionary, Hit, Language, create_hit
6+
7+
__all__ = ("Client",)
8+
9+
10+
class Client:
11+
base_url = "https://api.pons.com/v1"
12+
secret: str
13+
14+
def __init__(self, secret: str) -> None:
15+
self.secret = secret
16+
17+
def request(
18+
self, endpoint: str, *, send_secret: bool = False, **params: Any
19+
) -> List[Any]:
20+
headers = {"X-Secret": self.secret} if send_secret else None
21+
res = requests.request(
22+
"GET", self.base_url + endpoint, params=params, headers=headers
23+
)
24+
return res.json()
25+
26+
def get_dictionaries(self, language: Language) -> List[Dictionary]:
27+
data = self.request("/dictionaries", language=language)
28+
return [
29+
Dictionary(
30+
key=dictionary["key"],
31+
simple_label=dictionary["simple_label"],
32+
directed_label=dictionary["directed_label"],
33+
languages=dictionary["languages"],
34+
)
35+
for dictionary in data
36+
]
37+
38+
def query(
39+
self,
40+
term: str,
41+
dictionary_key: str,
42+
source_language: Language,
43+
output_language: Optional[Language] = None,
44+
*,
45+
fuzzy: bool = False,
46+
references: bool = False,
47+
) -> Dict[Language, List[Hit]]:
48+
data = self.request(
49+
"/dictionary",
50+
q=term,
51+
l=dictionary_key,
52+
fm=fuzzy,
53+
ref=references,
54+
language=output_language,
55+
send_secret=True,
56+
**{"in": source_language},
57+
)
58+
return {
59+
entry["lang"]: [create_hit(hit) for hit in entry["hits"]] for entry in data
60+
}

pons/models.py

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
from dataclasses import dataclass, is_dataclass
2+
from typing import Any, Dict, List, Literal, Optional
3+
4+
__all__ = (
5+
"Language",
6+
# dictionaries
7+
"Dictionary",
8+
# querying
9+
"Translation",
10+
"Arab",
11+
"Rom",
12+
"Hit",
13+
"EntryHit",
14+
"TranslationHit",
15+
"NestedEntryHit",
16+
"create_hit",
17+
)
18+
19+
20+
Language = Literal[
21+
"de", "el", "en", "es", "fr", "it", "pl", "pt", "ru", "sl", "tr", "zh"
22+
]
23+
24+
25+
@dataclass(repr=False)
26+
class Dictionary:
27+
key: str
28+
simple_label: str
29+
directed_label: Dict[str, str]
30+
languages: List[Language]
31+
32+
def __repr__(self) -> str:
33+
return f"Dictionary(key='{self.key}')"
34+
35+
36+
@dataclass(repr=False)
37+
class Translation:
38+
source: str
39+
target: str
40+
41+
def __repr__(self) -> str:
42+
return "Translation()"
43+
44+
45+
class Arab:
46+
header: str
47+
translations: List[Translation]
48+
49+
def __init__(self, data: Dict[str, Any]) -> None:
50+
self.header = data["header"]
51+
self.translations = [
52+
Translation(**translation) for translation in data["translations"]
53+
]
54+
55+
def __repr__(self) -> str:
56+
return f"Arab(header='{self.header}')"
57+
58+
59+
class Rom:
60+
headword: str
61+
headword_full: str
62+
wordclass: Optional[str]
63+
arabs: List[Arab]
64+
65+
def __init__(self, data: Dict[str, Any]) -> None:
66+
self.headword = data["headword"]
67+
self.headword_full = data["headword_full"]
68+
self.wordclass = data["wordclass"]
69+
self.arabs = [Arab(arab) for arab in data["arabs"]]
70+
71+
def __repr__(self) -> str:
72+
return f"Rom(headword='{self.headword}')"
73+
74+
75+
class Hit:
76+
type: Literal["entry", "translation", "entry_with_secondary_entries"]
77+
78+
def __repr__(self) -> str:
79+
return f"{self.__class__.__name__}()"
80+
81+
82+
class EntryHit(Hit):
83+
type = "entry"
84+
opendict: bool
85+
roms: List[Rom]
86+
87+
def __init__(self, data: Dict[str, Any]) -> None:
88+
self.opendict = data["opendict"]
89+
self.roms = [Rom(rom) for rom in data["roms"]]
90+
91+
@property
92+
def translations(self) -> List[str]:
93+
to_return = []
94+
for rom in self.roms:
95+
for arab in rom.arabs:
96+
for translation in arab.translations:
97+
to_return.append(translation.target)
98+
return to_return
99+
100+
101+
@dataclass(repr=False)
102+
class TranslationHit(Hit, Translation):
103+
type = "translation"
104+
opendict: bool
105+
106+
107+
class NestedEntryHit(Hit):
108+
type = "entry_with_secondary_entries"
109+
primary: EntryHit
110+
secondary: List[EntryHit]
111+
112+
def __init__(self, data: Dict[str, Any]) -> None:
113+
self.primary = EntryHit(data["primary_entry"])
114+
self.secondary = [EntryHit(entry) for entry in data["secondary_entries"]]
115+
116+
117+
def create_hit(data: Dict[str, Any]) -> Hit:
118+
hit_types = {cls.type: cls for cls in [EntryHit, TranslationHit, NestedEntryHit]}
119+
cls = hit_types[data.pop("type")]
120+
if is_dataclass(cls):
121+
return cls(**data)
122+
return cls(data)

0 commit comments

Comments
 (0)