Skip to content

Commit

Permalink
Merge pull request #14 from DigiKlausur/async
Browse files Browse the repository at this point in the history
Async
  • Loading branch information
tmetzl authored Oct 7, 2024
2 parents 0cefb0a + 0dbcc56 commit 0d4c9bc
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 53 deletions.
60 changes: 41 additions & 19 deletions e2xauthoring/app/handlers/base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import inspect
from typing import List

Expand All @@ -17,14 +18,26 @@ def status_msg(method):
method: The method to decorate
"""

def wrapper(*args, **kwargs):
try:
res = method(*args, **kwargs)
return SuccessMessage(data=res)
except Exception as e:
return ErrorMessage(error=getattr(e, "message", str(e)))
if asyncio.iscoroutinefunction(method):

return wrapper
async def async_wrapper(*args, **kwargs):
try:
res = await method(*args, **kwargs)
return SuccessMessage(data=res)
except Exception as e:
return ErrorMessage(error=getattr(e, "message", str(e)))

return async_wrapper
else:

def wrapper(*args, **kwargs):
try:
res = method(*args, **kwargs)
return SuccessMessage(data=res)
except Exception as e:
return ErrorMessage(error=getattr(e, "message", str(e)))

return wrapper


class ApiManageHandler(E2xApiHandler):
Expand Down Expand Up @@ -59,16 +72,21 @@ def extract_arguments(self, method: str):
params[arg] = argument
return params

def perform_action(self, action: str, allowed_actions: List[str]):
async def perform_action(self, action: str, allowed_actions: List[str]):
assert (
action is not None and action in allowed_actions
), f"Action {action} is not a valid action."
method = getattr(self.__manager, action)
arguments = self.extract_arguments(method)
return method(**arguments)

# If the method is async, await it, otherwise call it synchronously
if inspect.iscoroutinefunction(method):
return await method(**arguments)
else:
return method(**arguments)

@status_msg
def handle_request(self, request_type: str):
async def handle_request(self, request_type: str):
action = self.get_argument(
"action", default=None # self.__allowed_actions[request_type]["default"]
)
Expand All @@ -77,26 +95,30 @@ def handle_request(self, request_type: str):
"action", self.__allowed_actions[request_type]["default"]
)

return self.perform_action(
return await self.perform_action(
action, self.__allowed_actions[request_type]["actions"]
)

@web.authenticated
@check_xsrf
def get(self):
self.finish(self.handle_request("get").json())
async def get(self):
result = await self.handle_request("get")
self.finish(result.json())

@web.authenticated
@check_xsrf
def delete(self):
self.finish(self.handle_request("delete").json())
async def delete(self):
result = await self.handle_request("delete")
self.finish(result.json())

@web.authenticated
@check_xsrf
def put(self):
self.finish(self.handle_request("put").json())
async def put(self):
result = await self.handle_request("put")
self.finish(result.json())

@web.authenticated
@check_xsrf
def post(self):
self.finish(self.handle_request("post").json())
async def post(self):
result = await self.handle_request("post")
self.finish(result.json())
28 changes: 18 additions & 10 deletions e2xauthoring/managers/taskmanager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import os
import shutil

Expand All @@ -17,7 +18,7 @@ class TaskManager(BaseManager):
"pools", help="The relative directory where the pools are stored"
)

def __get_task_info(self, task, pool):
async def __get_task_info(self, task, pool):
base_path = os.path.join(self.base_path, pool)
notebooks = [
file
Expand All @@ -29,7 +30,11 @@ def __get_task_info(self, task, pool):
questions = 0

for notebook in notebooks:
nb = nbformat.read(os.path.join(base_path, task, notebook), as_version=4)
nb = await asyncio.to_thread(
nbformat.read,
os.path.join(base_path, task, notebook),
as_version=nbformat.NO_CONVERT,
)
for cell in nb.cells:
if "nbgrader" in cell.metadata and cell.metadata.nbgrader.grade:
points += cell.metadata.nbgrader.points
Expand Down Expand Up @@ -74,11 +79,12 @@ def git_diff(self, pool, task, file):
.replace("\n", "<br/>"),
)

def get(self, pool: str, name: str):
async def get(self, pool: str, name: str):
path = os.path.join(self.base_path, pool, name)
assert os.path.exists(path), "The task does not exists"
points, n_questions = self.__get_task_info(name, pool)
git_status = self.git_status(pool, name)
points, n_questions = await self.__get_task_info(name, pool)
git_status = await asyncio.to_thread(self.git_status, pool, name)

if "repo" in git_status:
del git_status["repo"]
return Task(
Expand Down Expand Up @@ -118,13 +124,14 @@ def remove(self, pool, name):
), f"No task with the name {name} from pool {pool} exists."
shutil.rmtree(path)

def list(self, pool):
async def list(self, pool):
tasks = []
path = os.path.join(self.base_path, pool)
assert os.path.exists(path), f"No pool with the name {pool} exists."
for task_dir in self.listdir(os.path.join(self.base_path, pool)):
points, n_questions = self.__get_task_info(task_dir, pool)
git_status = self.git_status(pool, task_dir)
points, n_questions = await self.__get_task_info(task_dir, pool)
git_status = await asyncio.to_thread(self.git_status, pool, task_dir)

if "repo" in git_status:
del git_status["repo"]
tasks.append(
Expand All @@ -138,11 +145,12 @@ def list(self, pool):
)
return tasks

def list_all(self):
async def list_all(self):
pool_manager = TaskPoolManager(self.coursedir)
tasks = []
for pool in pool_manager.list():
tasks.extend(self.list(pool.name))
pool_tasks = await self.list(pool.name)
tasks.extend(pool_tasks)
return tasks

def copy(self, old_name: str, new_name: str, pool: str = ""):
Expand Down
46 changes: 29 additions & 17 deletions e2xauthoring/managers/taskpoolmanager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import os
import shutil

Expand All @@ -13,14 +14,16 @@ class TaskPoolManager(BaseManager):
"pools", help="The relative directory where the pools are stored"
)

def __get_n_tasks(self, name) -> int:
return len(
[
d
for d in os.listdir(os.path.join(self.base_path, name))
if not d.startswith(".")
]
)
async def __get_n_tasks(self, name) -> int:
base_path = os.path.join(self.base_path, name)

# Offload os.listdir to a thread
directory_list = await asyncio.to_thread(os.listdir, base_path)

# Filter out directories that start with a dot ('.')
task_count = len([d for d in directory_list if not d.startswith(".")])

return task_count

def turn_into_repository(self, pool):
path = os.path.join(self.base_path, pool)
Expand Down Expand Up @@ -54,13 +57,22 @@ def remove(self, name):
assert os.path.exists(path), f"The task pool {name} does not exist"
shutil.rmtree(path)

def list(self):
assert os.path.exists(self.base_path), "Pool directory does not exist."
return [
TaskPool(
name=pool_dir,
n_tasks=self.__get_n_tasks(pool_dir),
is_repo=is_version_controlled(os.path.join(self.base_path, pool_dir)),
async def list(self):
if not os.path.exists(self.base_path):
self.log.warning("The pool directory does not exist.")
os.makedirs(self.base_path, exist_ok=True)
pool_dirs = await asyncio.to_thread(self.listdir, self.base_path)
tasks = []
for pool_dir in pool_dirs:
n_tasks = await self.__get_n_tasks(pool_dir)
is_repo = await asyncio.to_thread(
is_version_controlled, os.path.join(self.base_path, pool_dir)
)
tasks.append(
TaskPool(
name=pool_dir,
n_tasks=n_tasks,
is_repo=is_repo,
)
)
for pool_dir in self.listdir(self.base_path)
]
return tasks
4 changes: 3 additions & 1 deletion e2xauthoring/managers/templatemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ def remove(self, name: str):
shutil.rmtree(path)

def list(self):
assert os.path.exists(self.base_path), "Template directory not found."
if not os.path.exists(self.base_path):
self.log.warning("The template directory does not exist.")
os.makedirs(self.base_path, exist_ok=True)
templates = [
Template(name=template_dir) for template_dir in self.listdir(self.base_path)
]
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0d4c9bc

Please sign in to comment.