Skip to content

Commit

Permalink
chore(optimize-code): Updated router file and created a utils file to…
Browse files Browse the repository at this point in the history
… add helper functions code and use it from there and removed unwnated code from the components
  • Loading branch information
priyakanabar-crest committed Nov 15, 2024
1 parent 17565b9 commit 571eb58
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 160 deletions.
167 changes: 9 additions & 158 deletions zt_backend/router.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import subprocess
import shutil
from fastapi import (
APIRouter,
Expand Down Expand Up @@ -34,7 +33,7 @@
from zt_backend.models.state.user_state import UserState
from zt_backend.models.state.app_state import AppState
from zt_backend.models.state.notebook_state import UploadState
from fastapi.responses import HTMLResponse, StreamingResponse, Response
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi.requests import Request
from pathlib import Path
import logging
Expand All @@ -51,7 +50,8 @@
from typing import Dict, Tuple, Optional
import aiofiles
import tempfile
import zipfile
from zt_backend.utils.file_utils import *


router = APIRouter()
manager = ConnectionManager()
Expand Down Expand Up @@ -511,67 +511,15 @@ def share_notebook(shareRequest: request.ShareRequest):
response_json = response.json()
signed_url = response_json.get("uploadURL")
if not signed_url:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to get a signed URL",
)

python_warning = response_json.get("pythonWarning", "")
zt_warning = response_json.get("ztWarning", "")
project_warning = response_json.get("projectWarning", "")
warning_message = ""
if python_warning:
warning_message += f"\n{python_warning}"
if zt_warning:
warning_message += f"\n{zt_warning}"
if project_warning:
warning_message += f"\n{project_warning}"
if warning_message:
warning_message += "\nSelect confirm if you would like to proceed"
upload_state.signed_url = signed_url
return {"warning": warning_message}

publish_files(project_name, signed_url)

except HTTPException as e:
raise e
except Exception as e:
logger.error("Error submitting share request: %s", str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error submitting share request",
)

return {"Error": "Failed to get signed URL"}

@router.post("/api/confirm_share")
def confirm_share(shareRequest: request.ShareRequest):
if app_state.run_mode == "dev":
if upload_state.signed_url:
try:
publish_files(
shareRequest.projectName.lower().strip(), upload_state.signed_url
)
upload_state.signed_url = None
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error submitting share request",
)
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="No active signed URL"
output_filename = f"{project_name}"
project_source = os.path.normpath(os.getcwd())
logger.info(project_source)
shutil.make_archive(
base_name=output_filename, format="gztar", root_dir=project_source
)


def publish_files(project_name, signed_url):
try:
output_filename = Path(settings.zt_path) / f"{project_name}.tar.gz"
tar_base = str(Path(settings.zt_path) / project_name)

shutil.make_archive(
base_name=tar_base, format="gztar", root_dir=settings.zt_path
)

with output_filename.open("rb") as file:
upload_files = {"file": file}
upload_response = requests.post(
Expand Down Expand Up @@ -733,71 +681,6 @@ def delete_item(delete_request: request.DeleteItemRequest):
status_code=500, detail=f"An unexpected error occurred: {str(e)}"
)

def initialize_mime_types():
"""Initialize and customize MIME types."""
mimetypes.init()

custom_mime_types: Dict[str, str] = {
'.md': 'text/markdown',
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'.7z': 'application/x-7z-compressed',
'.rar': 'application/vnd.rar',
'.webp': 'image/webp',
# Add more custom MIME types here as needed
}

for ext, mime_type in custom_mime_types.items():
mimetypes.add_type(mime_type, ext)

def get_mime_type(filename: str) -> str:
"""Determine the MIME type of a file based on its filename."""
initialize_mime_types()
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'

def validate_path(path: Path, filename: str):
"""Validate the path and raise appropriate exceptions if invalid."""
if not path.exists():
raise HTTPException(status_code=404, detail=f"Path not found: {filename}")

async def create_zip_file(folder_path: Path, temp_zip_path: str):
"""Create a zip file from a folder."""
with zipfile.ZipFile(temp_zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, _, files in os.walk(folder_path):
for file in files:
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, start=str(folder_path))
zipf.write(file_path, arcname)

async def parse_range_header(range_header: Optional[str], file_size: int) -> Tuple[int, int]:
"""Parse Range header and return start and end bytes."""
if not range_header:
return 0, file_size - 1

try:
range_str = range_header.replace('bytes=', '')
start_str, end_str = range_str.split('-')
start = int(start_str)
end = int(end_str) if end_str else file_size - 1
return start, min(end, file_size - 1)
except ValueError:
raise HTTPException(status_code=400, detail="Invalid range header")

async def stream_file_range(file_path: str, start: int, end: int, chunk_size: int = 8192):
"""Stream file content for the specified byte range."""
async with aiofiles.open(file_path, mode='rb') as file:
await file.seek(start)
bytes_remaining = end - start + 1

while bytes_remaining > 0:
chunk_size = min(chunk_size, bytes_remaining)
chunk = await file.read(chunk_size)
if not chunk:
break
yield chunk
bytes_remaining -= len(chunk)

@router.get("/api/download")
async def download_item(
request: Request,
Expand Down Expand Up @@ -862,38 +745,6 @@ async def cleanup_temp_file():
detail=f"Download failed: {str(e)}"
)

def get_file_type(name):
extension = name.split(".")[-1]
if extension in ["html", "js", "json", "md", "pdf", "png", "txt", "xls"]:
return extension
return None


def list_dir(path):
items = []
for item in path.iterdir():
if item.is_dir():
items.append(
{
"title": item.name,
"file": "folder",
"id": item.as_posix(),
"children": [],
}
)
else:
file_type = get_file_type(item.name)
if file_type:
items.append(
{"title": item.name, "file": file_type, "id": item.as_posix()}
)
else:
items.append(
{"title": item.name, "file": "file", "id": item.as_posix()}
)
return items


@router.get("/api/get_files")
def list_files():
path = Path(".")
Expand Down
105 changes: 105 additions & 0 deletions zt_backend/utils/file_utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,114 @@
import zipfile
import mimetypes
import os
from typing import Dict, Optional, Tuple
from fastapi import HTTPException
from pathlib import Path
import aiofiles
import asyncio
import os
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import HTTPException, BackgroundTasks


def initialize_mime_types():
"""Initialize and customize MIME types."""
mimetypes.init()

custom_mime_types: Dict[str, str] = {
'.md': 'text/markdown',
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'.7z': 'application/x-7z-compressed',
'.rar': 'application/vnd.rar',
'.webp': 'image/webp',
# Add more custom MIME types here as needed
}

for ext, mime_type in custom_mime_types.items():
mimetypes.add_type(mime_type, ext)

def get_mime_type(filename: str) -> str:
"""Determine the MIME type of a file based on its filename."""
initialize_mime_types()
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'

def validate_path(path: Path, filename: str):
"""Validate the path and raise appropriate exceptions if invalid."""
if not path.exists():
raise HTTPException(status_code=404, detail=f"Path not found: {filename}")

async def create_zip_file(folder_path: Path, temp_zip_path: str):
"""Create a zip file from a folder."""
with zipfile.ZipFile(temp_zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, _, files in os.walk(folder_path):
for file in files:
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, start=str(folder_path))
zipf.write(file_path, arcname)

async def parse_range_header(range_header: Optional[str], file_size: int) -> Tuple[int, int]:
"""Parse Range header and return start and end bytes."""
if not range_header:
return 0, file_size - 1

try:
range_str = range_header.replace('bytes=', '')
start_str, end_str = range_str.split('-')
start = int(start_str)
end = int(end_str) if end_str else file_size - 1
return start, min(end, file_size - 1)
except ValueError:
raise HTTPException(status_code=400, detail="Invalid range header")

async def stream_file_range(file_path: str, start: int, end: int, chunk_size: int = 8192):
"""Stream file content for the specified byte range."""
async with aiofiles.open(file_path, mode='rb') as file:
await file.seek(start)
bytes_remaining = end - start + 1

while bytes_remaining > 0:
chunk_size = min(chunk_size, bytes_remaining)
chunk = await file.read(chunk_size)
if not chunk:
break
yield chunk
bytes_remaining -= len(chunk)

def get_file_type(name):
extension = name.split(".")[-1]
if extension in ["html", "js", "json", "md", "pdf", "png", "txt", "xls"]:
return extension
return None


def list_dir(path):
items = []
for item in path.iterdir():
if item.is_dir():
items.append(
{
"title": item.name,
"file": "folder",
"id": item.as_posix(),
"children": [],
}
)
else:
file_type = get_file_type(item.name)
if file_type:
items.append(
{"title": item.name, "file": file_type, "id": item.as_posix()}
)
else:
items.append(
{"title": item.name, "file": "file", "id": item.as_posix()}
)
return items


# Semaphore to control concurrent uploads
upload_semaphore = asyncio.Semaphore(5)
# Locks to ensure each file is written in isolation
Expand Down
2 changes: 0 additions & 2 deletions zt_frontend/src/components/FileFolderDownloadDialog.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<template>
<div>
<v-dialog v-model="dialogVisible" max-width="500px" persistent>
<v-card>
<v-card-title>Confirm Download</v-card-title>
Expand Down Expand Up @@ -27,7 +26,6 @@
</v-btn>
</template>
</v-snackbar>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
Expand Down

0 comments on commit 571eb58

Please sign in to comment.