Skip to content

Commit

Permalink
Initial commit (code)
Browse files Browse the repository at this point in the history
  • Loading branch information
davc0n committed May 5, 2021
1 parent 805fc70 commit 44d166c
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.idea/
build/
dist/
venv/
modernmt.egg-info/
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# ModernMT Python Library
Provides convenient access to the ModernMT API.

## Documentation
See the official website [API docs](https://www.modernmt.com/api?lang=python).

## Requirements
Python 3
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
requests
build
twine
26 changes: 26 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[metadata]
name = modernmt
version = 1.0.0
author = ModernMT
author_email = [email protected]
description = ModernMT API wrapper
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/modernmt/modernmt-python
project_urls =
Bug Tracker = https://github.com/modernmt/modernmt-python/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: OS Independent

[options]
package_dir =
= src
packages = find:
python_requires = >=3
install_requires =
requests

[options.packages.find]
where = src
5 changes: 5 additions & 0 deletions src/modernmt/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .modernmt import ModernMTException
from .modernmt import ModernMT
from .modernmt import Translation
from .modernmt import Memory
from .modernmt import ImportJob
225 changes: 225 additions & 0 deletions src/modernmt/modernmt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import requests


class ModernMTException(Exception):
def __init__(self, status, type, message) -> None:
super().__init__("(%s) %s" % (type, message))
self.status = status
self.type = type
self.message = message


class ModernMT(object):
def __init__(self, license_key, platform="modernmt-python", platform_version="1.0.0") -> None:
self.__base_url = "https://api.modernmt.com"
self.__headers = {
"MMT-ApiKey": license_key,
"MMT-Platform": platform,
"MMT-PlatformVersion": platform_version
}

self.memories = _MemoryServices(self.__headers, self.__send)

def list_supported_languages(self):
return self.__send("get", "/translate/languages")

def translate(self, source, target, q, hints=None, context_vector=None, options=None):
multiple_targets = isinstance(q, list)

data = {"target": target, "q": q}
if source is not None:
data["source"] = source
if context_vector is not None:
data["context_vector"] = context_vector
if hints is not None:
data["hints"] = hints

if options is not None:
if "priority" in options:
data["priority"] = options["priority"]
if "project_id" in options:
data["project_id"] = options["project_id"]
if "multiline" in options:
data["multiline"] = options["multiline"]
if "timeout" in options:
data["timeout"] = options["timeout"]

res = self.__send("get", "/translate", data=data)

if not multiple_targets:
return Translation(res)

translations = []
for el in res:
translations.append(Translation(el))

return translations

def get_context_vector(self, source, targets, text, hints=None, limit=None):
multiple_targets = isinstance(targets, list)
data = {"source": source, "text": text}

if multiple_targets:
data["targets"] = targets
else:
data["targets"] = [targets]

if hints is not None:
data["hints"] = hints
if limit is not None:
data["limit"] = limit

res = self.__send("get", "/context-vector", data=data)

if multiple_targets:
return res["vectors"]
else:
if targets in res["vectors"]:
return res["vectors"][targets]
else:
return None

def get_context_vector_from_file(self, source, targets, file, hints=None, limit=None, compression=None):
multiple_targets = isinstance(targets, list)

if isinstance(file, str):
file = open(file, "rb")

data = {"source": source}
if multiple_targets:
data["targets"] = targets
else:
data["targets"] = [targets]

if hints is not None:
data["hints"] = hints
if limit is not None:
data["limit"] = limit
if compression is not None:
data["compression"] = compression

res = self.__send("get", "/context-vector", data=data, files={"content": file})

if multiple_targets:
return res["vectors"]
else:
if targets in res["vectors"]:
return res["vectors"][targets]
else:
return None

def __send(self, method, endpoint, data=None, files=None, cls=None):
url = self.__base_url + endpoint

headers = self.__headers
headers["X-HTTP-Method-Override"] = method

if files is None:
r = requests.post(url, headers=headers, json=data)
else:
r = requests.post(url, headers=headers, data=data, files=files)

json = r.json()
if r.status_code != requests.codes.ok:
ex_type = "UnknownException"
ex_msg = r.text
try:
error = json["error"]
ex_type, ex_msg = error["type"], error["message"]
except KeyError:
pass
except ValueError:
pass

raise ModernMTException(r.status_code, ex_type, ex_msg)

return json["data"] if cls is None else cls(json["data"])


class _MemoryServices(object):
def __init__(self, headers, send) -> None:
self.__headers = headers
self.__send = send

def list(self):
res = self.__send("get", "/memories")

memories = []
for el in res:
memories.append(Memory(el))

return memories

def get(self, id):
return self.__send("get", "/memories/%s" % id, cls=Memory)

def create(self, name, description=None, external_id=None):
data = {"name": name}
if description is not None:
data["description"] = description
if external_id is not None:
data["external_id"] = external_id

return self.__send("post", "/memories", data=data, cls=Memory)

def edit(self, id, name=None, description=None):
data = {}
if name is not None:
data["name"] = name
if description is not None:
data["description"] = description

return self.__send("put", "/memories/%s" % id, data=data, cls=Memory)

def delete(self, id):
return self.__send("delete", "/memories/%s" % id, cls=Memory)

def add(self, id, source, target, sentence, translation, tuid=None):
data = {"source": source, "target": target, "sentence": sentence, "translation": translation}
if tuid is not None:
data["tuid"] = tuid

return self.__send("post", "/memories/%s/content" % id, data=data, cls=ImportJob)

def replace(self, id, tuid, source, target, sentence, translation):
data = {"tuid": tuid, "source": source, "target": target, "sentence": sentence, "translation": translation}
return self.__send("put", "/memories/%s/content" % id, data=data, cls=ImportJob)

def import_tmx(self, id, tmx, compression=None):
if isinstance(tmx, str):
tmx = open(tmx, "rb")

data = {}
if compression is not None:
data["compression"] = compression

return self.__send("post", "/memories/%s/content" % id, data=data, files={"tmx": tmx}, cls=ImportJob)

def get_import_status(self, uuid):
return self.__send("get", "/import-jobs/%s" % uuid, cls=ImportJob)


class _Model(object):
def __init__(self, data, fields) -> None:
self.__dict__ = {k: v for k, v in data.items() if k in fields}

def __repr__(self):
return str(self)

def __str__(self):
return str(vars(self))


class Translation(_Model):
def __init__(self, data) -> None:
super().__init__(data, ["translation", "contextVector", "characters", "billedCharacters", "detectedLanguage"])


class Memory(_Model):
def __init__(self, data) -> None:
super().__init__(data, ["id", "name", "description", "creationDate"])


class ImportJob(_Model):
def __init__(self, data) -> None:
super().__init__(data, ["id", "memory", "size", "progress"])

0 comments on commit 44d166c

Please sign in to comment.