Skip to content

Commit 748ed5c

Browse files
Use APScheduler
1 parent f6ce094 commit 748ed5c

File tree

2 files changed

+68
-81
lines changed

2 files changed

+68
-81
lines changed

pydatalab/src/pydatalab/routes/v0_1/export.py

Lines changed: 46 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -21,48 +21,48 @@
2121
def _(): ...
2222

2323

24+
def _do_export(task_id: str, collection_id: str):
25+
try:
26+
flask_mongo.db.export_tasks.update_one(
27+
{"task_id": task_id}, {"$set": {"status": ExportStatus.PROCESSING}}
28+
)
29+
30+
export_dir = Path(CONFIG.FILE_DIRECTORY) / "exports"
31+
export_dir.mkdir(exist_ok=True)
32+
33+
output_path = export_dir / f"{task_id}.eln"
34+
create_eln_file(collection_id, str(output_path))
35+
36+
flask_mongo.db.export_tasks.update_one(
37+
{"task_id": task_id},
38+
{
39+
"$set": {
40+
"status": ExportStatus.READY,
41+
"file_path": str(output_path),
42+
"completed_at": datetime.now(tz=timezone.utc),
43+
}
44+
},
45+
)
46+
47+
except Exception as e:
48+
flask_mongo.db.export_tasks.update_one(
49+
{"task_id": task_id},
50+
{
51+
"$set": {
52+
"status": ExportStatus.ERROR,
53+
"error_message": str(e),
54+
"completed_at": datetime.now(tz=timezone.utc),
55+
}
56+
},
57+
)
58+
59+
2460
def _generate_export_in_background(task_id: str, collection_id: str, app):
25-
"""Background function to generate the .eln file.
26-
27-
Parameters:
28-
task_id: ID of the export task
29-
collection_id: ID of the collection to export
30-
app: Flask application instance
31-
"""
32-
with app.app_context():
33-
try:
34-
flask_mongo.db.export_tasks.update_one(
35-
{"task_id": task_id}, {"$set": {"status": ExportStatus.PROCESSING}}
36-
)
37-
38-
export_dir = Path(CONFIG.FILE_DIRECTORY) / "exports"
39-
export_dir.mkdir(exist_ok=True)
40-
41-
output_path = export_dir / f"{task_id}.eln"
42-
create_eln_file(collection_id, str(output_path))
43-
44-
flask_mongo.db.export_tasks.update_one(
45-
{"task_id": task_id},
46-
{
47-
"$set": {
48-
"status": ExportStatus.READY,
49-
"file_path": str(output_path),
50-
"completed_at": datetime.now(tz=timezone.utc),
51-
}
52-
},
53-
)
54-
55-
except Exception as e:
56-
flask_mongo.db.export_tasks.update_one(
57-
{"task_id": task_id},
58-
{
59-
"$set": {
60-
"status": ExportStatus.ERROR,
61-
"error_message": str(e),
62-
"completed_at": datetime.now(tz=timezone.utc),
63-
}
64-
},
65-
)
61+
if app is not None:
62+
with app.app_context():
63+
_do_export(task_id, collection_id)
64+
else:
65+
_do_export(task_id, collection_id)
6666

6767

6868
@EXPORT.route("/collections/<string:collection_id>/export", methods=["POST"])
@@ -82,7 +82,7 @@ def start_collection_export(collection_id: str):
8282
task_id = str(uuid.uuid4())
8383

8484
if not CONFIG.TESTING:
85-
creator_id = current_user.person.immutable_id
85+
creator_id = str(current_user.person.immutable_id)
8686
else:
8787
creator_id = "000000000000000000000000"
8888

@@ -95,7 +95,10 @@ def start_collection_export(collection_id: str):
9595

9696
flask_mongo.db.export_tasks.insert_one(export_task.dict())
9797

98-
app = current_app._get_current_object()
98+
try:
99+
app = current_app._get_current_object()
100+
except RuntimeError:
101+
app = None
99102

100103
export_scheduler.add_job(
101104
func=_generate_export_in_background,
@@ -110,15 +113,6 @@ def start_collection_export(collection_id: str):
110113

111114
@EXPORT.route("/exports/<string:task_id>/status", methods=["GET"])
112115
def get_export_status(task_id: str):
113-
"""Get the status of an export task.
114-
115-
Parameters:
116-
task_id: The export task ID
117-
118-
Returns:
119-
JSON response with task status and download URL if ready
120-
"""
121-
122116
task = flask_mongo.db.export_tasks.find_one({"task_id": task_id})
123117

124118
if not task:
@@ -146,15 +140,6 @@ def get_export_status(task_id: str):
146140

147141
@EXPORT.route("/exports/<string:task_id>/download", methods=["GET"])
148142
def download_export(task_id: str):
149-
"""Download the generated .eln file.
150-
151-
Parameters:
152-
task_id: The export task ID
153-
154-
Returns:
155-
The .eln file as attachment
156-
"""
157-
158143
task = flask_mongo.db.export_tasks.find_one({"task_id": task_id})
159144

160145
if not task:

pydatalab/tests/server/test_export.py

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,35 +27,37 @@ def sample_collection(database, user_id):
2727

2828
@pytest.fixture
2929
def mock_scheduler():
30-
with patch("pydatalab.routes.v0_1.export.export_scheduler") as mock_scheduler:
31-
mock_scheduler.add_job = MagicMock()
32-
33-
def mock_add_job(func, args, job_id=None):
34-
return None
35-
36-
mock_scheduler.add_job.side_effect = mock_add_job
37-
38-
yield mock_scheduler
30+
with patch("pydatalab.routes.v0_1.export.export_scheduler") as mock_sched:
31+
mock_sched.add_job = MagicMock(return_value=None)
32+
yield mock_sched
3933

4034

4135
class TestExportRoutes:
42-
def test_start_collection_export_success(self, client, sample_collection, mock_scheduler):
36+
def test_start_collection_export_success(
37+
self, client, sample_collection, mock_scheduler, database
38+
):
4339
collection_id = sample_collection["collection_id"]
4440

45-
with patch("pydatalab.routes.v0_1.export.current_app") as mock_app:
46-
mock_app._get_current_object.return_value = MagicMock()
41+
response = client.post(f"/collections/{collection_id}/export")
4742

48-
response = client.post(f"/collections/{collection_id}/export")
43+
assert response.status_code == 202
44+
45+
data = json.loads(response.data)
46+
assert data["status"] == "success"
47+
assert "task_id" in data
48+
assert "status_url" in data
49+
assert data["status_url"] == f"/exports/{data['task_id']}/status"
4950

50-
assert response.status_code == 202
51+
assert mock_scheduler.add_job.called
5152

52-
data = json.loads(response.data)
53-
assert data["status"] == "success"
54-
assert "task_id" in data
55-
assert "status_url" in data
56-
assert data["status_url"] == f"/exports/{data['task_id']}/status"
53+
# Verify the task was created in the database
54+
task = database.export_tasks.find_one({"task_id": data["task_id"]})
55+
assert task is not None
56+
assert task["collection_id"] == collection_id
57+
assert task["status"] == ExportStatus.PENDING
5758

58-
assert mock_scheduler.add_job.called
59+
# Clean up
60+
database.export_tasks.delete_one({"task_id": data["task_id"]})
5961

6062
def test_start_collection_export_not_found(self, client):
6163
response = client.post("/collections/nonexistent/export")

0 commit comments

Comments
 (0)