Skip to content

Feat/49/painting-process-data-simulator-service: 전착도장 데이터 전송 시뮬레이터 기능 구현#53

Merged
TaeHyunaivle merged 14 commits intomainfrom
feat/49/ecoating-backend-simulator
Aug 27, 2025
Merged

Feat/49/painting-process-data-simulator-service: 전착도장 데이터 전송 시뮬레이터 기능 구현#53
TaeHyunaivle merged 14 commits intomainfrom
feat/49/ecoating-backend-simulator

Conversation

@TaeHyunaivle
Copy link
Member

@TaeHyunaivle TaeHyunaivle commented Aug 13, 2025

✨ Description

  • 전에 작성한 모델-시뮬레이터 테스트용 시뮬레이터가 아닌 단순 데이터 전송용 시뮬레이터
  • Azure storage에서 csv 데이터를 받아 spring boot에 정해진 시간마다 전송하는 기능을 구현함

✅ Task

  • Data Simulator Service 기본 구조 및 엔드포인트(/start, /stop) 생성
  • Azure Blob Storage 연동 모듈 구현
  • Spring boot로 데이터를 전송하는 기능 구현
  • 간단한 테스트를 추가해 기능 테스트 완료
  • docker 환경에서 기능 구현

✅ Test Result

  • API 라우터, 스케줄러, Azure Storage 서비스, 백엔드 클라이언트의 핵심 로직에 대한 단위 테스트 작성 및 완료

📸 Screenshot

  • 시뮬레이터
화면 캡처 2025-08-13 105742
  • spring boot
화면 캡처 2025-08-13 105717

📌 Etc

Summary by CodeRabbit

  • New Features

    • Added root (/) and health (/health) endpoints, centralized logging, and a .env example for local setup.
  • Changes

    • Simulator now sends data to a backend on a seconds-based interval; status payload simplified. Removed model-integration, connection-test and recent-logs endpoints; start/stop failure responses simplified.
  • Documentation

    • Rebranded README, streamlined local install/run steps, and added logging guidance.
  • Tests

    • Test suite refocused and significantly trimmed; new backend and scheduler unit tests added.
  • Chores

    • Dependency list adjusted and default test discovery restored.

기존 시뮬레이터에서 더 이상 사용되지 않는 파일 및 테스트 코드를 정리
기존에 사용하던 print문을 적절한 로깅으로 대체, 기존 시뮬레이터의 settings.py를 변경
기존 시뮬레이터에서 모델 및 데이터 파일을 주고받는 로직을 제거하고 Spring Boot 서버에 데이터를 전송하도록 기능을 변경
수정에 따른 README 및 requirements 변경. .env.example 을 사용함으로써 수월하게 .env 사용 가능(Azure 액세스 키만 직접 추가하면 됨)
API 라우터, 스케줄러, Azure Storage 서비스, 백엔드 클라이언트의 핵심 로직에 대한 단위 테스트를 추가. 테스트를 위한 라이브러리 추가
파이썬 3.10버전에 맞는 numpy버전을 2.3.2에서 2.2.6으로 수정하고 .env파일에 따옴표를 빼서 오류를 방지
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 13, 2025

Walkthrough

Reorients the simulator to fetch CSV rows from Azure Blob Storage on a seconds-based schedule and POST them to a backend; adds centralized logging and an async backend HTTP client; updates settings, main app endpoints, scheduler, and Azure service; removes model-client, anomaly logger, diagnostic routes, and many legacy tests.

Changes

Cohort / File(s) Summary
Docs & Env
services/painting-process-data-simulator-service/README.md, services/painting-process-data-simulator-service/.env.example
README rebranded for backend-data-transmission; adds .env example with Azure, scheduler (seconds), backend URL, and HTTP timeout.
Config & Logging
services/painting-process-data-simulator-service/app/config/settings.py, services/painting-process-data-simulator-service/app/config/logging_config.py, services/painting-process-data-simulator-service/requirements.txt, services/painting-process-data-simulator-service/pytest.ini
Switch scheduler minutes→seconds, add backend_service_url, remove legacy model/log fields, add validators, keep log_directory; add centralized logging setup; update requirements and remove explicit pytest testpaths.
App Entrypoint & Routers
services/painting-process-data-simulator-service/app/main.py, services/painting-process-data-simulator-service/app/routers/simulator_router.py, services/painting-process-data-simulator-service/app/routers/connection_test_router.py
main.py: add logging, lifespan, root and /health endpoints, integrate simulator router. simulator_router: simplify status, remove /logs/recent and model-health composition; connection_test_router deleted.
Services (Core Logic)
services/painting-process-data-simulator-service/app/services/azure_storage.py, .../backend_client.py, .../scheduler_service.py, .../model_client.py
AzureStorageService: persistent client, logger usage, CSV loading and simulate_real_time_data updates; add module-level azure_storage. Add BackendClient async POST client and module-level backend_client. Scheduler: use seconds-based IntervalTrigger, fetch simulated data and send to backend_service_url. model_client removed.
Utilities
services/painting-process-data-simulator-service/app/utils/logger.py
Remove AnomalyLogger and global anomaly_logger instance.
Tests
services/painting-process-data-simulator-service/tests/*
Add tests for backend_client; simplify tests for azure_storage, scheduler, and simulator_router; delete many legacy tests (model_client, anomaly logger, settings, main integration).
Other service tweak
services/painting-process-equipment-defect-detection-model-service/app/routers/predict.py
On internal save-failure, returns original result instead of wrapped {"predictions": [...]} — logs error and continues returning result.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant API as FastAPI (/simulator)
  participant Scheduler
  participant Azure as AzureStorageService
  participant Backend as BackendClient
  participant Spring as Spring Boot Backend

  User->>API: POST /simulator/start
  API->>Scheduler: start(interval=seconds)
  Scheduler-->>API: started

  loop Every interval_seconds
    Scheduler->>Azure: simulate_real_time_data()
    Azure-->>Scheduler: simulated_data or None
    alt Data available
      Scheduler->>Backend: send_to_backend(data, backend_service_url)
      Backend->>Spring: HTTP POST JSON
      Spring-->>Backend: 2xx / error
      Backend-->>Scheduler: success / failure
    else No data
      Scheduler-->>Scheduler: skip
    end
  end

  User->>API: POST /simulator/stop
  API->>Scheduler: stop()
  Scheduler-->>API: stopped
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
Data Simulator Service 기본 구조 및 엔드포인트 생성 (#49)
Azure Blob Storage 연동 모듈 구현 (#49)
Spring boot로 데이터를 전송하는 기능 구현 (#49)

Out-of-scope changes

Code Change Explanation
Removal of ModelClient module (services/painting-process-data-simulator-service/app/services/model_client.py) Deleted model-client integration and tests; not required by the Azure→backend data-transmission objective.
Removal of AnomalyLogger utility (services/painting-process-data-simulator-service/app/utils/logger.py) Deleted file-write anomaly logging APIs; not part of stated objectives.
Deletion of large test suites (services/painting-process-data-simulator-service/tests/test_settings.py, .../tests/test_main.py, .../tests/test_model_client.py, .../tests/test_logger.py) Many configuration and integration tests removed, which reduces coverage beyond implementing the simulator feature.

"I twitch my whiskers to the beat, every second I hop and send a treat.
From Azure rows to backend gate, I post with joy — I cannot wait.
Logs hum soft, the scheduler hops, a rabbit courier that never stops.
I nibble bugs and trace the thread, then hop away to dream of bread.
Hooray — data sent, the meadow sings, a little rabbit flaps its wings."

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ef1b17b and eb0a50a.

📒 Files selected for processing (1)
  • services/painting-process-data-simulator-service/app/config/settings.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/painting-process-data-simulator-service/app/config/settings.py
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/49/ecoating-backend-simulator

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link

github-actions bot commented Aug 13, 2025

🤖 Smart FAST 자동 코드 리뷰

📦 변경된 서비스 (1개):

  • painting-process-data-simulator-service 서비스

🎯 PR 제목에서 서비스 자동 감지됨

  • 제목: Feat/49/painting-process-data-simulator-service: 전착도장 데이터 전송 시뮬레이터 기능 구현

📊 전체 검사 결과:

⚠️ 일부 검사에서 문제 발견 - 확인 필요

🧪 테스트 & 코드 품질 결과:

⚠️ 테스트 & 린트 상태 불명

  • 총 테스트: 10개
  • 성공: 10개 ✅
  • 커버리지: 0%

🔒 보안 스캔 결과:

⚠️ 보안 스캔 상태 불명

  • 보안 이슈가 발견되지 않았습니다 ✅

✅ 검사 체크리스트:

  • ❌ 테스트 실패
  • ✅ 코드 품질 검사 통과
  • ❌ 보안 이슈 발견

🚀 배포 정보:

  • ⚠️ 검사 실패 - 문제 해결 후 배포 가능
    • 🧪 테스트 문제 해결 필요
    • 🔒 보안 이슈 해결 필요
  • 📋 상세 로그는 Actions 탭에서 확인 가능

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🔭 Outside diff range comments (2)
services/painting-process-data-simulator-service/app/routers/simulator_router.py (1)

20-21: Don’t return raw exception messages to clients

Returning str(e) can leak internals. Log the exception server-side and return a generic message.

-    except Exception as e:
-        raise HTTPException(status_code=500, detail=f"시뮬레이터 시작 실패: {str(e)}") from e
+    except Exception as e:
+        logger.exception("시뮬레이터 시작 실패")
+        raise HTTPException(status_code=500, detail="시뮬레이터 시작 실패") from e

Add this at the top of the module (outside the selected lines):

import logging
logger = logging.getLogger(__name__)
services/painting-process-data-simulator-service/app/main.py (1)

57-62: Expose the actual backend target instead of the legacy model name.

Replace the legacy "target_model" with the configured backend URL for clarity and correctness.

Apply this diff:

-        "target_model": "painting-process-equipment-defect-detection",
+        "target_backend": settings.backend_service_url,
🧹 Nitpick comments (15)
services/painting-process-data-simulator-service/requirements.txt (1)

34-35: Consider moving test-only dependencies to a dev requirements file

pytest and pytest-asyncio inflate the runtime image and attack surface. If tests aren’t executed inside the production container, move them to requirements-dev.txt and keep this file runtime-only.

Apply this diff to slim the runtime requirements (only if tests are not run in the production image):

-pytest==8.4.1
-pytest-asyncio==1.1.0

Additionally, add a dev requirements file at services/painting-process-data-simulator-service/requirements-dev.txt:

pytest==8.4.1
pytest-asyncio==1.1.0

And update your CI to install both:

  • pip install -r services/painting-process-data-simulator-service/requirements.txt
  • pip install -r services/painting-process-data-simulator-service/requirements-dev.txt
services/painting-process-data-simulator-service/.env.example (2)

9-9: Azure Storage 연결 문자열이 비어있습니다

Azure Storage 연결 문자열은 시뮬레이터의 핵심 기능인 CSV 데이터 읽기를 위해 필수입니다. 예제 파일이지만 사용자가 설정해야 할 필수 값임을 명확히 하는 것이 좋습니다.

-AZURE_CONNECTION_STRING=
+AZURE_CONNECTION_STRING=<your-azure-storage-connection-string-here>

35-36: 백엔드 서비스 URL 설정 명확화 필요

두 개의 백엔드 URL이 제공되어 혼란을 줄 수 있습니다. 주석을 더 명확히 수정하여 각각의 사용 시나리오를 구분하는 것이 좋습니다.

-# 도커 사용시 시뮬레이터가 데이터를 전송할 Spring Boot 백엔드 서비스의 전체 URL
-BACKEND_SERVICE_URL=http://paintingprocessmonitoring:8087/equipment-data
-# BACKEND_SERVICE_URL=http://localhost:8087/equipment-data # uvicorn으로 실행할때 사용
+# Spring Boot 백엔드 서비스의 전체 URL
+# Docker Compose 환경에서 실행 시:
+BACKEND_SERVICE_URL=http://paintingprocessmonitoring:8087/equipment-data
+# 로컬 개발 환경(uvicorn)에서 실행 시:
+# BACKEND_SERVICE_URL=http://localhost:8087/equipment-data
services/painting-process-data-simulator-service/app/services/azure_storage.py (1)

79-82: 필수 컬럼 검증 로직 개선 필요

DataFrame에서 iloc로 선택한 row는 Series 객체이므로 index 속성 확인이 올바른 방법입니다. 하지만 더 명확한 에러 메시지를 위해 실제 존재하는 컬럼도 함께 출력하는 것이 좋습니다.

 # 필요한 모든 열이 존재하는지 확인
 required_columns = ['machineId', 'timeStamp', 'Thick', 'PT_jo_V_1', 'PT_jo_A_Main_1', 'PT_jo_TP']
 missing_columns = [col for col in required_columns if col not in data_row.index]
 if missing_columns:
-    raise ValueError(f"Missing required columns: {missing_columns}")
+    available_columns = list(data_row.index)
+    raise ValueError(f"Missing required columns: {missing_columns}. Available columns: {available_columns}")
services/painting-process-data-simulator-service/README.md (1)

1-1: 서비스 명칭 일관성 확인 필요

README 제목은 "Backend Simulator Service"이지만 프로젝트 구조에서는 "backend-simulator-service"로 표기되어 있습니다. 또한 PR 목적과 일치하는지 확인이 필요합니다.

-# Painting Process Backend Simulator Service
+# Painting Process Data Simulator Service
services/painting-process-data-simulator-service/app/services/backend_client.py (1)

9-9: Consider reusing a long-lived AsyncClient to reduce connection overhead

Creating a new client per call prevents connection pooling and TLS reuse. Prefer a single client per service lifetime and close it on shutdown (e.g., FastAPI lifespan event). I can help wire this if you want.

services/painting-process-data-simulator-service/tests/test_simulator_router.py (2)

13-15: Align mocked status shape with actual scheduler.get_status output or relax assertions

Real get_status includes interval_seconds and backend_service_url. Your mock returns only two keys, which may hide schema drift. Either add the fields or assert only on required keys.

Apply this diff to relax assertions (alternative provided in next comment):

-        mock.get_status.return_value = {"is_running": False, "next_run": None}
+        mock.get_status.return_value = {"is_running": False, "next_run": None}

If you prefer full alignment, import settings and include:

  • "interval_seconds": settings.scheduler_interval_seconds
  • "backend_service_url": settings.backend_service_url

39-44: Also assert that start() was awaited in the error path

Confirms the endpoint actually awaited the coroutine before producing 500.

     response = client.post("/simulator/start")
     assert response.status_code == 500
     assert "시뮬레이터 시작 실패" in response.json()["detail"]
+    mock_scheduler.start.assert_awaited_once()
services/painting-process-data-simulator-service/tests/test_azure_storage.py (2)

25-34: Cover index progression and wrap-around (optional)

With a single-row fixture, index wrap-around isn’t observable. Consider extending the fixture to two rows and asserting the second call advances selection. If you’d like, I can provide a patch to expand the fixture and assertions.


36-51: Assert that _load_dataframe resets current_index to 0

Matches production behavior and guards against regressions.

         await service._load_dataframe()
 
         mock_list.assert_awaited_once()
         mock_read.assert_awaited_once_with('test.csv')
         assert service.cached_df is not None
+        assert service.current_index == 0
services/painting-process-data-simulator-service/tests/test_scheduler_service.py (1)

27-27: Add idempotency test for start() (optional)

start() should no-op when already running. A small test will guard against regressions (e.g., double add_job/start). I can draft it if you want.

services/painting-process-data-simulator-service/app/services/scheduler_service.py (2)

54-55: Use timezone-aware ISO 8601 timestamps in logs.

Current log uses a naive local time string, which hampers cross-system correlation. Prefer ISO 8601 with timezone.

Apply this diff:

-from datetime import datetime
+from datetime import datetime, timezone
-            logger.info(f"🔄 데이터 시뮬레이션 및 전송 시작 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
+            logger.info(f"🔄 데이터 시뮬레이션 및 전송 시작 - {datetime.now(timezone.utc).isoformat(timespec='seconds')}")

Also applies to: 1-1


62-63: Propagate configurable HTTP timeout to backend calls.

httpx has a default timeout, but you already have settings.http_timeout. Wire it through to prevent indefinite waits and align with environment configuration.

Apply this diff locally:

-            await backend_client.send_to_backend(simulated_data, settings.backend_service_url)
+            await backend_client.send_to_backend(
+                simulated_data,
+                settings.backend_service_url,
+                timeout=settings.http_timeout
+            )

And update BackendClient accordingly (in app/services/backend_client.py):

# outside the selected range; for reference
from typing import Any, Dict, Optional
import httpx
import logging

logger = logging.getLogger(__name__)

class BackendClient:
    async def send_to_backend(self, data: Dict[str, Any], url: str, timeout: Optional[float] = None):
        async with httpx.AsyncClient(timeout=timeout) as client:
            try:
                response = await client.post(url, json=data)
                response.raise_for_status()
                logger.info(f"✅ 데이터 전송 성공: {response.status_code}")
            except httpx.HTTPStatusError as e:
                logger.error(f"❌ 데이터 전송 실패: {e.response.status_code} - {e.response.text}")
            except Exception as e:
                logger.error(f"❌ 데이터 전송 중 예외 발생: {e}")

backend_client = BackendClient()
services/painting-process-data-simulator-service/app/main.py (1)

41-44: Align service description with the new backend data-sending purpose.

The description still mentions a “defect detection model” simulator. Update to reflect the backend data transmission simulator.

Apply this diff:

-    description="도장 공정 설비 결함 탐지 모델을 위한 실시간 데이터 시뮬레이터",
+    description="도장 공정 설비 데이터를 백엔드로 전송하는 실시간 데이터 시뮬레이터",
services/painting-process-data-simulator-service/app/config/settings.py (1)

6-7: Optional: Treat secrets as secrets.

Consider using SecretStr for azure_connection_string to avoid accidental logging/printing. This requires minor usage changes (call .get_secret_value() where needed).

Example change in this file:

-    azure_connection_string: str
+    azure_connection_string: SecretStr

And usage (outside this file), e.g., in azure client initialization:

# outside the selected range; for reference
conn_str = settings.azure_connection_string.get_secret_value()
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7747062 and 93744bd.

📒 Files selected for processing (22)
  • services/painting-process-data-simulator-service/.env.example (1 hunks)
  • services/painting-process-data-simulator-service/README.md (3 hunks)
  • services/painting-process-data-simulator-service/app/config/logging_config.py (1 hunks)
  • services/painting-process-data-simulator-service/app/config/settings.py (1 hunks)
  • services/painting-process-data-simulator-service/app/main.py (1 hunks)
  • services/painting-process-data-simulator-service/app/routers/connection_test_router.py (0 hunks)
  • services/painting-process-data-simulator-service/app/routers/simulator_router.py (2 hunks)
  • services/painting-process-data-simulator-service/app/services/azure_storage.py (2 hunks)
  • services/painting-process-data-simulator-service/app/services/backend_client.py (1 hunks)
  • services/painting-process-data-simulator-service/app/services/model_client.py (0 hunks)
  • services/painting-process-data-simulator-service/app/services/scheduler_service.py (2 hunks)
  • services/painting-process-data-simulator-service/app/utils/logger.py (0 hunks)
  • services/painting-process-data-simulator-service/pytest.ini (0 hunks)
  • services/painting-process-data-simulator-service/requirements.txt (2 hunks)
  • services/painting-process-data-simulator-service/tests/test_azure_storage.py (1 hunks)
  • services/painting-process-data-simulator-service/tests/test_backend_client.py (1 hunks)
  • services/painting-process-data-simulator-service/tests/test_logger.py (0 hunks)
  • services/painting-process-data-simulator-service/tests/test_main.py (0 hunks)
  • services/painting-process-data-simulator-service/tests/test_model_client.py (0 hunks)
  • services/painting-process-data-simulator-service/tests/test_scheduler_service.py (1 hunks)
  • services/painting-process-data-simulator-service/tests/test_settings.py (0 hunks)
  • services/painting-process-data-simulator-service/tests/test_simulator_router.py (1 hunks)
💤 Files with no reviewable changes (8)
  • services/painting-process-data-simulator-service/pytest.ini
  • services/painting-process-data-simulator-service/tests/test_main.py
  • services/painting-process-data-simulator-service/tests/test_logger.py
  • services/painting-process-data-simulator-service/app/services/model_client.py
  • services/painting-process-data-simulator-service/app/utils/logger.py
  • services/painting-process-data-simulator-service/tests/test_model_client.py
  • services/painting-process-data-simulator-service/app/routers/connection_test_router.py
  • services/painting-process-data-simulator-service/tests/test_settings.py
🧰 Additional context used
🧬 Code Graph Analysis (7)
services/painting-process-data-simulator-service/tests/test_backend_client.py (1)
services/painting-process-data-simulator-service/app/services/backend_client.py (2)
  • BackendClient (7-17)
  • send_to_backend (8-17)
services/painting-process-data-simulator-service/app/services/scheduler_service.py (2)
services/painting-process-data-simulator-service/app/services/azure_storage.py (1)
  • simulate_real_time_data (65-101)
services/painting-process-data-simulator-service/app/services/backend_client.py (1)
  • send_to_backend (8-17)
services/painting-process-data-simulator-service/tests/test_scheduler_service.py (3)
services/painting-process-data-simulator-service/app/services/scheduler_service.py (4)
  • SimulatorScheduler (11-79)
  • start (16-39)
  • stop (41-49)
  • _simulate_and_send_data (51-65)
services/painting-process-data-simulator-service/app/services/azure_storage.py (1)
  • simulate_real_time_data (65-101)
services/painting-process-data-simulator-service/app/services/backend_client.py (1)
  • send_to_backend (8-17)
services/painting-process-data-simulator-service/tests/test_simulator_router.py (1)
services/painting-process-data-simulator-service/app/services/scheduler_service.py (3)
  • get_status (67-79)
  • start (16-39)
  • stop (41-49)
services/painting-process-data-simulator-service/tests/test_azure_storage.py (1)
services/painting-process-data-simulator-service/app/services/azure_storage.py (3)
  • AzureStorageService (10-122)
  • simulate_real_time_data (65-101)
  • _load_dataframe (103-122)
services/painting-process-data-simulator-service/app/routers/simulator_router.py (1)
services/painting-process-data-simulator-service/app/services/scheduler_service.py (1)
  • get_status (67-79)
services/painting-process-data-simulator-service/app/main.py (1)
services/painting-process-data-simulator-service/app/config/logging_config.py (1)
  • setup_logging (6-15)
🪛 dotenv-linter (3.3.0)
services/painting-process-data-simulator-service/.env.example

[warning] 17-17: [ExtraBlankLine] Extra blank line detected


[warning] 28-28: [ExtraBlankLine] Extra blank line detected


[warning] 33-33: [ExtraBlankLine] Extra blank line detected


[warning] 38-38: [ExtraBlankLine] Extra blank line detected

🔇 Additional comments (10)
services/painting-process-data-simulator-service/requirements.txt (5)

12-12: Bump to charset-normalizer 3.4.3 looks good

This aligns with requests 2.32.x constraints (<4.0). No compatibility concerns on this pin.


49-49: yarl 1.20.1 pin is appropriate for aiohttp 3.12.x

This version pairing is commonly used and should be compatible with aiohttp 3.12.15.


1-49: No known vulnerabilities detected in key dependencies

A quick OSV scan of FastAPI, Starlette, HTTPX, Requests, and Cryptography returned zero known issues for the pinned versions.

Optional best practices to further harden your supply chain:

  • Generate a lock/constraints file with hashes (e.g., via pip-compile or pip-tools) and install using --require-hashes
  • Schedule periodic CVE audits to catch newly disclosed vulnerabilities

24-26: Confirmed Python 3.10 Base Image Meets NumPy 2.2 Requirement
NumPy 2.2.x requires Python >=3.10, and services/painting-process-data-simulator-service/Dockerfile uses FROM python:3.10. No changes required.


15-16: Dependencies pins satisfy upstream constraints

A programmatic check against PyPI’s requires_dist for fastapi 0.116.1, httpx 0.28.1, azure-storage-blob 12.26.0, and aiohttp 3.12.15 (covering their Starlette, Pydantic, HTTPCore, AnyIO, SniffIO, etc. requirements) returned “OK”—no version conflicts detected. No changes required.

services/painting-process-data-simulator-service/app/services/azure_storage.py (1)

3-3: 비동기 import 사용되었으나 동기 클라이언트 사용 중

azure.storage.blob.aio에서 BlobServiceClient를 import하고 있으나, 실제로는 Line 16에서 동기 방식의 from_connection_string을 사용하고 있습니다. 비동기 처리를 위해서는 올바른 import와 초기화가 필요합니다.

-from azure.storage.blob.aio import BlobServiceClient
+from azure.storage.blob.aio import BlobServiceClient as AsyncBlobServiceClient

그리고 초기화 부분도 수정이 필요합니다:

-            self.blob_service_client = BlobServiceClient.from_connection_string(self.connection_string)
+            self.blob_service_client = AsyncBlobServiceClient.from_connection_string(self.connection_string)

Likely an incorrect or invalid review comment.

services/painting-process-data-simulator-service/tests/test_azure_storage.py (1)

3-3: All async declarations verified

Both list_data_files (line 22) and read_csv_data (line 43) in azure_storage.py are declared as async def and are correctly awaited by callers (lines 106 and 115). No changes to the tests are needed.

services/painting-process-data-simulator-service/tests/test_scheduler_service.py (3)

14-16: Good isolation of scheduler internals via patching

Patching add_job/start/shutdown keeps this a pure unit test without touching APScheduler internals. Assertions are sufficient.


28-43: Robust and focused verification of data path (azure -> backend)

Nice use of AsyncMock to assert awaited calls and correct URL propagation.


45-59: Good negative-path test

Verifies that no backend call happens when no data is available.

Comment on lines +12 to +18
with patch('httpx.AsyncClient') as mock_async_client:
mock_client_instance = AsyncMock()
mock_async_client.return_value.__aenter__.return_value = mock_client_instance

await client.send_to_backend(test_data, test_url)

mock_client_instance.post.assert_awaited_once_with(test_url, json=test_data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

HTTP 에러 상황에 대한 테스트 추가 필요

현재 테스트는 성공 케이스만 검증하고 있습니다. BackendClient의 에러 처리 로직도 테스트해야 합니다.

HTTP 에러나 네트워크 예외 상황에 대한 추가 테스트 케이스를 생성하시겠습니까? 예를 들어:

  • HTTP 500 에러 응답 처리
  • 네트워크 연결 실패
  • 타임아웃 발생

다음과 같은 테스트를 추가할 수 있습니다:

@pytest.mark.asyncio
async def test_send_to_backend_http_error():
    """Test that the backend client handles HTTP errors correctly."""
    client = BackendClient()
    test_data = {"test_key": "test_value"}
    test_url = "http://test-backend.com/api"

    with patch('httpx.AsyncClient') as mock_async_client:
        mock_client_instance = AsyncMock()
        mock_async_client.return_value.__aenter__.return_value = mock_client_instance
        
        # Simulate HTTP 500 error
        mock_response = AsyncMock()
        mock_response.raise_for_status.side_effect = httpx.HTTPStatusError(
            "Server Error", request=None, response=AsyncMock(status_code=500, text="Internal Server Error")
        )
        mock_client_instance.post.return_value = mock_response
        
        # Should not raise exception but log the error
        await client.send_to_backend(test_data, test_url)
        mock_client_instance.post.assert_awaited_once_with(test_url, json=test_data)
🤖 Prompt for AI Agents
In services/painting-process-data-simulator-service/tests/test_backend_client.py
around lines 12 to 18, add tests covering error paths for
BackendClient.send_to_backend: create separate async test cases that patch
'httpx.AsyncClient' similar to the existing success test but simulate (1) an
HTTP error by making the mocked post() return a response whose
raise_for_status() raises httpx.HTTPStatusError (with a mock
response.status_code 500), (2) a network/connect failure by having post() raise
httpx.ConnectError, and (3) a timeout by having post() raise httpx.ReadTimeout
or httpx.TimeoutException; in each test assert the mocked post was awaited with
the expected url and json and that send_to_backend does not propagate the
exception (i.e., completes) and optionally asserts the logger was called with an
error message.

Comment on lines +18 to 24
def test_get_simulator_status(mock_scheduler):
"""Test GET /simulator/status endpoint."""
response = client.get("/simulator/status")

# Then
assert response.status_code == 200
data = response.json()
assert data["is_running"] is False
assert "model_services_health" not in data
assert response.json() == {"is_running": False, "next_run": None}
mock_scheduler.get_status.assert_called_once()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make the status assertion resilient to additional fields

Avoid strict equality so additions to the status schema don’t break the test.

-    assert response.json() == {"is_running": False, "next_run": None}
-    mock_scheduler.get_status.assert_called_once()
+    payload = response.json()
+    assert payload.get("is_running") is False
+    assert "next_run" in payload
+    mock_scheduler.get_status.assert_called_once()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_get_simulator_status(mock_scheduler):
"""Test GET /simulator/status endpoint."""
response = client.get("/simulator/status")
# Then
assert response.status_code == 200
data = response.json()
assert data["is_running"] is False
assert "model_services_health" not in data
assert response.json() == {"is_running": False, "next_run": None}
mock_scheduler.get_status.assert_called_once()
def test_get_simulator_status(mock_scheduler):
"""Test GET /simulator/status endpoint."""
response = client.get("/simulator/status")
assert response.status_code == 200
payload = response.json()
assert payload.get("is_running") is False
assert "next_run" in payload
mock_scheduler.get_status.assert_called_once()
🤖 Prompt for AI Agents
In
services/painting-process-data-simulator-service/tests/test_simulator_router.py
around lines 18 to 24, the test uses strict equality on the entire response JSON
which will break if extra fields are added; change the assertion to only verify
the expected keys and their values (e.g., assert
response.json().get("is_running") is False and response.json().get("next_run")
is None) or assert that the expected items are a subset of
response.json().items(), so the test remains resilient to additional fields
while still validating the important values.

@TaeHyunaivle TaeHyunaivle changed the title Feat/49/ecoating backend simulator: 데이터 전송 시뮬레이터 기능 구현 Feat/49/ecoating backend simulator: 전착도장 데이터 전송 시뮬레이터 기능 구현 Aug 13, 2025
@TaeHyunaivle TaeHyunaivle changed the title Feat/49/ecoating backend simulator: 전착도장 데이터 전송 시뮬레이터 기능 구현 Feat/49/painting-process-data-simulator-service: 전착도장 데이터 전송 시뮬레이터 기능 구현 Aug 13, 2025
래빗코드의 조언에 따라 코드를 리팩토링하고 잠재된 문제 및 오류들을 감지하고 대응
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🔭 Outside diff range comments (1)
services/painting-process-data-simulator-service/app/routers/simulator_router.py (1)

23-24: Hide internal error details in /start response (mirror /stop).

Currently returns the exception message to clients. Log internally and return a generic message instead.

Apply this diff:

-    except Exception as e:
-        raise HTTPException(status_code=500, detail=f"시뮬레이터 시작 실패: {str(e)}") from e
+    except Exception:
+        logger.exception("시뮬레이터 시작 실패")
+        raise HTTPException(status_code=500, detail="시뮬레이터 시작 실패")
♻️ Duplicate comments (2)
services/painting-process-data-simulator-service/app/config/settings.py (1)

2-2: Good adoption of URL typing and interval validation (matches prior feedback).

Using AnyHttpUrl for backend_service_url and validating scheduler_interval_seconds > 0 address configuration safety concerns raised earlier.

Also applies to: 13-13, 16-18, 25-31

services/painting-process-data-simulator-service/README.md (1)

16-16: 이전 리뷰 사항 반영 확인: 디렉토리명 일치 OK.

프로젝트 구조에 "painting-process-data-simulator-service/"로 수정되어 이전 불일치 이슈가 해결되었습니다. 좋습니다.

🧹 Nitpick comments (10)
services/painting-process-data-simulator-service/app/config/settings.py (1)

5-7: Treat the Azure connection string as a secret to avoid accidental logging.

Typing azure_connection_string as SecretStr helps prevent accidental exposure in logs and reprs.

Apply this diff:

-from pydantic import AnyHttpUrl, field_validator  
+from pydantic import AnyHttpUrl, field_validator, SecretStr
@@
-    azure_connection_string: str
+    azure_connection_string: SecretStr
services/painting-process-data-simulator-service/app/config/logging_config.py (1)

10-16: Optional: add log rotation to prevent unbounded log growth.

RotatingFileHandler avoids filling the disk in long-running containers.

Example (outside current hunk):

from logging.handlers import RotatingFileHandler

handlers = [logging.StreamHandler(sys.stdout)]
try:
    handlers.append(
        RotatingFileHandler(
            f"{settings.log_directory}/service.log",
            maxBytes=5_000_000,
            backupCount=5,
        )
    )
except OSError as e:
    logging.warning("RotatingFileHandler creation failed; using stdout-only logging. (%s)", e)
services/painting-process-data-simulator-service/app/services/backend_client.py (1)

7-26: Optional: reuse a single AsyncClient for connection pooling.

Creating a new AsyncClient per request prevents keep-alive reuse and adds overhead. Manage a client lifecycle on app startup/shutdown (FastAPI lifespan) or expose aclose() on the client.

Example refactor (outside current hunk):

class BackendClient:
    def __init__(self) -> None:
        self._client = httpx.AsyncClient(timeout=httpx.Timeout(settings.http_timeout))
    async def aclose(self) -> None:
        await self._client.aclose()
    async def send_to_backend(self, data: Dict[str, Any], url: str) -> bool:
        try:
            response = await self._client.post(url, json=data)
            response.raise_for_status()
            logger.info("✅ 데이터 전송 성공: %s", response.status_code)
            return True
        except httpx.HTTPStatusError as e:
            logger.error("❌ 데이터 전송 실패(HTTP %s)", getattr(e.response, "status_code", "unknown"))
            return False
        except httpx.RequestError as e:
            logger.error("❌ 네트워크 오류로 데이터 전송 실패: %r", e)
            return False
        except Exception:
            logger.exception("❌ 데이터 전송 중 예외 발생")
            return False

Wire BackendClient.aclose() into the app’s lifespan to cleanly close on shutdown.

services/painting-process-data-simulator-service/app/services/scheduler_service.py (3)

29-31: Externalize misfire_grace_time to settings for configurability.

Hardcoding 30 makes tuning harder across environments.

Apply this diff here:

-                misfire_grace_time=30,    # seconds to allow a delayed run
+                misfire_grace_time=settings.scheduler_misfire_grace_seconds,  # seconds

And add to Settings (in app/config/settings.py, example outside current hunk):

# 스케줄러 설정
scheduler_misfire_grace_seconds: int = 30

66-67: Surface send outcome to logs/metrics.

send_to_backend returns a bool but the result is ignored. Log failures (or consider a retry/backoff strategy later).

Apply this diff:

-            await backend_client.send_to_backend(simulated_data, settings.backend_service_url)
+            ok = await backend_client.send_to_backend(simulated_data, settings.backend_service_url)
+            if not ok:
+                logger.warning("⚠️ 백엔드 전송 실패 (재시도 미구현)")

38-40: Minor: avoid logging full backend URL unless necessary.

The router already strips backend_service_url from public responses; consider avoiding logging the full URL here if logs may be exposed. Log host or path only.

Example:

from urllib.parse import urlparse
target = urlparse(str(settings.backend_service_url))
logger.info("🎯 대상 백엔드 서비스: %s://%s%s", target.scheme, target.netloc, target.path)
services/painting-process-data-simulator-service/README.md (4)

9-11: 라이브러리 표기 및 정확성 정리(HTTPX → httpx).

공식 패키지명은 소문자 "httpx"입니다. README의 표기와 requirements.txt 내용이 일치하도록 맞춰주세요.

적용 제안(diff):

-- **Spring Boot 백엔드 연동**: `HTTPX` 클라이언트를 사용하여 Spring Boot 백엔드의 API를 비동기적으로 호출합니다.
+- **Spring Boot 백엔드 연동**: `httpx` 클라이언트를 사용하여 Spring Boot 백엔드의 API를 비동기적으로 호출합니다.

36-46: venv 활성화 명령 주석 제거.

Linux/macOS 활성화 명령이 주석 처리되어 있어 혼란을 줄 수 있습니다.

적용 제안(diff):

-    # macOS/Linux
-    # source venv/bin/activate
+    # macOS/Linux
+    source venv/bin/activate

66-72: 엔드포인트 사용 예시(cURL)와 요청 스키마를 명확히 해주세요.

/simulator/start의 요청 본문(필요한 경우), 각 엔드포인트의 응답 스키마/예시를 제공하면 소비자 측 혼선을 줄일 수 있습니다. 또한 존재한다면 /health 엔드포인트도 함께 문서화하세요.

추가 제안(diff; 표 아래 예시 삽입):

 | `GET`       | `/simulator/status`| 현재 스케줄러의 상태를 확인합니다. |
 
+#### 사용 예시
+```bash
+# Swagger UI
+open http://localhost:8011/docs
+
+# 시뮬레이터 시작 (요청 본문이 필요 없다면 -d 생략)
+curl -X POST http://localhost:8011/simulator/start \
+  -H "Content-Type: application/json" \
+  -d '{"intervalSeconds": 5}'
+
+# 상태 확인
+curl http://localhost:8011/simulator/status
+
+# 시뮬레이터 중지
+curl -X POST http://localhost:8011/simulator/stop
+```
+
+> 참고: intervalSeconds, 대상 Blob 범위 등 런타임 파라미터를 API로 설정할 수 없는 경우, 환경 변수로 구성하세요(위 .env 섹션 참조).
+
+> (선택) 헬스체크 엔드포인트(`/health` 또는 `/`)가 존재하면 문서화해 주세요.

74-81: 로그 보존/회전 정책 및 경로 설정 명시.

로그 파일이 무한 성장하지 않도록 회전 정책(예: RotatingFileHandler)과 LOG_DIRECTORY 설정/권한 이슈를 문서화하세요. 컨테이너에서는 volume 마운트를 권장합니다.

추가 제안(diff):

   - **파일**: 로그는 `logs/service.log` 파일에도 저장되어, 서비스 실행 이력을 확인할 수 있습니다.
+  - **회전 정책**: 로그 파일 크기/개수 제한을 설정하여 디스크 점유를 방지하세요(예: 10MB × 5개 순환).
+  - **경로 설정**: `LOG_DIRECTORY` 환경 변수로 로그 디렉토리를 지정할 수 있습니다. 컨테이너 사용 시 `-v $(pwd)/logs:/app/logs`로 호스트에 마운트하는 것을 권장합니다.

확인 요청:

  • logging_config.py에서 회전 핸들러 사용 여부와 LOG_DIRECTORY 반영 여부를 확인해 README와 일치시켜 주세요.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93744bd and 9bdb9e9.

📒 Files selected for processing (8)
  • services/painting-process-data-simulator-service/README.md (3 hunks)
  • services/painting-process-data-simulator-service/app/config/logging_config.py (1 hunks)
  • services/painting-process-data-simulator-service/app/config/settings.py (2 hunks)
  • services/painting-process-data-simulator-service/app/main.py (1 hunks)
  • services/painting-process-data-simulator-service/app/routers/simulator_router.py (2 hunks)
  • services/painting-process-data-simulator-service/app/services/azure_storage.py (2 hunks)
  • services/painting-process-data-simulator-service/app/services/backend_client.py (1 hunks)
  • services/painting-process-data-simulator-service/app/services/scheduler_service.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • services/painting-process-data-simulator-service/app/main.py
  • services/painting-process-data-simulator-service/app/services/azure_storage.py
🧰 Additional context used
🧬 Code Graph Analysis (2)
services/painting-process-data-simulator-service/app/services/scheduler_service.py (2)
services/painting-process-data-simulator-service/app/services/azure_storage.py (1)
  • simulate_real_time_data (71-107)
services/painting-process-data-simulator-service/app/services/backend_client.py (1)
  • send_to_backend (8-24)
services/painting-process-data-simulator-service/app/routers/simulator_router.py (1)
services/painting-process-data-simulator-service/app/services/scheduler_service.py (1)
  • get_status (71-83)
🪛 Ruff (0.12.2)
services/painting-process-data-simulator-service/app/routers/simulator_router.py

36-36: Undefined name logger

(F821)

🔇 Additional comments (2)
services/painting-process-data-simulator-service/app/routers/simulator_router.py (1)

9-12: Good: internal URL removed from public /status.

Popping backend_service_url before returning avoids leaking internal endpoints.

services/painting-process-data-simulator-service/app/services/scheduler_service.py (1)

51-53: Non-blocking shutdown looks good.

Using asyncio.to_thread avoids blocking the event loop when shutting down the scheduler.

Comment on lines +35 to +37
except Exception as e:
logger.exception("시뮬레이터 중지 실패")
raise HTTPException(status_code=500, detail="시뮬레이터 중지 실패") from e No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix NameError: logger is undefined.

Static analysis flagged logger as undefined. Define a module logger to use logger.exception safely.

Add these lines near the top of the file (after imports):

import logging
logger = logging.getLogger(__name__)
🧰 Tools
🪛 Ruff (0.12.2)

36-36: Undefined name logger

(F821)

🤖 Prompt for AI Agents
In
services/painting-process-data-simulator-service/app/routers/simulator_router.py
around lines 35 to 37, the exception handler uses logger.exception but no module
logger is defined; add an import for the logging module and define a
module-level logger (e.g., import logging and logger =
logging.getLogger(__name__)) near the top of the file after the existing imports
so logger.exception can be called safely.

@@ -1,35 +1,30 @@
# Painting Process Equipment Simulator Service
# Painting Process Backend Simulator Service
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

문서 타이틀을 서비스 명칭과 일치시키세요.

PR/디렉토리명은 "painting-process-data-simulator-service"인데, 제목은 "Backend Simulator"로 되어 있습니다. 명칭 혼용은 혼란을 유발합니다.

적용 제안(diff):

-# Painting Process Backend Simulator Service
+# Painting Process Data Simulator Service
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Painting Process Backend Simulator Service
# Painting Process Data Simulator Service
🤖 Prompt for AI Agents
services/painting-process-data-simulator-service/README.md lines 1-1: README 제목이
디렉토리 및 서비스명과 일치하지 않아 혼동을 유발합니다; 파일 상단의 제목을 현재 디렉토리명/서비스명인
"painting-process-data-simulator-service" 또는 더 읽기 쉬운 서비스명(예: "Painting Process
Data Simulator Service")로 변경하여 PR 및 디렉토리명과 일치시키세요.

Comment on lines +20 to +27
│ │ ├── settings.py # Pydantic-settings를 이용한 환경 변수 및 설정 관리
│ │ └── logging_config.py # 서비스 로깅 설정
│ ├── routers/
│ │ ├── simulator_router.py # 시뮬레이터 시작/중지/상태 확인 API
│ │ └── test_connection_router.py # 외부 서비스(Azure, 모델) 연결 테스트 API
│ ├── services/
│ │ ├── scheduler_service.py # APScheduler를 사용한 핵심 스케줄링 로직
│ │ ├── model_client.py # 모델 예측 서비스 API 호출 클라이언트
│ │ └── azure_storage.py # Azure Blob Storage 데이터 처리 서비스
│ └── utils/
│ └── logger.py # 이상 및 정상 로그 기록 유틸리티
├── logs/ # 시뮬레이션 결과 로그가 저장되는 디렉토리
│ │ └── simulator_router.py # 시뮬레이터 시작/중지/상태 확인 API
│ └── services/
│ ├── scheduler_service.py # APScheduler를 사용한 핵심 스케줄링 로직
│ ├── backend_client.py # Spring Boot 백엔드 API 호출 클라이언트
│ └── azure_storage.py # Azure Blob Storage 데이터 처리 서비스
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

설정 항목(환경 변수) 문서화 보강 제안.

settings.py에서 관리하는 주요 설정값(예: scheduler_interval_seconds, backend_service_url, log_directory 등)의 키명/기본값/유효성 제약을 README에 명시해 주세요. 운영/도커 배포 시 필수 정보입니다.

원하는 경우, .env 섹션 아래에 "환경 변수 요약" 표를 추가하는 패치를 제안할 수 있습니다.

🤖 Prompt for AI Agents
In services/painting-process-data-simulator-service/README.md around lines 20 to
27, add documentation for the key environment settings managed in settings.py:
list each env var name (e.g., scheduler_interval_seconds, backend_service_url,
log_directory, azure_blob_connection_string, storage_container,
scheduler_max_workers), their default values, expected types/validation rules
(ranges, required formats, URL pattern, integer min/max), and whether they are
mandatory for production/docker; include an ".env" subsection with a concise "환경
변수 요약" table showing key, default, required, and brief description so operators
can copy into Docker or deployment manifests.

│ ├── scheduler_service.py # APScheduler를 사용한 핵심 스케줄링 로직
│ ├── backend_client.py # Spring Boot 백엔드 API 호출 클라이언트
│ └── azure_storage.py # Azure Blob Storage 데이터 처리 서비스
├── .env # Azure 연결 문자열 등 민감한 환경 변수 파일
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

보안/운영 혼동 방지: .env 대신 .env.example 표시 권장.

프로젝트 트리에 .env를 직접 표기하면 커밋 오해를 유발합니다. 예시 파일(.env.example)을 트리에 노출하고, .env는 gitignore 대상임을 명시하세요.

적용 제안(diff):

-├── .env                             # Azure 연결 문자열 등 민감한 환경 변수 파일
+├── .env.example                     # 환경 변수 예시 파일 (.env는 git에 커밋하지 않음)

또한 .gitignore에 .env가 포함되어 있는지 확인 부탁드립니다.


🏁 Script executed:

#!/bin/bash
# Locate all .gitignore files and check for .env entries
find . -type f -name ".gitignore" | while read gitignore; do
  echo "Checking $gitignore"
  if grep -qE '(^|/)\.env($|[[:space:]])' "$gitignore"; then
    echo "  ✅ .env is already listed in $gitignore"
  else
    echo "  ❌ .env is NOT listed in $gitignore"
  fi
done

Length of output: 267


🏁 Script executed:

#!/bin/bash
# Locate the README for the painting-process-data-simulator-service
target=$(find . -type f -name README.md | grep painting-process-data-simulator-service)
echo "Found README at: $target"
echo "Lines with '.env':"
grep -n "├── .env" "$target" || echo "No matches found"

Length of output: 521


Replace .env entry in service README with .env.example

The .env file is already listed in .gitignore, so please update the tree diagram in the service’s README to refer to an example file instead:

• File: services/painting-process-data-simulator-service/README.md
– Line 28: change the .env entry to .env.example

Suggested patch:

--- a/services/painting-process-data-simulator-service/README.md
+++ b/services/painting-process-data-simulator-service/README.md
@@ -28,1 +28,1 @@
-├── .env                             # Azure 연결 문자열 등 민감한 환경 변수 파일
+├── .env.example                     # 환경 변수 예시 파일 (.env는 git에 커밋하지 않음)

Ensure you also include a real services/painting-process-data-simulator-service/.env.example with placeholder values.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
├── .env # Azure 연결 문자열 등 민감한 환경 변수 파일
├── .env.example # 환경 변수 예시 파일 (.env는 git에 커밋하지 않음)
🤖 Prompt for AI Agents
In services/painting-process-data-simulator-service/README.md around line 28,
the tree diagram currently shows a private `.env` file; change that entry to
`.env.example` and update the README accordingly, and add a new
services/painting-process-data-simulator-service/.env.example file containing
placeholder values for each required environment variable (do not include real
secrets), ensuring the example matches the variables used by the service and
that `.env` remains gitignored.

Comment on lines 52 to 57
3. **.env 파일 설정**:
프로젝트 루트에 `.env` 파일을 생성하고, Azure Storage 연결 문자열을 추가합니다.
프로젝트 루트에 `.env` 파일을 생성하고, Azure Storage 연결 문자열과 백엔드 서비스 URL을 추가합니다.
```env
AZURE_CONNECTION_STRING="<Your_Azure_Storage_Connection_String>"
BACKEND_SERVICE_URL="<Your_Backend_Service_URL>"
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

.env 예시에 필수/권장 변수 추가.

스케줄러 주기와 로그 디렉토리 등 핵심 설정을 함께 예시로 제공하세요. 운영 시 초기 장애를 줄여줍니다.

적용 제안(diff):

     AZURE_CONNECTION_STRING="<Your_Azure_Storage_Connection_String>"
     BACKEND_SERVICE_URL="<Your_Backend_Service_URL>"
+    # 전송 주기(초). 예: 5
+    SCHEDULER_INTERVAL_SECONDS=5
+    # 로그 디렉토리(컨테이너/호스트 모두 쓰기 가능 경로)
+    LOG_DIRECTORY="logs"
+    # (선택) Azure Blob 선택을 위한 컨테이너/경로 설정이 필요한 경우 아래 키 사용
+    # AZURE_BLOB_CONTAINER="your-container"
+    # AZURE_BLOB_PREFIX="path/prefix/"

확인 요청:

  • settings.py에서 실제 사용하는 키명이 위와 동일한지 검증 필요합니다. 불일치 시 README를 settings와 동일하게 맞춰주세요.

🏁 Script executed:

#!/bin/bash
# Locate all settings.py files in the repo
echo "=== settings.py files ==="
fd --hidden --glob settings.py

# Search for environment variable accesses in the simulator service
echo "=== getenv and environ usage in service folder ==="
grep -R --color=always -nE "os\.getenv|os\.environ" services/painting-process-data-simulator-service

# Search specifically for scheduler and log-related keys
echo "=== Scheduler & Log keys ==="
grep -R -nE "SCHEDULER|LOG_DIRECTORY|BLOB_CONTAINER|BLOB_PREFIX" -n services/painting-process-data-simulator-service

Length of output: 731


🏁 Script executed:

#!/bin/bash
# Display the settings.py for painting-process-data-simulator-service
sed -n '1,200p' services/painting-process-data-simulator-service/app/config/settings.py

Length of output: 1066


.env 예시: 필수/권장 변수 추가 및 settings.py 키 이름 동기화

README의 .env 예시에 services/painting-process-data-simulator-service/app/config/settings.py에서 사용하는 모든 환경 변수 키를 반영해주세요. 주요 변경 사항:

  • AZURE_CONTAINER_NAME, PAINTING_DATA_FOLDER, BATCH_SIZE, HTTP_TIMEOUT 항목을 추가하여 기본값과 함께 예시 제공
  • 기존 제안된 AZURE_BLOB_CONTAINER/AZURE_BLOB_PREFIX 대신, 실제 사용하는 AZURE_CONTAINER_NAME으로 맞춤
  • SCHEDULER_INTERVAL_SECONDSLOG_DIRECTORY는 기본값 30초/“logs”로 유지

적용할 diff 예시 (파일: services/painting-process-data-simulator-service/README.md, 해당 섹션 교체):

  AZURE_CONNECTION_STRING="<Your_Azure_Storage_Connection_String>"
  BACKEND_SERVICE_URL="<Your_Backend_Service_URL>"
+# Azure Blob 컨테이너 이름 (기본: "simulator-data")
+AZURE_CONTAINER_NAME="simulator-data"
+# 페인팅 프로세스 데이터 저장 폴더 (기본: "painting-process-equipment")
+PAINTING_DATA_FOLDER="painting-process-equipment"
+# 스케줄러 주기(초) (기본: 30)
+SCHEDULER_INTERVAL_SECONDS=30
+# 배치 사이즈 (기본: 10)
+BATCH_SIZE=10
+# HTTP 타임아웃(초) (기본: 30)
+HTTP_TIMEOUT=30
+# 로그 디렉토리 (기본: "logs")
+LOG_DIRECTORY="logs"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
3. **.env 파일 설정**:
프로젝트 루트에 `.env` 파일을 생성하고, Azure Storage 연결 문자열을 추가합니다.
프로젝트 루트에 `.env` 파일을 생성하고, Azure Storage 연결 문자열과 백엔드 서비스 URL을 추가합니다.
```env
AZURE_CONNECTION_STRING="<Your_Azure_Storage_Connection_String>"
BACKEND_SERVICE_URL="<Your_Backend_Service_URL>"
```
3. **.env 파일 설정**:
프로젝트 루트에 `.env` 파일을 생성하고, Azure Storage 연결 문자열과 백엔드 서비스 URL을 추가합니다.
🤖 Prompt for AI Agents
In services/painting-process-data-simulator-service/README.md around lines 52 to
57, the .env example is missing several variables used by
app/config/settings.py; update the example to replace
AZURE_BLOB_CONTAINER/AZURE_BLOB_PREFIX with AZURE_CONTAINER_NAME and add
AZURE_CONTAINER_NAME, PAINTING_DATA_FOLDER, BATCH_SIZE, HTTP_TIMEOUT (with
sensible defaults), and keep SCHEDULER_INTERVAL_SECONDS=30 and
LOG_DIRECTORY="logs"; ensure the README .env sample keys exactly match the names
used in settings.py and include brief default values for each variable.

Comment on lines 61 to 63
uvicorn app.main:app --reload --port 8011
```
실행 후 `http://localhost:8011/docs`에서 API 문서를 확인할 수 있습니다.

### 4.2. Docker를 이용한 실행

**사전 준비:** `painting-process-equipment-defect-detection-model-service`가 `model-service`라는 컨테이너 이름으로 동일한 Docker 네트워크(`smart-fast-net`)에서 실행 중이어야 합니다.

1. **Docker 네트워크 생성** (이미 생성했다면 생략):
```bash
docker network create smart-fast-net
```

2. **모델 서비스 실행** (이미 실행 중이라면 생략):
```bash
# 모델 서비스 디렉토리에서 실행
docker build -t model-service .
docker run --name model-service --network smart-fast-net -p 8001:8001 model-service
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Docker 실행 가이드 추가 제안.

PR 요약에 “Docker 환경(포트 8011)”이 강조되었으나, README에는 Docker 빌드/실행 예시가 없습니다. 도커 사용자 가이드가 필요합니다.

추가 제안(diff; 섹션 하단에 삽입):

  uvicorn app.main:app --reload --port 8011 
 }
 
+### Docker로 실행
+
+```bash
+# 이미지 빌드
+docker build -t painting-process-data-simulator:latest .
+
+# (권장) .env 사용 시
+docker run --rm -p 8011:8011 --env-file ./.env \
+  -v $(pwd)/logs:/app/logs \
+  --name painting-data-simulator \
+  painting-process-data-simulator:latest
+
+# (대안) 개별 환경변수 전달
+# docker run --rm -p 8011:8011 \
+#   -e AZURE_CONNECTION_STRING="..." \
+#   -e BACKEND_SERVICE_URL="..." \
+#   -e SCHEDULER_INTERVAL_SECONDS=5 \
+#   -e LOG_DIRECTORY="logs" \
+#   -v $(pwd)/logs:/app/logs \
+#   painting-process-data-simulator:latest
+```
🤖 Prompt for AI Agents
In services/painting-process-data-simulator-service/README.md around lines
61-63, the README lacks the Docker build and run examples referenced in the PR;
add a new subsection at the bottom showing how to build the image and run it
with port 8011 mapped, an example using --env-file and mounting ./logs to
/app/logs, and an alternative showing individual -e environment variables for
AZURE_CONNECTION_STRING, BACKEND_SERVICE_URL, SCHEDULER_INTERVAL_SECONDS and
LOG_DIRECTORY; ensure commands include docker build -t
painting-process-data-simulator:latest ., docker run --rm -p 8011:8011
--env-file ./.env -v $(pwd)/logs:/app/logs --name painting-data-simulator
painting-process-data-simulator:latest and commented example of docker run with
explicit -e variables.

래빗코드의 조언에 따라 코드를 리팩토링하고 잠재된 문제 및 오류들을 감지하고 대응
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
services/painting-process-data-simulator-service/app/config/settings.py (2)

22-24: Make sure the log directory exists and consider rotation

Ensure your logging bootstrap creates settings.log_directory (and sets sane rotation/retention) to avoid runtime failures and log bloat.

Example (outside this file, e.g., in logging_config.py):

from pathlib import Path

def ensure_log_dir(path: str) -> None:
    Path(path).mkdir(parents=True, exist_ok=True)

33-38: Make the shared validator’s error message field-specific

The current message mentions both fields even when only one fails. Use FieldValidationInfo to tailor the message.

Apply this diff:

-    @field_validator("batch_size", "http_timeout")
-    @classmethod
-    def _positive_ints(cls, v: int) -> int:
-        if v <= 0:
-            raise ValueError("batch_size and http_timeout must be > 0")
-        return v
+    @field_validator("batch_size", "http_timeout")
+    @classmethod
+    def _positive_ints(cls, v: int, info: "FieldValidationInfo") -> int:
+        if v <= 0:
+            raise ValueError(f"{info.field_name} must be > 0")
+        return v

Add this import outside the selected range:

from pydantic import FieldValidationInfo
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9bdb9e9 and a40d320.

📒 Files selected for processing (3)
  • services/painting-process-data-simulator-service/app/config/logging_config.py (1 hunks)
  • services/painting-process-data-simulator-service/app/config/settings.py (2 hunks)
  • services/painting-process-data-simulator-service/app/services/backend_client.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • services/painting-process-data-simulator-service/app/config/logging_config.py
  • services/painting-process-data-simulator-service/app/services/backend_client.py
🔇 Additional comments (2)
services/painting-process-data-simulator-service/app/config/settings.py (2)

2-2: Strong typing for URL and validator import — LGTM

Good move switching to AnyHttpUrl and adopting field_validator; this tightens config correctness at load time.


13-15: ✅ All painting-process-data-simulator references updated to seconds-based interval

Verified in services/painting-process-data-simulator-service that:

  • app/config/settings.py defines only scheduler_interval_seconds
  • app/main.py logs use settings.scheduler_interval_seconds
  • app/services/scheduler_service.py uses IntervalTrigger(seconds=…) and returns "interval_seconds"
  • .env.example contains SCHEDULER_INTERVAL_SECONDS=… and no minute-based keys remain

No further changes required.

Comment on lines 16 to 18
# Backend 서비스 설정
backend_service_url: AnyHttpUrl = "http://localhost:8087/equipment-data"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Ensure backend endpoint path isn’t double-appended

Default value includes a path (/equipment-data). Confirm the HTTP client doesn’t also append the same segment, which would yield duplicate paths.

Run:

If you intend backend_service_url to include the full endpoint (vs. just base URL), consider renaming to backend_endpoint_url for clarity.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Where is backend_service_url used?"
rg -n -S 'backend_service_url' -A3 -B3

echo
echo "Where does '/equipment-data' appear (possible hard-coded appends)?"
rg -n -S '/equipment-data' -A2 -B2

Length of output: 6466


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Search for backend_client definition/import:"
rg -n --color always 'backend_client' -A5 -B5 services/painting-process-data-simulator-service/app

Length of output: 4266


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Displaying backend_client.py:"
sed -n '1,200p' services/painting-process-data-simulator-service/app/services/backend_client.py

Length of output: 1261


backend_service_url is used verbatim—no double-appended path

We confirmed in services/backend_client.py that send_to_backend calls client.post(url, …) without modifying the URL, so supplying "http://localhost:8087/equipment-data" will not lead to duplicated segments.

• No code concatenates "/equipment-data" onto settings.backend_service_url.
• The default value already includes the full endpoint path.

For clarity (optional):

  • Rename settings.backend_service_urlsettings.backend_endpoint_url to signal it holds the full URL, not just a base.
🤖 Prompt for AI Agents
In services/painting-process-data-simulator-service/app/config/settings.py
around lines 16-18, the setting name backend_service_url is misleading because
it contains the full endpoint path; rename it to backend_endpoint_url and keep
the default value "http://localhost:8087/equipment-data". Update every
usage/import across the codebase (including services/backend_client.py, env/CI
config, tests, and docs) to reference settings.backend_endpoint_url, and update
any environment variable names or README references accordingly to avoid
breaking runtime configuration.

Comment on lines +25 to +32
# Validators
@field_validator("scheduler_interval_seconds")
@classmethod
def _positive_interval(cls, v: int) -> int:
if v <= 0:
raise ValueError("scheduler_interval_seconds must be > 0")
return v

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add Azure container name validation to fail fast

A malformed container name will cause runtime errors with Azure. Validate per Azure rules (3–63 chars; lowercase letters, numbers, hyphens; no leading/trailing hyphen; no consecutive hyphens).

Add a validator (and import re) alongside the existing validators:

import re

@field_validator("azure_container_name")
@classmethod
def _valid_container_name(cls, v: str) -> str:
    # must be 3-63 chars, lowercase letters/numbers/hyphens, no leading/trailing hyphen, no consecutive hyphens
    if not re.match(r'^(?!-)(?!.*--)[a-z0-9-]{3,63}(?<!-)$', v):
        raise ValueError("azure_container_name must be 3-63 chars, use lowercase letters/numbers/hyphens, no leading/trailing '-' or consecutive '-'")
    return v

Optionally, alias the connection string to the common Azure env name for convenience:

# Example (requires pydantic Field):
# azure_connection_string: str = Field(validation_alias="AZURE_STORAGE_CONNECTION_STRING")
🤖 Prompt for AI Agents
In services/painting-process-data-simulator-service/app/config/settings.py
around lines 25 to 32, there is no validation for azure_container_name which can
lead to runtime Azure errors; add an import for re at the top and implement a
field_validator for "azure_container_name" that uses a regex to enforce 3–63
characters, only lowercase letters/numbers/hyphens, no leading/trailing hyphen
and no consecutive hyphens (raise ValueError with a clear message on failure),
and optionally add a Field with
validation_alias="AZURE_STORAGE_CONNECTION_STRING" for the connection string if
you want the env var alias.

래빗코드가 리팩토링한 코드 중 시뮬레이터의 backend_client.py 파일에서 httpx 라이브러리를 호출할 때,
{predictions: [result]} 으로 출력하지 않고 시뮬레이터처럼 단순 json로 출력되게 수정
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
services/painting-process-equipment-defect-detection-model-service/app/routers/predict.py (1)

25-37: Offload blocking inference to a threadpool to avoid starving the event loop

run_analysis looks CPU/blocking (model inference). Calling it directly in an async route will block the event loop and degrade throughput. FastAPI recommends run_in_threadpool for this case.

Apply:

@@
-    try:
-        # run_analysis 함수를 호출하여 분석을 수행합니다.
-        result = run_analysis(input_data, model, explainer, config)
+    try:
+        # run_analysis 함수를 호출하여 분석을 수행합니다.
+        result = await run_in_threadpool(run_analysis, input_data, model, explainer, config)

Add this import once:

from starlette.concurrency import run_in_threadpool
🧹 Nitpick comments (5)
services/painting-process-equipment-defect-detection-model-service/app/routers/predict.py (5)

114-119: Avoid blocking the event loop while iterating rows — use run_in_threadpool per invocation

Row-by-row synchronous calls inside an async endpoint will block the loop for large files. Offloading to the threadpool keeps the server responsive.

-                result = run_analysis(input_data, model, explainer, config)
+                result = await run_in_threadpool(run_analysis, input_data, model, explainer, config)

62-67: Case-insensitive CSV extension check

Minor: accept uppercase extensions.

-    if not file.filename.endswith('.csv'):
+    if not file.filename.lower().endswith('.csv'):

99-103: Guard against header/row length mismatches to avoid IndexError

Using enumerate(row) with mapped_header[i] will raise if a row has fewer columns than the header. Use zip and optionally log a warning.

-            row_data = {mapped_header[i]: val for i, val in enumerate(row)}
+            row_data = {col: val for col, val in zip(mapped_header, row)}
+            if len(row) != len(mapped_header):
+                logger.warning(f"{index}행: 헤더/행 길이 불일치. header={len(mapped_header)}, row={len(row)}")

33-37: Replace print statements with structured logging

Use the centralized logger for consistency and observability. Map severity to context (error/exception for failures, info for progress, warning for partial row failures).

Example changes:

-        print(f"분석 중 오류 발생: {e}")
+        logger.exception("분석 중 오류 발생")

-        print(f"이슈 로그를 저장하는 중에 오류가 발생했습니다: {e}")
+        logger.warning(f"이슈 로그 저장 실패: {e}")

-        print(f"{file.filename} 파일 처리를 시작합니다...")
+        logger.info(f"{file.filename} 파일 처리를 시작합니다...")

-                print(f"{index}행 처리 중 HTTPException 발생: {e.detail}")
+                logger.warning(f"{index}행 처리 중 HTTPException 발생: {e.detail}")

-                print(f"{index}행 처리 중 오류 발생: {e}")
+                logger.warning(f"{index}행 처리 중 오류 발생: {e}")

-        print(f"파일 처리 실패: {e}")
+        logger.exception(f"파일 처리 실패: {e}")

If not already configured, add at module top:

import logging
logger = logging.getLogger(__name__)

Also applies to: 47-47, 97-97, 123-126, 136-136


48-50: No internal callers expect the old predictions wrapper—response schema change is safe

We ran ripgrep across the repo for both ['predictions'] and get('predictions') and found no references in Python files.

  • If you have external API consumers, make sure to update your API docs or bump the version to avoid surprises.
  • Optional: to surface whether logs were persisted and switch to structured logging, you can apply the following refactor in
    services/painting-process-equipment-defect-detection-model-service/app/routers/predict.py around lines 48–50:
@@ services/painting-process-equipment-defect-detection-model-service/app/routers/predict.py:48
-    # 이슈가 감지되면 로그를 저장하고 결과를 반환합니다.
-    try:
-        save_issue_log(result, config)
-    except Exception as e:
-        print(f"이슈 로그를 저장하는 중에 오류가 발생했습니다: {e}")
-
-    return result
+    # 이슈가 감지되면 로그를 저장하고 결과를 반환합니다.
+    from fastapi.responses import JSONResponse
+    import logging
+    logger = logging.getLogger(__name__)
+
+    log_saved = True
+    try:
+        save_issue_log(result, config)
+    except Exception as e:
+        logger.warning(f"이슈 로그 저장 실패: {e}")
+        log_saved = False
+
+    return JSONResponse(content=result, headers={"X-Log-Save": "1" if log_saved else "0"})
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7605f8e and 1f76b96.

📒 Files selected for processing (1)
  • services/painting-process-equipment-defect-detection-model-service/app/routers/predict.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
services/painting-process-equipment-defect-detection-model-service/app/routers/predict.py (1)
services/press-defect-detection-model-service/app/routers/predict.py (1)
  • predict_inspection_batch (132-166)

spring boot 포트를 8091로 변경한것을 반영
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
services/painting-process-data-simulator-service/.env.example (5)

8-16: Clarify Azure auth and add a concrete example (keep secrets out of VCS).

Recommend adding a commented example connection string for quick local setup, and clarifying that non-local environments should prefer managed identity or AAD where possible. This improves DX without risking secret leakage.

Apply this non-breaking doc-only enhancement:

 # Azure Storage 계정의 연결 문자열 (필수)
 AZURE_CONNECTION_STRING=
+# 예시(로컬 개발용, 실제 키는 절대 커밋 금지):
+# DefaultEndpointsProtocol=https;AccountName=<account>;AccountKey=<key>;EndpointSuffix=core.windows.net
+# 비-로컬 환경에서는 Azure AD/Managed Identity 사용을 권장합니다.
 
 # 데이터를 읽어올 컨테이너 이름
 AZURE_CONTAINER_NAME=simulator-data

34-36: Localhost will not work inside a container; provide a Docker Compose-friendly example.

Using localhost from the simulator container will point to itself, not the backend. Add a commented alternative using the Compose service name to prevent common misconfig.

Proposed tweak:

-# 도커 사용시 시뮬레이터가 데이터를 전송할 Spring Boot 백엔드 서비스의 전체 URL
-BACKEND_SERVICE_URL=http://localhost:8091/equipment-data
+# 백엔드 서비스의 전체 URL
+# - 로컬(호스트에서 백엔드 실행): localhost 사용
+BACKEND_SERVICE_URL=http://localhost:8091/equipment-data
+# - Docker Compose 사용(서비스명이 backend 인 경우 예시):
+# BACKEND_SERVICE_URL=http://backend:8091/equipment-data

If your Compose file uses a different service name/port, update the commented example accordingly.


17-17: Trim extra blank lines flagged by dotenv-linter.

Keep a single blank line between sections for readability; remove extras to satisfy linting.

- 
- 
+# (하나의 공백 줄만 유지)
 
 #==============================
 # 스케줄러 설정
 #==============================
@@
- 
- 
+# (하나의 공백 줄만 유지)
 
 #==============================
 # 백엔드 서비스 설정
 #==============================
- 
- 
+# (하나의 공백 줄만 유지)
@@
- 
+# (하나의 공백 줄만 유지)

Also applies to: 28-28, 33-33, 37-37


22-27: Field Validators Present for Scheduler Parameters

  • The Settings class in
    services/painting-process-data-simulator-service/app/config/settings.py
    already defines @field_validator methods to ensure:

    • scheduler_interval_seconds > 0
    • batch_size > 0
  • If you still want to guard against excessively large values, you can optionally introduce upper-bound checks (e.g. via le= in the Field or additional validators).


1-44: Env var names align with Settings – no mismatches found

All environment variable names in .env.example map exactly to the Pydantic ​Settings attributes (uppercase names vs. snake_case fields), so there’s no risk of silent defaults or runtime failures due to naming mismatches.

• AZURE_CONNECTION_STRING → azure_connection_string
• AZURE_CONTAINER_NAME → azure_container_name
• PAINTING_DATA_FOLDER → painting_data_folder
• SCHEDULER_INTERVAL_SECONDS → scheduler_interval_seconds
• BATCH_SIZE → batch_size
• BACKEND_SERVICE_URL → backend_service_url
• HTTP_TIMEOUT → http_timeout

However, note that the default port for backend_service_url differs between .env.example (:8091) and settings.py (:8087). You may want to standardize one of these defaults for consistency.

Since there are currently no tests covering loading of these vars in painting-process-data-simulator-service, consider adding a simple test to verify that required env vars are loaded (and that invalid values are rejected by your validators).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1f76b96 and ef1b17b.

📒 Files selected for processing (1)
  • services/painting-process-data-simulator-service/.env.example (1 hunks)
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
services/painting-process-data-simulator-service/.env.example

[warning] 17-17: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)


[warning] 28-28: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)


[warning] 33-33: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)


[warning] 37-37: [ExtraBlankLine] Extra blank line detected

(ExtraBlankLine)

🔇 Additional comments (2)
services/painting-process-data-simulator-service/.env.example (2)

1-3: Good starter header and guidance.

Clear instruction to copy as .env for local development. No issues.


42-43: Timeout is sensible and scoped; LGTM.

30s is a reasonable default; can be tuned per environment.

@TaeHyunaivle TaeHyunaivle merged commit 57f9dde into main Aug 27, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 데이터 전송 시뮬레이터 기능 구현

1 participant