Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async #14

Merged
merged 5 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

Loading