diff --git a/docat/docat/app.py b/docat/docat/app.py index c6de7720..bc4a84b5 100644 --- a/docat/docat/app.py +++ b/docat/docat/app.py @@ -28,6 +28,7 @@ UPLOAD_FOLDER, calculate_token, create_symlink, + directory_size, extract_archive, get_all_projects, get_project_details, @@ -139,6 +140,10 @@ def upload_icon( with icon_path.open("wb") as buffer: shutil.copyfileobj(file.file, buffer) + # recalculate size cache + directory_size(project_base_path, recalculate=True) + directory_size(DOCAT_UPLOAD_FOLDER, recalculate=True) + return ApiResponse(message="Icon successfully uploaded") @@ -266,6 +271,10 @@ def upload( if not (base_path / "index.html").exists(): return ApiResponse(message="Documentation uploaded successfully, but no index.html found at root of archive.") + # recalculate size cache + directory_size(project_base_path, recalculate=True) + directory_size(DOCAT_UPLOAD_FOLDER, recalculate=True) + return ApiResponse(message="Documentation uploaded successfully") @@ -372,6 +381,9 @@ def delete( response.status_code = status.HTTP_404_NOT_FOUND return ApiResponse(message=message) + # recalculate size cache + directory_size(DOCAT_UPLOAD_FOLDER, recalculate=True) + return ApiResponse(message=f"Successfully deleted version '{version}'") diff --git a/docat/docat/utils.py b/docat/docat/utils.py index ac0f7199..6a4f45c1 100644 --- a/docat/docat/utils.py +++ b/docat/docat/utils.py @@ -93,6 +93,9 @@ def remove_docs(project: str, version: str, upload_folder_path: Path): if not link.resolve().exists(): link.unlink() + # remove size info + (upload_folder_path / project / ".size").unlink(missing_ok=True) + # remove empty projects if not [d for d in docs.parent.iterdir() if d.is_dir()]: docs.parent.rmdir() @@ -158,8 +161,19 @@ def readable_size(bytes: int) -> str: return str(amount) + size_suffix -def directory_size(path: Path) -> int: - return sum(file.stat().st_size for file in path.rglob("*") if file.is_file()) +def directory_size(path: Path, recalculate: bool = False) -> int: + """ + Returns the size of a directory and caches it's size unless + recalculate is set to true or the cache is missed + """ + size_info = path / ".size" + if size_info.exists() and not recalculate: + return int(size_info.read_text()) + + dir_size = sum(file.stat().st_size for file in path.rglob("*") if file.is_file()) + size_info.write_text(str(dir_size)) # cache directory size + + return dir_size def get_system_stats(upload_folder_path: Path) -> Stats: diff --git a/docat/tests/test_stats.py b/docat/tests/test_stats.py index 166c4a9b..f1bfe7ba 100644 --- a/docat/tests/test_stats.py +++ b/docat/tests/test_stats.py @@ -9,10 +9,10 @@ @pytest.mark.parametrize( ("project_config", "n_projects", "n_versions", "storage"), [ - ([("some-project", ["1.0.0"])], 1, 1, "20 bytes"), - ([("some-project", ["1.0.0", "2.0.0"])], 1, 2, "40 bytes"), - ([("some-project", ["1.0.0", "2.0.0"])], 1, 2, "40 bytes"), - ([("some-project", ["1.0.0", "2.0.0"]), ("another-project", ["1"])], 2, 3, "60 bytes"), + ([("some-project", ["1.0.0"])], 1, 1, "22 bytes"), + ([("some-project", ["1.0.0", "2.0.0"])], 1, 2, "44 bytes"), + ([("some-project", ["1.0.0", "2.0.0"])], 1, 2, "44 bytes"), + ([("some-project", ["1.0.0", "2.0.0"]), ("another-project", ["1"])], 2, 3, "66 bytes"), ], ) def test_get_stats(_, project_config, n_projects, n_versions, storage, client_with_claimed_project): diff --git a/docat/tests/test_upload_icon.py b/docat/tests/test_upload_icon.py index 1a22e447..03a04655 100644 --- a/docat/tests/test_upload_icon.py +++ b/docat/tests/test_upload_icon.py @@ -181,7 +181,7 @@ def test_get_project_recongizes_icon(_, client_with_claimed_project): { "name": "some-project", "logo": True, - "storage": "103 bytes", + "storage": "105 bytes", "versions": [{"name": "1.0.0", "timestamp": "2000-01-01T01:01:00", "tags": [], "hidden": False}], } ]