Skip to content

Commit

Permalink
[add] adding stars endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
Animenosekai committed Jun 18, 2022
1 parent 624eeff commit 424e9ed
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 22 deletions.
10 changes: 10 additions & 0 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@ A translation ID is a hash created using the following information :
- the destination language
- the source text
- the destination text

## Environment Variables

| Name | Description | Default  |
|---------------------------|------------------------------------------------------------------------|-------------|
| `HOST` | The host to listen to | `127.0.0.1` |
| `PORT` | The port to listen to | `5001` |
| `TRANSLATEPY_DB_DISABLED` | To disable any DB interaction | `False`  |
| `TRANSLATEPY_MONGO_URI` | The MongoDB URI to connect to. If none, a `mongod` process will be ran | `None`  |
| `TRANSLATEPY_IP_SALT` | The salt to create IP hashes | ``  |
8 changes: 5 additions & 3 deletions server/db.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from os import environ
from nasse.logging import log, LogLevels

from nasse.logging import LogLevels, log
from nasse.utils.boolean import to_bool
from yuno import MongoDB

from schemas.client import TranslatepyClient

if not to_bool(environ.get("TRANSLATEPY_DB_DISABLED", False)):
MONGO_URI = environ.get("MONGO_URI", None)
MONGO_URI = environ.get("TRANSLATEPY_MONGO_URI", None)
if MONGO_URI is None:
mongo = MongoDB()
log("Starting MongoDB", LogLevels.INFO)
Expand All @@ -14,4 +16,4 @@

client = TranslatepyClient(MONGO_URI, connect=False)
else:
client = {}
client = TranslatepyClient()
145 changes: 145 additions & 0 deletions server/endpoints/stars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
from os import environ

from db import client
from exceptions import DatabaseDisabled, Forbidden, NotFound
from nasse import Response, Request
from nasse.models import Dynamic, Endpoint, Error, Login, Param, Return
from nasse.utils.boolean import to_bool
from translatepy.server.server import app

from datetime import datetime

from yuno.security.hash import Hasher
from yuno.security.token import TokenManager
from yuno.security.encrypt import AES

hasher = Hasher()
aes = AES(client, prefix="translatepy")
token_manager = TokenManager(key=client, sign=client)

base = Endpoint(
section="Stars",
errors=Error(name="DATABASE_DISABLED", description="When the server disabled any database interaction", code=501),
login=Login(no_login=True)
)


if not to_bool(environ.get("TRANSLATEPY_DB_DISABLED", False)):
stars = client.translatepy.stars
else:
stars = {}


@app.route("/stars", description="Get all starred translations")
def stars_handler(request: Request):
if to_bool(environ.get("TRANSLATEPY_DB_DISABLED", False)):
raise DatabaseDisabled

query = stars.find({
"users.{hash}".format(
hash=hasher.hash_string(
"{ip}{salt}".format(
ip=request.client_ip,
salt=environ.get("TRANSLATEPY_SALT", "")
)
)
): {
"$exists": True
}
})
results = []
for document in query:
result = document.copy()
result["users"] = len(result["users"])
results.append(result)
return Response(
data={
"stars": results
},
message="Here are your starred translations"
)


def TranslationToken(value: str):
return token_manager.decode(value, encryption=aes)


@app.route("/stars/<translation_id>", Endpoint(
methods=["GET", "POST", "DELETE"],
description={
"GET": "Get the stars for a translation",
"POST": "Star a translation",
"DELETE": "Unstar a translation"
},
params=[
Param(name="token", description="The token to authenticate the translation", type=TranslationToken, methods="POST")
],
dynamics=[
Dynamic(name="translation_id", description="The ID of the translation to star", methods="POST"),
Dynamic(name="translation_id", description="The ID of the translation to get", methods="GET"),
Dynamic(name="translation_id", description="The ID of the translation to unstar", methods="DELETE"),
],
errors=[
Error(name="FORBIDDEN", description="You are not allowed to star this translation", code=403),
Error(name="NOT_FOUND", description="The translation could not be found", code=404)
] + base.errors,
returns=[
Return(name="source", description="The source text", methods=["GET", "POST"]),
Return(name="result", description="The result text", methods=["GET", "POST"]),
Return(name="language", description="The translation languages", children=[
Return(name="source", description="The source language", methods=["GET", "POST"]),
Return(name="dest", description="The destination language", methods=["GET", "POST"])
], methods=["GET", "POST"]),
Return(name="users", description="The number of users who starred the translation", type=int, methods=["GET", "POST"])
]
))
def stars__translation_id__(request: Request, method: str, translation_id: str, token: dict = None):
if to_bool(environ.get("TRANSLATEPY_DB_DISABLED", False)):
raise DatabaseDisabled

current_ip_hash = hasher.hash_string(
"{ip}{salt}".format(
ip=request.client_ip,
salt=environ.get("TRANSLATEPY_SALT", "")
)
)

if method == "DELETE":
stars.update({
"_id": translation_id # query
}, {
"$unset": { # command
"users.{hash}".format(hash=current_ip_hash): ""
}
})
return "Removed the star"

try:
translation = stars[translation_id]
except KeyError as err:
raise NotFound("We couldn't find the given translation") from err

if method == "POST":
# token body
# {
# "sub": "user hash",
# "source": "source text",
# "result": "result text",
# "language": {
# "source": "source language",
# "dest": "destination language"
# }
# }
if current_ip_hash != token["sub"]:
raise Forbidden("You are not allowed to star this translation")

if len(translation.users) <= 0:
translation.source = token["source"]
translation.result = token["result"]
translation.language = token["language"]

translation.users[current_ip_hash] = datetime.utcnow()

result = translation.copy()
result["users"] = len(result["users"])
return Response(result)
9 changes: 7 additions & 2 deletions server/endpoints/translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from nasse.timer import Timer
from db import client
from nasse.utils.boolean import to_bool
from server.exceptions import DatabaseDisabled
import translatepy
from translatepy.server.translation import (BaseTranslator, FlaskResponse,
Language, List, NoResult, Queue,
Expand All @@ -14,7 +15,9 @@


def log_time(service: BaseTranslator, time: float, storage: dict):
print("{service} took {time} seconds".format(service=service, time=time))
# print("{service} took {time} seconds".format(service=service, time=time))
if to_bool(environ.get("TRANSLATEPY_DB_DISABLED", False)):
raise DatabaseDisabled
storage[str(service).replace(".", "*dot*")] = time


Expand All @@ -26,7 +29,9 @@ def log_time(service: BaseTranslator, time: float, storage: dict):


def log_error(service: str, error: str):
print("{service} failed: {error}".format(service=service, error=error))
# print("{service} failed: {error}".format(service=service, error=error))
if to_bool(environ.get("TRANSLATEPY_DB_DISABLED", False)):
raise DatabaseDisabled
new_id = ObjectId()
errors[new_id] = {
"_id": new_id,
Expand Down
22 changes: 22 additions & 0 deletions server/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from nasse.exceptions import NasseException


class DatabaseDisabled(NasseException):
STATUS_CODE = 501
MESSAGE = "The database is currently disabled on the server"
EXCEPTION_NAME = "DATABASE_DISABLED"
LOG = False


class Forbidden(NasseException):
STATUS_CODE = 403
MESSAGE = "You do not have the rights to access this endpoint"
EXCEPTION_NAME = "FORBIDDEN"
LOG = False


class NotFound(NasseException):
STATUS_CODE = 404
MESSAGE = "We couldn't find the resource you were looking for"
EXCEPTION_NAME = "NOT_FOUND"
LOG = False
2 changes: 2 additions & 0 deletions server/schemas/client.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from yuno import YunoClient, YunoDatabase

from schemas.stars import StarsCollection
from schemas.errors import ErrorsCollection
from schemas.timings import TimingsCollection


class TranslatepyDatabase(YunoDatabase):
errors: ErrorsCollection
timings: TimingsCollection
stars: StarsCollection


class TranslatepyClient(YunoClient):
Expand Down
4 changes: 2 additions & 2 deletions server/schemas/errors.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import datetime
from schemas.types import Datetime
from yuno import YunoDict, YunoCollection


Expand All @@ -8,7 +8,7 @@ class Error(YunoDict):
"""
service: str
error: str
timestamp: datetime
timestamp: Datetime


class ErrorsCollection(YunoCollection):
Expand Down
16 changes: 12 additions & 4 deletions server/schemas/stars.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
from datetime import datetime
from schemas.types import Datetime
from yuno import YunoDict, YunoCollection


class StarredTranslationLanguage(YunoDict):
"""Starred translation language"""
source: str
dest: str


class StarredTranslation(YunoDict):
"""User starred translations"""
_id: str # translation ID
timestamp: datetime
services: list[str]
users: list[str]
language: StarredTranslationLanguage = {}
source: str = "" # source text
result: str = "" # translated text
services: list[str] = []
users: dict[str, Datetime]


class StarsCollection(YunoCollection):
Expand Down
10 changes: 1 addition & 9 deletions server/schemas/timings.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
from bson import ObjectId
from datetime import datetime
from schemas.types import Datetime
from yuno import YunoDict, YunoCollection


class Datetime(datetime):
def __new__(self, *args, **kwargs) -> datetime:
print(args, kwargs)
if len(args) > 0 and isinstance(args[0], datetime):
return args[0]
return datetime(*args, **kwargs)


class ServiceTimings(YunoDict):
_id: ObjectId
timings: dict[str, int]
Expand Down
9 changes: 9 additions & 0 deletions server/schemas/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from datetime import datetime


class Datetime(datetime):
def __new__(self, *args, **kwargs) -> datetime:
print(args, kwargs)
if len(args) > 0 and isinstance(args[0], datetime):
return args[0]
return datetime(*args, **kwargs)
2 changes: 1 addition & 1 deletion translatepy/server/language.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from nasse import Response
from nasse.models import Dynamic, Endpoint, Error, Login, Param, Return, Header
from nasse.models import Dynamic, Endpoint, Error, Login, Param, Return
from nasse.utils.boolean import to_bool
import translatepy
from translatepy.exceptions import UnknownLanguage
Expand Down
2 changes: 1 addition & 1 deletion translatepy/server/translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from bs4 import NavigableString
from flask import Response as FlaskResponse
from nasse import Response
from nasse.models import Endpoint, Error, Login, Param, Return, Header
from nasse.models import Endpoint, Error, Login, Param, Return
import translatepy
from translatepy import Translator
from translatepy.exceptions import NoResult, UnknownLanguage, UnknownTranslator
Expand Down

1 comment on commit 424e9ed

@vercel
Copy link

@vercel vercel bot commented on 424e9ed Jun 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.