mirror of
https://github.com/clearml/clearml-server
synced 2025-03-03 18:54:20 +00:00
Exported csv file name now contains the project name (including non-ascii names)
This commit is contained in:
parent
5c80336aa9
commit
3ad636c468
@ -4,4 +4,5 @@ tags_cache {
|
||||
download {
|
||||
redis_timeout_sec: 300
|
||||
batch_size: 500
|
||||
max_project_name_length: 60
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
import unicodedata
|
||||
from functools import partial
|
||||
|
||||
from flask import request, Response, redirect
|
||||
from werkzeug.datastructures import ImmutableMultiDict
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from werkzeug.urls import url_quote
|
||||
|
||||
from apiserver.apierrors import APIError
|
||||
from apiserver.apierrors.base import BaseError
|
||||
@ -44,9 +46,17 @@ class RequestHandlers:
|
||||
else:
|
||||
headers = None
|
||||
if call.result.filename:
|
||||
headers = {
|
||||
"Content-Disposition": f"attachment; filename={call.result.filename}"
|
||||
}
|
||||
try:
|
||||
call.result.filename.encode("ascii")
|
||||
except UnicodeEncodeError:
|
||||
simple = unicodedata.normalize("NFKD", call.result.filename)
|
||||
simple = simple.encode("ascii", "ignore").decode("ascii")
|
||||
# safe = RFC 5987 attr-char
|
||||
quoted = url_quote(call.result.filename, safe="")
|
||||
filenames = f"filename={simple}; filename*=UTF-8''{quoted}"
|
||||
else:
|
||||
filenames = f"filename={call.result.filename}"
|
||||
headers = {"Content-Disposition": "attachment; " + filenames}
|
||||
|
||||
response = Response(
|
||||
content,
|
||||
|
@ -177,6 +177,9 @@ def _get_download_getter_fn(
|
||||
return getter
|
||||
|
||||
|
||||
download_conf = config.get("services.organization.download")
|
||||
|
||||
|
||||
@endpoint("organization.prepare_download_for_get_all")
|
||||
def prepare_download_for_get_all(
|
||||
call: APICall, company: str, request: PrepareDownloadForGetAll
|
||||
@ -194,7 +197,7 @@ def prepare_download_for_get_all(
|
||||
|
||||
redis.setex(
|
||||
f"get_all_download_{call.id}",
|
||||
int(config.get("services.organization.download.redis_timeout_sec", 300)),
|
||||
int(download_conf.get("redis_timeout_sec", 300)),
|
||||
json.dumps(call.data),
|
||||
)
|
||||
|
||||
@ -248,7 +251,7 @@ def download_for_get_all(call: APICall, company, request: DownloadForGetAll):
|
||||
with ThreadPoolExecutor(1) as pool:
|
||||
page = 0
|
||||
page_size = int(
|
||||
config.get("services.organization.download.batch_size", 500)
|
||||
download_conf.get("batch_size", 500)
|
||||
)
|
||||
future = pool.submit(get_fn, page, page_size)
|
||||
|
||||
@ -270,6 +273,26 @@ def download_for_get_all(call: APICall, company, request: DownloadForGetAll):
|
||||
if page == 0:
|
||||
yield csv.writer(SingleLine()).writerow(projection)
|
||||
|
||||
call.result.filename = f"{request.entity_type}_export.csv"
|
||||
def get_project_name() -> Optional[str]:
|
||||
projects = call_data.get("project")
|
||||
if not projects or not isinstance(projects, (list, str)):
|
||||
return
|
||||
if isinstance(projects, list):
|
||||
if len(projects) > 1:
|
||||
return
|
||||
projects = projects[0]
|
||||
if projects is None:
|
||||
return "root"
|
||||
project: Project = Project.objects(id=projects).only("basename").first()
|
||||
if not project:
|
||||
return
|
||||
|
||||
return project.basename[:download_conf.get("max_project_name_length", 60)]
|
||||
|
||||
call.result.filename = "-".join(
|
||||
filter(
|
||||
None, ("clearml", get_project_name(), f"{request.entity_type}s.csv")
|
||||
)
|
||||
)
|
||||
call.result.content_type = "text/csv"
|
||||
call.result.raw_data = stream_with_context(generate())
|
||||
|
Loading…
Reference in New Issue
Block a user