diff --git a/.github/workflows/build-docker-images.yml b/.github/workflows/build-docker-images.yml index 7519d620..ad4d720b 100644 --- a/.github/workflows/build-docker-images.yml +++ b/.github/workflows/build-docker-images.yml @@ -7,6 +7,16 @@ on: - dev - demo - hotfix + paths: + - 'src/backend/**' + - 'src/frontend/**' + - 'docker/**' + - '.github/workflows/build-docker-images.yml' + - '.github/workflows/build-docker.yml' + - 'infra/**' + - 'scripts/**' + - 'azure.yaml' + - '.github/workflows/deploy.yml' pull_request: branches: - main @@ -18,6 +28,16 @@ on: - ready_for_review - reopened - synchronize + paths: + - 'src/backend/**' + - 'src/frontend/**' + - 'docker/**' + - '.github/workflows/build-docker-images.yml' + - '.github/workflows/build-docker.yml' + - 'infra/**' + - 'scripts/**' + - 'azure.yaml' + - '.github/workflows/deploy.yml' merge_group: workflow_dispatch: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index dd0e0d83..0a131fa0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -438,4 +438,4 @@ jobs: if: always() run: | az logout - echo "Logged out from Azure." + echo "Logged out from Azure." \ No newline at end of file diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 7010cc66..7f9673c4 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -1,6 +1,13 @@ name: PyLint -on: [push] +on: + push: + paths: + - '**/*.py' + - '**/requirements.txt' + - '**/pyproject.toml' + - '.flake8' + - '.github/workflows/pylint.yml' jobs: lint: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index da96ebbb..9c02ca76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,13 @@ on: - main - dev - demo + paths: + - 'src/backend/**/*.py' + - 'src/tests/backend/**' + - '.github/workflows/test.yml' + - 'src/backend/requirements.txt' + - 'src/frontend/requirements.txt' + - 'src/backend/pyproject.toml' pull_request: types: - opened @@ -16,6 +23,13 @@ on: - main - dev - demo + paths: + - 'src/backend/**/*.py' + - 'src/tests/backend/**' + - '.github/workflows/test.yml' + - 'src/backend/requirements.txt' + - 'src/frontend/requirements.txt' + - 'src/backend/pyproject.toml' jobs: # frontend_tests: diff --git a/src/backend/api/api_routes.py b/src/backend/api/api_routes.py index 44718f8a..8a3be72f 100644 --- a/src/backend/api/api_routes.py +++ b/src/backend/api/api_routes.py @@ -279,6 +279,7 @@ async def batch_status_updates( try: await websocket.receive_text() except asyncio.TimeoutError: + # TimeoutError is ignored to keep the WebSocket connection open without receiving data pass except WebSocketDisconnect: diff --git a/src/backend/common/database/cosmosdb.py b/src/backend/common/database/cosmosdb.py index 35071709..7ff043f4 100644 --- a/src/backend/common/database/cosmosdb.py +++ b/src/backend/common/database/cosmosdb.py @@ -352,10 +352,6 @@ async def update_batch_entry( batch["file_count"] = file_count await self.batch_container.replace_item(item=batch_id, body=batch) - # if isinstance(status, ProcessStatus): - # self.logger.info(f"Updated batch {batch_id} to status {status.value}") - # else: - # self.logger.info(f"Updated batch {batch_id} to status {status}") return batch except Exception as e: diff --git a/src/backend/common/services/batch_service.py b/src/backend/common/services/batch_service.py index 59b1b411..f467820a 100644 --- a/src/backend/common/services/batch_service.py +++ b/src/backend/common/services/batch_service.py @@ -175,6 +175,8 @@ async def delete_batch(self, batch_id: UUID, user_id: str): self.logger.info(f"Successfully deleted batch with ID: {batch_id}") return {"message": "Batch deleted successfully", "batch_id": str(batch_id)} + else: + return {"message": "Batch not found", "batch_id": str(batch_id)} async def delete_file(self, file_id: UUID, user_id: str): """Delete a file and its logs, and update batch file count.""" diff --git a/src/backend/common/storage/blob_azure.py b/src/backend/common/storage/blob_azure.py index da53b2c3..33b126f6 100644 --- a/src/backend/common/storage/blob_azure.py +++ b/src/backend/common/storage/blob_azure.py @@ -52,7 +52,7 @@ async def upload_file( raise try: # Upload the file - upload_results = blob_client.upload_blob( # noqa: F841 + blob_client.upload_blob( file_content, content_type=content_type, metadata=metadata, diff --git a/src/backend/sql_agents/helpers/comms_manager.py b/src/backend/sql_agents/helpers/comms_manager.py index 7080e38f..fddc0a7a 100644 --- a/src/backend/sql_agents/helpers/comms_manager.py +++ b/src/backend/sql_agents/helpers/comms_manager.py @@ -270,17 +270,3 @@ async def cleanup(self): except Exception as e: self.logger.error("Error during cleanup: %s", str(e)) - - def __del__(self): - """Destructor to ensure cleanup if not explicitly called.""" - try: - # Only attempt cleanup if there's an active event loop - loop = asyncio.get_running_loop() - if loop and not loop.is_closed(): - # Schedule cleanup as a task - loop.create_task(self.cleanup()) - except RuntimeError: - # No event loop running, can't clean up asynchronously - self.logger.warning("No event loop available for cleanup in destructor") - except Exception as e: - self.logger.error("Error in destructor cleanup: %s", str(e)) diff --git a/src/backend/sql_agents/tools/src/Program.cs b/src/backend/sql_agents/tools/src/Program.cs index b728eefc..06c5f213 100644 --- a/src/backend/sql_agents/tools/src/Program.cs +++ b/src/backend/sql_agents/tools/src/Program.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.SqlServer.TransactSql.ScriptDom; using Newtonsoft.Json; @@ -42,18 +43,14 @@ static void Main(string[] args) IList errors = ParseSqlQuery(sqlQuery); - var errorList = new List>(); - - foreach (var error in errors) - { - var errorDict = new Dictionary + var errorList = errors + .Select(error => new Dictionary { { "Line", error.Line }, { "Column", error.Column }, { "Error", error.Message } - }; - errorList.Add(errorDict); - } + }) + .ToList(); string jsonOutput = JsonConvert.SerializeObject(errorList, Formatting.Indented); Console.WriteLine(jsonOutput); diff --git a/src/frontend/frontend_server.py b/src/frontend/frontend_server.py index 199d1a66..c53af042 100644 --- a/src/frontend/frontend_server.py +++ b/src/frontend/frontend_server.py @@ -4,7 +4,7 @@ from dotenv import load_dotenv from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import FileResponse, HTMLResponse +from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles # Load environment variables from .env file diff --git a/src/frontend/src/api/utils.tsx b/src/frontend/src/api/utils.tsx index 95ce5459..50341195 100644 --- a/src/frontend/src/api/utils.tsx +++ b/src/frontend/src/api/utils.tsx @@ -414,7 +414,7 @@ export const renderErrorSection = (batchSummary, expandedSections, setExpandedSe export const renderErrorContent = (batchSummary) => { // Group errors by file - const errorFiles = batchSummary.files.filter(file => file.error_count && file.error_count); + const errorFiles = batchSummary.files.filter(file => file.error_count); if (errorFiles.length === 0) { return (
diff --git a/src/frontend/src/components/batchHistoryPanel.tsx b/src/frontend/src/components/batchHistoryPanel.tsx index 0e0259d9..49fd10cb 100644 --- a/src/frontend/src/components/batchHistoryPanel.tsx +++ b/src/frontend/src/components/batchHistoryPanel.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef } from "react"; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { Card, Spinner, Tooltip } from "@fluentui/react-components"; import { useNavigate } from "react-router-dom"; import ConfirmationDialog from "../commonComponents/ConfirmationDialog/confirmationDialogue"; @@ -21,7 +21,7 @@ interface BatchHistoryItem { status: string; } const HistoryPanel: React.FC = ({ isOpen, onClose }) => { - const headers = {} + const headers = {}; const [batchHistory, setBatchHistory] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -81,46 +81,6 @@ const HistoryPanel: React.FC = ({ isOpen, onClose }) => { } }; - // Function to categorize batches - const categorizeBatches = () => { - const now = new Date(); - const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; - - // Get start of "Today", "Past 7 days", and "Past 30 days" in LOCAL time - const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()); - const past7DaysStart = new Date(todayStart); - const past30DaysStart = new Date(todayStart); - - past7DaysStart.setDate(todayStart.getDate() - 7); - past30DaysStart.setDate(todayStart.getDate() - 30); - - const todayBatches: BatchHistoryItem[] = []; - const past7DaysBatches: BatchHistoryItem[] = []; - const past30DaysBatches: BatchHistoryItem[] = []; - - batchHistory.forEach(batch => { - // Convert UTC timestamp to user's local date - const updatedAtUTC = new Date(batch.created_at); - const updatedAtLocal = new Date(updatedAtUTC.toLocaleString("en-US", { timeZone: userTimeZone })); - - // Extract only the local **date** part for comparison - const updatedDate = new Date(updatedAtLocal.getFullYear(), updatedAtLocal.getMonth(), updatedAtLocal.getDate()); - - // Categorize based on **exact day comparison** - if (updatedDate.getTime() === todayStart.getTime()) { - todayBatches.push(batch); - } else if (updatedDate.getTime() >= past7DaysStart.getTime()) { - past7DaysBatches.push(batch); - } else if (updatedDate.getTime() >= past30DaysStart.getTime()) { - past30DaysBatches.push(batch); - } - }); - - return { todayBatches, past7DaysBatches, past30DaysBatches }; - }; - - // const { todayBatches, past7DaysBatches, past30DaysBatches } = categorizeBatches(); - const deleteBatchFromHistory = (batchId: string) => { // Get the current URL path const currentPath = window.location.pathname; diff --git a/src/frontend/src/components/bottomBar.tsx b/src/frontend/src/components/bottomBar.tsx index 3041a988..c2702d2e 100644 --- a/src/frontend/src/components/bottomBar.tsx +++ b/src/frontend/src/components/bottomBar.tsx @@ -1,6 +1,5 @@ import { Button, Card, Dropdown, DropdownProps, Option } from "@fluentui/react-components" -import React, { useState } from "react" -import { useNavigate } from "react-router-dom" +import React from "react" // Define possible upload states const UploadState = { diff --git a/src/frontend/src/components/uploadButton.tsx b/src/frontend/src/components/uploadButton.tsx index a833f669..b6d90a1e 100644 --- a/src/frontend/src/components/uploadButton.tsx +++ b/src/frontend/src/components/uploadButton.tsx @@ -68,7 +68,6 @@ const FileUploadZone: React.FC = ({ const [uploadIntervals, setUploadIntervals] = useState<{ [key: string]: ReturnType }>({}); const [showCancelDialog, setShowCancelDialog] = useState(false); const [showLogoCancelDialog, setShowLogoCancelDialog] = useState(false); - const [uploadState, setUploadState] = useState<'IDLE' | 'UPLOADING' | 'COMPLETED'>('IDLE'); const [batchId, setBatchId] = useState(uuidv4()); const [allUploadsComplete, setAllUploadsComplete] = useState(false); const [fileLimitExceeded, setFileLimitExceeded] = useState(false); @@ -98,7 +97,6 @@ const FileUploadZone: React.FC = ({ } } - setUploadState(newState); onUploadStateChange?.(newState); }, [uploadingFiles, onUploadStateChange]); diff --git a/src/frontend/src/main.jsx b/src/frontend/src/main.jsx index 7ae29774..da9d477d 100644 --- a/src/frontend/src/main.jsx +++ b/src/frontend/src/main.jsx @@ -50,7 +50,7 @@ const Main = () => { const baseURL = config.API_URL.replace(/\/api$/, ''); // Remove '/api' if it appears at the end console.log('Checking connection to:', baseURL); try { - const response = await fetch(`${baseURL}/health`); + await fetch(`${baseURL}/health`); } catch (error) { console.error('Error connecting to backend:', error); } diff --git a/src/frontend/src/pages/batchView.tsx b/src/frontend/src/pages/batchView.tsx index ba8b9de2..df83a65f 100644 --- a/src/frontend/src/pages/batchView.tsx +++ b/src/frontend/src/pages/batchView.tsx @@ -33,7 +33,7 @@ import PanelRight from "../components/Panels/PanelRight"; import PanelRightToolbar from "../components/Panels/PanelRightToolbar"; import BatchHistoryPanel from "../components/batchHistoryPanel"; import ConfirmationDialog from "../commonComponents/ConfirmationDialog/confirmationDialogue"; -import { determineFileStatus, filesLogsBuilder, renderErrorSection, useStyles, renderFileError, filesErrorCounter, completedFiles, hasFiles, fileErrorCounter, BatchSummary, fileWarningCounter } from "../api/utils"; +import { determineFileStatus, filesLogsBuilder, renderErrorSection, useStyles, renderFileError, filesErrorCounter, completedFiles, hasFiles, fileErrorCounter, BatchSummary } from "../api/utils"; export const History = bundleIcon(HistoryFilled, HistoryRegular); import { format } from "sql-formatter"; @@ -73,7 +73,6 @@ const BatchStoryPage = () => { const [selectedFileId, setSelectedFileId] = useState(""); const [expandedSections, setExpandedSections] = useState(["errors"]); const [batchSummary, setBatchSummary] = useState(null); - const [selectedFileContent, setSelectedFileContent] = useState(""); const [selectedFileTranslatedContent, setSelectedFileTranslatedContent] = useState(""); @@ -209,40 +208,6 @@ const BatchStoryPage = () => { fetchFileContent(); }, [selectedFileId]); - - const renderWarningContent = () => { - if (!expandedSections.includes("warnings")) return null; - - if (!batchSummary) return null; - - // Group warnings by file - const warningFiles = files.filter(file => file.warningCount && file.warningCount > 0 && file.id !== "summary"); - - if (warningFiles.length === 0) { - return ( -
- No warnings found. -
- ); - } - - return ( -
- {warningFiles.map((file, fileIndex) => ( -
-
- {file.name} ({file.warningCount}) - source -
-
- Warning in file processing. See file for details. -
-
- ))} -
- ); - }; - const renderContent = () => { // Define header content based on selected file const renderHeader = () => { @@ -385,7 +350,7 @@ const BatchStoryPage = () => { } // Show the summary page when summary is selected - if (selectedFile.id === "summary" && batchSummary) { + if (selectedFile.id === "summary") { // Check if there are no errors and all files are processed successfully const noErrors = (batchSummary.error_count === 0); const allFilesProcessed = (batchSummary.completed_files === batchSummary.total_files); diff --git a/src/frontend/src/pages/landingPage.tsx b/src/frontend/src/pages/landingPage.tsx index b3707393..b86200e6 100644 --- a/src/frontend/src/pages/landingPage.tsx +++ b/src/frontend/src/pages/landingPage.tsx @@ -33,7 +33,6 @@ export const LandingPage = (): JSX.Element => { const dispatch = useDispatch(); // Add dispatch hook const [selectedTargetLanguage, setSelectedTargetLanguage] = useState(["T-SQL"]); const [selectedCurrentLanguage, setSelectedCurrentLanguage] = useState(["Informix"]); - const batchHistoryRef = useRef<{ triggerDeleteAll: () => void } | null>(null); const isPanelOpen = useSelector((state: RootState) => state.historyPanel.isOpen); const navigate = useNavigate(); diff --git a/src/frontend/src/pages/modernizationPage.tsx b/src/frontend/src/pages/modernizationPage.tsx index f5d6d04e..7f232148 100644 --- a/src/frontend/src/pages/modernizationPage.tsx +++ b/src/frontend/src/pages/modernizationPage.tsx @@ -477,13 +477,13 @@ const getPrintFileStatus = (status: string): string => { const ModernizationPage = () => { const { batchId } = useParams<{ batchId: string }>(); - const navigate = useNavigate() + const navigate = useNavigate(); // Redux state to listen for start processing completion const batchState = useSelector((state: any) => state.batch); const [batchSummary, setBatchSummary] = useState(null); - const styles = useStyles() + const styles = useStyles(); const [text, setText] = useState(""); const [isPanelOpen, setIsPanelOpen] = React.useState(false); // Add state management @@ -492,16 +492,14 @@ const ModernizationPage = () => { // State for the loading component const [showLoading, setShowLoading] = useState(true); - const [loadingError, setLoadingError] = useState(null); const [selectedFilebg, setSelectedFile] = useState(null); - const [selectedFileId, setSelectedFileId] = React.useState("") + const [selectedFileId, setSelectedFileId] = React.useState(""); const [fileId, setFileId] = React.useState(""); - const [expandedSections, setExpandedSections] = React.useState([]) - const [progressPercentage, setProgressPercentage] = useState(0); + const [expandedSections, setExpandedSections] = React.useState([]); const [allFilesCompleted, setAllFilesCompleted] = useState(false); + const [progressPercentage, setProgressPercentage] = useState(0); const [isZipButtonDisabled, setIsZipButtonDisabled] = useState(true); const [fileLoading, setFileLoading] = useState(false); - const [selectedFileTranslatedContent, setSelectedFileTranslatedContent] = useState(""); const [lastActivityTime, setLastActivityTime] = useState(Date.now()); const [pageLoadTime] = useState(Date.now()); @@ -517,11 +515,9 @@ const ModernizationPage = () => { if (!selectedFile || !selectedFile.translatedCode) { setFileLoading(true); const newFileUpdate = await fetchFileFromAPI(selectedFile?.fileId || ""); - setSelectedFileTranslatedContent(newFileUpdate.translatedContent); setFileLoading(false); } else { - setSelectedFileTranslatedContent(selectedFile.translatedCode); } } catch (err) { @@ -799,8 +795,6 @@ const ModernizationPage = () => { } }, [batchId]); - const highestProgressRef = useRef(0); - const currentProcessingFileRef = useRef(null); //new PT FR ends @@ -1251,7 +1245,7 @@ useEffect(() => { } // Show the full summary page only when all files are completed and summary is selected - if (allFilesCompleted && selectedFile?.id === "summary") { + if (selectedFile?.id === "summary") { const completedCount = files.filter(file => file.status === "completed" && file.file_result !== "error" && file.id !== "summary").length; const totalCount = files.filter(file => file.id !== "summary").length; const errorCount = selectedFile.errorCount || 0; diff --git a/src/frontend/vite.config.js b/src/frontend/vite.config.js index 0d05262f..d239b70e 100644 --- a/src/frontend/vite.config.js +++ b/src/frontend/vite.config.js @@ -1,6 +1,5 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' -import path from 'path' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ diff --git a/src/tests/backend/common/storage/blob_azure_test.py b/src/tests/backend/common/storage/blob_azure_test.py index 68e5ad0d..afc76e7e 100644 --- a/src/tests/backend/common/storage/blob_azure_test.py +++ b/src/tests/backend/common/storage/blob_azure_test.py @@ -28,7 +28,6 @@ def mock_blob_service(): @pytest.fixture def blob_storage(mock_blob_service): """Fixture to initialize AzureBlobStorage with mocked dependencies""" - service_client, container_client, blob_client = mock_blob_service return AzureBlobStorage(account_name="test_account", container_name="test_container") diff --git a/tests/e2e-test/pages/HomePage.py b/tests/e2e-test/pages/HomePage.py index d863ac18..6e184a83 100644 --- a/tests/e2e-test/pages/HomePage.py +++ b/tests/e2e-test/pages/HomePage.py @@ -19,6 +19,7 @@ class HomePage(BasePage): FILE_PROCESSED_MSG = "//span[normalize-space()='3 files processed successfully']" def __init__(self, page): + super().__init__(page) self.page = page def validate_home_page(self): diff --git a/tests/e2e-test/pages/loginPage.py b/tests/e2e-test/pages/loginPage.py index 0b412556..c8f37aa9 100644 --- a/tests/e2e-test/pages/loginPage.py +++ b/tests/e2e-test/pages/loginPage.py @@ -11,6 +11,7 @@ class LoginPage(BasePage): PERMISSION_ACCEPT_BUTTON = "//input[@type='submit']" def __init__(self, page): + super().__init__() self.page = page def authenticate(self, username, password): diff --git a/tests/e2e-test/tests/conftest.py b/tests/e2e-test/tests/conftest.py index b9919e6d..d24d06c6 100644 --- a/tests/e2e-test/tests/conftest.py +++ b/tests/e2e-test/tests/conftest.py @@ -34,17 +34,6 @@ def pytest_html_report_title(report): report.title = "Automation_CodeGen" -# Add a column for descriptions -# def pytest_html_results_table_header(cells): -# cells.insert(1, html.th("Description")) - - -# def pytest_html_results_table_row(report, cells): -# cells.insert( -# 1, html.td(report.description if hasattr(report, "description") else "") -# ) - - log_streams = {} @@ -120,14 +109,3 @@ def rename_duration_column(): # Register this function to run after everything is done atexit.register(rename_duration_column) - - -# Add logs and docstring to report -# @pytest.hookimpl(hookwrapper=True) -# def pytest_runtest_makereport(item, call): -# outcome = yield -# report = outcome.get_result() -# report.description = str(item.function.__doc__) -# os.makedirs("logs", exist_ok=True) -# extra = getattr(report, "extra", []) -# report.extra = extra