Refactor: Improve modularity and readability of backend services.#69
Refactor: Improve modularity and readability of backend services.#69
Conversation
This commit introduces several changes to enhance the structure and maintainability of the Node.js and Python backend services. Key changes include: - Refactored `server/routes.ts`: Split into multiple, more focused route files (e.g., `emailRoutes.ts`, `aiRoutes.ts`). - Refactored `server/python_backend/main.py`: Divided FastAPI endpoints into separate route modules (e.g., `email_routes.py`, `ai_routes.py`). - Refactored `server/python_nlp/nlp_engine.py`: Renamed analyzer classes to model classes (e.g., `SentimentModel`) and ensured they encapsulate model loading and prediction logic. - Consolidated Logic: Centralized the AI-driven category name to category ID matching logic within the Python `AdvancedAIEngine`. This removes redundancy previously present in the Node.js AI routes. - Organized Python Models: Consolidated Pydantic models used in the Python backend into `server/python_backend/models.py`. - Added Unit Tests: Introduced new unit tests for the refactored components in both Node.js (Jest) and Python (FastAPI TestClient, pytest) to ensure correctness and prevent regressions. These changes aim to make the codebase easier to understand, navigate, and maintain, while also reducing redundancy and improving the separation of concerns.
Reviewer's GuideThis PR refactors both the Node.js and Python backends for better modularity and readability by splitting monolithic route files into focused modules, centralizing Pydantic schemas, consolidating AI/NLP logic into dedicated engine classes with category-matching support, updating the Python–TypeScript bridge to carry category IDs, and expanding unit test coverage across both stacks. Entity relationship diagram for AI category matching and email assignmenterDiagram
EMAILS {
int id
string subject
string content
int category_id
int confidence
}
CATEGORIES {
int id
string name
}
EMAILS }o--|| CATEGORIES : "assigned to"
AI_ANALYSIS {
int email_id
int category_id
float confidence
}
AI_ANALYSIS ||--|| CATEGORIES : "matches"
AI_ANALYSIS ||--|| EMAILS : "analyzes"
Class diagram for refactored Python NLP model componentsclassDiagram
class NLPEngine {
- SentimentModel sentiment_analyzer
- TopicModel topic_analyzer
- IntentModel intent_analyzer
- UrgencyModel urgency_analyzer
+ analyze_email(subject, content)
}
class SentimentModel {
- model
- has_nltk
+ analyze(text)
}
class TopicModel {
- model
+ analyze(text)
}
class IntentModel {
- model
+ analyze(text)
}
class UrgencyModel {
- model
+ analyze(text)
}
NLPEngine --> SentimentModel
NLPEngine --> TopicModel
NLPEngine --> IntentModel
NLPEngine --> UrgencyModel
Class diagram for AdvancedAIEngine and category matchingclassDiagram
class AdvancedAIEngine {
- NLPEngine nlp_engine
+ initialize()
+ analyze_email(subject, content, db)
- _match_category_id(ai_categories, db)
}
class DatabaseManager {
+ get_all_categories()
}
AdvancedAIEngine --> NLPEngine
AdvancedAIEngine --> DatabaseManager
Class diagram for refactored GmailAIService with dependency injectionclassDiagram
class GmailAIService {
- GmailDataCollector collector
- GmailMetadataExtractor metadata_extractor
- DataCollectionStrategy data_strategy
- AdvancedAIEngine advanced_ai_engine
- DatabaseManager db_manager
+ sync_gmail_emails(...)
+ execute_smart_retrieval(...)
+ get_performance_metrics(...)
}
GmailAIService --> AdvancedAIEngine
GmailAIService --> DatabaseManager
Class diagram for centralized Pydantic models in models.pyclassDiagram
class ActionExtractionRequest {
subject: Optional[str]
content: str
}
class ActionItem {
action_phrase: str
verb: Optional[str]
object: Optional[str]
raw_due_date_text: Optional[str]
context: str
}
class GmailSyncRequest {
maxEmails: int
queryFilter: str
includeAIAnalysis: bool
strategies: List[str]
timeBudgetMinutes: int
}
class SmartRetrievalRequest {
strategies: List[str]
maxApiCalls: int
timeBudgetMinutes: int
}
Class diagram for TypeScript AIAnalysis and PythonNLPBridge updateclassDiagram
class AIAnalysis {
topic: string
sentiment: string
intent: string
urgency: string
confidence: number
categories: string[]
keywords: string[]
reasoning: string
suggestedLabels: string[]
riskFlags: string[]
categoryId: number
}
class PythonNLPBridge {
+ analyzeEmail(subject, content): Promise<AIAnalysis>
}
PythonNLPBridge --> AIAnalysis
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
WalkthroughThe codebase was refactored to modularize route definitions for both Node.js (Express) and Python (FastAPI) backends. All route logic, models, and service initializations were moved from monolithic files into dedicated modules. New test suites were introduced for each route module, and AI analysis components were updated to support category ID integration and improved dependency injection. Changes
Sequence Diagram(s)Example: Node.js Email Creation FlowsequenceDiagram
participant Client
participant ExpressApp
participant EmailRouter
participant Storage
Client->>ExpressApp: POST /api/emails (email data)
ExpressApp->>EmailRouter: handle POST /
EmailRouter->>Storage: createEmail(validatedData)
Storage-->>EmailRouter: createdEmail
EmailRouter-->>ExpressApp: 201 Created (createdEmail)
ExpressApp-->>Client: Response
Example: FastAPI Gmail Sync FlowsequenceDiagram
participant Client
participant FastAPIApp
participant GmailRouter
participant GmailAIService
participant DatabaseManager
participant AdvancedAIEngine
Client->>FastAPIApp: POST /api/gmail/sync (request)
FastAPIApp->>GmailRouter: handle sync_gmail
GmailRouter->>GmailAIService: sync_gmail_emails(request)
GmailAIService->>DatabaseManager: (as needed)
GmailAIService->>AdvancedAIEngine: analyze_email(subject, content, db)
AdvancedAIEngine-->>GmailAIService: AIAnalysisResult
GmailAIService-->>GmailRouter: SyncResult
GmailRouter-->>FastAPIApp: Response
FastAPIApp-->>Client: JSON Response
Suggested labels
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
| """Get real-time performance overview""" | ||
| try: | ||
| overview = await performance_monitor.get_real_time_dashboard() | ||
| return overview |
Check warning
Code scanning / CodeQL
Information exposure through an exception Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 10 months ago
To fix the issue, we need to ensure that no sensitive information, such as stack traces or raw exception messages, is included in the response sent to the client. Instead, we should return a generic error message while logging the detailed error information on the server for debugging purposes. Specifically:
- Modify the
get_real_time_dashboardmethod inPerformanceMonitorto return a generic error message in theoverallStatus.errorfield instead of the raw exception message. - Update the
get_performance_overviewendpoint to ensure that the response does not include sensitive error details.
| @@ -53,2 +53,4 @@ | ||
| overview = await performance_monitor.get_real_time_dashboard() | ||
| if overview.get("overallStatus", {}).get("status") == "unhealthy": | ||
| overview["overallStatus"]["error"] = "An internal error occurred." | ||
| return overview |
| @@ -242,3 +242,3 @@ | ||
| "timestamp": datetime.now().isoformat(), | ||
| "overallStatus": {"status": "unhealthy", "error": str(e)}, | ||
| "overallStatus": {"status": "unhealthy", "error": "An internal error occurred."}, | ||
| "quotaStatus": {"dailyUsage": {"percentage": 0}}, |
There was a problem hiding this comment.
Actionable comments posted: 34
🔭 Outside diff range comments (2)
server/python_nlp/nlp_engine.py (1)
85-100: Loaded models are discarded – breaks _analyze_topic_model & friends
_sentiment_model_obj,_topic_model_obj, … are correctly loaded, but never assigned to the public attributes (self.sentiment_model,self.topic_model, …).
Any downstream helper (e.g._analyze_topic_model(),_analyze_sentiment_model()) that still checks these attributes will seeNoneand fall back every time.# Load models if dependencies are available @@ _urgency_model_obj = None @@ _urgency_model_obj = self._load_model(self.urgency_model_path) - # Initialize SentimentModel … + # Persist raw model objects for the legacy helpers + self.sentiment_model = _sentiment_model_obj + self.topic_model = _topic_model_obj + self.intent_model = _intent_model_obj + self.urgency_model = _urgency_model_obj + + # Initialize SentimentModel …server/aiRoutes.test.ts (1)
215-218: Remove sentinel lines – TypeScript parser errorThe closing marker made it into the source file and breaks compilation:
-}); -[end of server/aiRoutes.test.ts] +});
🧹 Nitpick comments (24)
server/python_nlp/analysis_components/sentiment_model.py (1)
17-20: Consider defaultinghas_nltk_installedparameter
Requiring callers to passhas_nltk_installedcan lead to inconsistencies. You might refactor the constructor to default to the module-levelHAS_NLTKflag:-from typing import Optional, Dict, Any -from .sentiment_model import HAS_NLTK +from typing import Optional, Dict, Any +from .sentiment_model import HAS_NLTK class SentimentModel: - def __init__(self, sentiment_model: Optional[Any], has_nltk_installed: bool): + def __init__(self, sentiment_model: Optional[Any], has_nltk_installed: bool = HAS_NLTK): self.model = sentiment_model self.has_nltk = has_nltk_installed self.logger = logging.getLogger(__name__)server/python_backend/models.py (1)
135-140: Avoid usingobjectas a field nameThe field name
objectshadows the built-inobjecttype, which can confuse static analysis tools and readers. Consider a more descriptive name such astargetoritem_object.-class ActionItem(BaseModel): - action_phrase: str - verb: Optional[str] = None - object: Optional[str] = None +class ActionItem(BaseModel): + action_phrase: str + verb: Optional[str] = None + target: Optional[str] = Field(None, alias="object")Add
alias="object"if the original payloads already useobject.server/categoryRoutes.ts (1)
20-34: POST/– surface validation details consistently
ZodErrordetails are exposed, but the success branch omits a location header. Consider addingLocation: /api/categories/{id}for REST semantics.server/performanceRoutes.ts (1)
35-40: Consistent default response shape
overview || { … }may drift from the documented schema. Create a typed factory (e.g.,emptyPerformanceOverview()) to guarantee field presence and keep the shape in sync between routes.server/activityRoutes.ts (1)
23-37: Surface Zod validation details for any failure pathOnly the happy‐path of the
ZodErrorbranch exposeserror.errors; if other validation errors surface insidestorage.createActivity, callers will only get a generic 500. Consider normalising error responses and logging the original error for observability.Also note that returning Zod’s
errorsverbatim may leak internal schema details; you may want to map them to a more compact shape before sending them to clients.server/python_nlp/tests/analysis_components/test_urgency_model.py (1)
45-63: Avoid coupling tests to private helpersThe suite calls the internal method
_analyze_regex(). While useful, this couples the test to implementation details and can hinder refactors. Prefer validating public behaviour only and, if needed, expose a small helper through the public API for unit-level testing.server/python_backend/tests/test_dashboard_routes.py (1)
52-74: Mock shape mismatch forweekly_growthThe comment notes that
weekly_growthmust conform to the Pydantic sub-model, then immediately overrides the mock dict. Prefer constructing one canonical fixture that matches the API contract and avoid the mid-test mutation.Refactor for clarity and maintainability.
server/python_backend/tests/test_category_routes.py (1)
61-66: Consider returning 201 for resource creation
POST /api/categoriescurrently returns 200, but REST conventions reserve 201 for successful creation. Aligning with the convention simplifies client-side expectations and allows theLocationheader in the future.If you keep 200 intentionally, add a comment in the route explaining the choice to avoid confusion.
server/python_backend/category_routes.py (2)
53-55: pydantic v2 compatibility
CategoryCreateis a V2 model – favourmodel_dump()overdict()to respect alias & exclude rules:- created_category_dict = await db.create_category(category.dict()) + created_category_dict = await db.create_category(category.model_dump())
22-33: Re-raise with context for clearer logsSame B904 pattern as elsewhere:
- raise HTTPException(status_code=503, detail="Database service unavailable.") + raise HTTPException(status_code=503, detail="Database service unavailable.") from db_errRepeat for the other
exceptblocks.server/python_backend/dashboard_routes.py (1)
37-47: Chain underlying exceptionsTo keep stack-traces intact:
- raise HTTPException(status_code=503, detail="Database service unavailable.") + raise HTTPException(status_code=503, detail="Database service unavailable.") from db_errApply similarly in the generic
except.server/python_backend/tests/test_action_routes.py (1)
6-6: Remove unused importActionItem.
ActionItemis imported but never referenced in this test module, producing anF401/unused-importwarning and unnecessary coupling to model code.-from server.python_backend.models import ActionItem # For response type hint if needed +# (import not required)server/python_backend/tests/test_email_routes.py (2)
5-8: Commented guidance can be dropped; it leaks internal context and clutters tests.These inline remarks about import paths/dependency timing are useful while refactoring but should not remain in committed test code. They don’t add value to future readers and may become outdated.
63-66:dependency_overridesnot cleaned up – risk of leakage to other test modules.FastAPI keeps
app.dependency_overridesafter the test finishes.
Add atry/finally(oryield) in theclientfixture to restore the original state:@pytest.fixture def client(): from server.python_backend.database import get_db - app.dependency_overrides[get_db] = lambda: mock_db_manager - return TestClient(app) + app.dependency_overrides[get_db] = lambda: mock_db_manager + try: + yield TestClient(app) + finally: + app.dependency_overrides.clear()server/python_nlp/tests/analysis_components/test_sentiment_model.py (1)
97-99:if __name__ == "__main__": unittest.main()is redundant under pytest.Pytest discovers and runs
unittest.TestCaseclasses automatically.
Keeping the manual runner is harmless but unnecessary noise.server/gmailRoutes.ts (1)
8-37: Repeatedtry/catch+console.*blocks – extract reusable error handler.Every route repeats identical error handling. Consider a helper:
const wrap = (fn: express.RequestHandler): express.RequestHandler => async (req, res) => { try { await fn(req, res); } catch (err) { console.error(err); res.status(500).json({ success: false, error: err instanceof Error ? err.message : "Unknown error", }); } };and then
router.post("/sync", wrap(async (req,res)=>{ … })).This removes duplication and guarantees consistent behaviour across future routes.
server/categoryRoutes.test.ts (1)
78-114: Zod-error branch is currently un-testableThe note in-code is correct: mocking
storage.createCategorydoes not exercise the Zod-parsing branch because parsing happens before hitting storage.
Either (a) switch these tests tosupertestso the request flows through the real validator or (b) export the raw handler fromcategoryRoutes.tsand invoke it directly with a craftedZodError.Until then this test is a false positive.
server/python_backend/tests/test_ai_engine.py (1)
16-36: Redundant reassignment ofanalyze_email
patch.object(NLPEngine, 'analyze_email')already replaces the method for every instance.
engine.nlp_engine.analyze_email = mock_nlp_analyzeis redundant and may hide leaks if the patch is reverted.
Safe to delete the reassignment line.server/python_backend/ai_engine.py (1)
70-96: ImportDatabaseManagerunderTYPE_CHECKINGto silence F821Ruff flags
DatabaseManageras undefined even though it only appears in type hints.
Add a guarded import to keep runtime deps unchanged and satisfy static analysis:+from typing import TYPE_CHECKING + +if TYPE_CHECKING: # for type-checkers / linters only + from server.python_backend.database import DatabaseManagerserver/python_backend/filter_routes.py (2)
2-3: Drop unusedtyping.*imports
List,Dict,Anyare never referenced – they only add noise.-from typing import List, Dict, Any +from typing import Any # if really needed, otherwise remove entirely
90-100: Re-raise DB errors with contextInside the
except psycopg2.Error as db_err:block, re-raise withfrom db_errto preserve traceback:-raise HTTPException(status_code=503, detail="Database service unavailable.") +raise HTTPException(status_code=503, + detail="Database service unavailable.") from db_errserver/python_backend/gmail_routes.py (2)
2-2: Remove unusedtypingimports
List,Dict,Anyare not used; keep the file lean.
21-24: Heavy services created at import timeInstantiating
DatabaseManager()andAdvancedAIEngine()at module import can slow startup, hamper unit-tests, and make configuration via env-vars impossible. Wire them via FastAPIDependsor a factory instartupevents.server/aiRoutes.ts (1)
46-55: Leverage optional chaining for cleaner null-checks.The pattern
if (analysis && analysis.categoryId) { ... }is repeated several times.
Usinganalysis?.categoryId(and similar) makes the intent clearer and satisfies the linter warning (useOptionalChain).-if (analysis && analysis.categoryId) { +if (analysis?.categoryId) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (42)
server/activityRoutes.test.ts(1 hunks)server/activityRoutes.ts(1 hunks)server/ai-engine.ts(1 hunks)server/aiRoutes.test.ts(1 hunks)server/aiRoutes.ts(1 hunks)server/categoryRoutes.test.ts(1 hunks)server/categoryRoutes.ts(1 hunks)server/dashboardRoutes.test.ts(1 hunks)server/dashboardRoutes.ts(1 hunks)server/emailRoutes.test.ts(1 hunks)server/emailRoutes.ts(1 hunks)server/gmailRoutes.test.ts(1 hunks)server/gmailRoutes.ts(1 hunks)server/performanceRoutes.ts(1 hunks)server/python-bridge.ts(2 hunks)server/python_backend/action_routes.py(1 hunks)server/python_backend/ai_engine.py(2 hunks)server/python_backend/category_routes.py(1 hunks)server/python_backend/dashboard_routes.py(1 hunks)server/python_backend/email_routes.py(1 hunks)server/python_backend/filter_routes.py(1 hunks)server/python_backend/gmail_routes.py(1 hunks)server/python_backend/main.py(1 hunks)server/python_backend/models.py(1 hunks)server/python_backend/tests/test_action_routes.py(1 hunks)server/python_backend/tests/test_ai_engine.py(1 hunks)server/python_backend/tests/test_category_routes.py(1 hunks)server/python_backend/tests/test_dashboard_routes.py(1 hunks)server/python_backend/tests/test_email_routes.py(1 hunks)server/python_backend/tests/test_filter_routes.py(1 hunks)server/python_backend/tests/test_gmail_routes.py(1 hunks)server/python_nlp/analysis_components/intent_model.py(1 hunks)server/python_nlp/analysis_components/sentiment_model.py(1 hunks)server/python_nlp/analysis_components/topic_model.py(1 hunks)server/python_nlp/analysis_components/urgency_model.py(1 hunks)server/python_nlp/gmail_service.py(6 hunks)server/python_nlp/nlp_engine.py(2 hunks)server/python_nlp/tests/analysis_components/test_intent_model.py(1 hunks)server/python_nlp/tests/analysis_components/test_sentiment_model.py(1 hunks)server/python_nlp/tests/analysis_components/test_topic_model.py(1 hunks)server/python_nlp/tests/analysis_components/test_urgency_model.py(1 hunks)server/routes.ts(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (23)
server/dashboardRoutes.ts (1)
server/storage.ts (1)
storage(304-304)
server/categoryRoutes.ts (2)
shared/schema.ts (2)
categories(11-17)insertCategorySchema(111-113)server/storage.ts (1)
storage(304-304)
server/python_nlp/tests/analysis_components/test_topic_model.py (1)
server/python_nlp/analysis_components/topic_model.py (3)
TopicModel(7-94)analyze(82-94)_analyze_keyword(33-80)
server/dashboardRoutes.test.ts (2)
server/python_backend/models.py (1)
DashboardStats(275-283)server/storage.ts (1)
storage(304-304)
server/python_backend/category_routes.py (3)
server/python_backend/database.py (2)
DatabaseManager(18-662)get_db(665-672)server/python_backend/models.py (2)
CategoryCreate(91-92)CategoryResponse(94-96)server/python_backend/performance_monitor.py (2)
PerformanceMonitor(50-566)track(482-487)
server/emailRoutes.ts (2)
shared/schema.ts (2)
emails(19-94)insertEmailSchema(115-117)server/storage.ts (1)
storage(304-304)
server/python-bridge.ts (1)
server/ai-engine.ts (2)
AIAnalysis(18-18)AccuracyValidation(28-28)
server/python_backend/tests/test_category_routes.py (3)
server/python_backend/category_routes.py (1)
create_category(46-75)server/python_backend/database.py (1)
get_db(665-672)server/python_backend/tests/test_email_routes.py (1)
client(62-66)
server/python_backend/tests/test_dashboard_routes.py (3)
server/python_backend/dashboard_routes.py (1)
get_dashboard_stats(16-47)server/python_backend/database.py (1)
get_db(665-672)server/python_backend/tests/test_email_routes.py (1)
client(62-66)
server/python_backend/action_routes.py (3)
server/python_backend/ai_engine.py (2)
AdvancedAIEngine(51-270)analyze_email(97-119)server/python_backend/performance_monitor.py (2)
PerformanceMonitor(50-566)track(482-487)server/python_backend/models.py (2)
ActionExtractionRequest(131-133)ActionItem(135-140)
server/python_backend/tests/test_ai_engine.py (2)
server/python_backend/ai_engine.py (3)
AdvancedAIEngine(51-270)AIAnalysisResult(18-49)analyze_email(97-119)server/python_nlp/nlp_engine.py (2)
NLPEngine(56-798)analyze_email(681-748)
server/python_backend/dashboard_routes.py (3)
server/python_backend/database.py (2)
DatabaseManager(18-662)get_db(665-672)server/python_backend/performance_monitor.py (2)
PerformanceMonitor(50-566)track(482-487)server/python_backend/models.py (1)
DashboardStats(275-283)
server/python_nlp/tests/analysis_components/test_sentiment_model.py (1)
server/python_nlp/analysis_components/sentiment_model.py (3)
SentimentModel(16-142)analyze(124-142)_analyze_keyword(85-122)
server/emailRoutes.test.ts (1)
server/storage.ts (1)
storage(304-304)
server/gmailRoutes.ts (2)
server/gmail-ai-service.ts (1)
gmailAIService(403-403)server/python_backend/metrics.py (1)
metrics(106-111)
server/python_backend/tests/test_gmail_routes.py (2)
server/python_nlp/gmail_service.py (4)
sync_gmail_emails(121-159)execute_smart_retrieval(523-558)get_retrieval_strategies(560-576)get_performance_metrics(578-608)server/python_backend/gmail_routes.py (1)
get_retrieval_strategies(175-189)
server/python_backend/tests/test_email_routes.py (4)
server/python_backend/database.py (5)
search_emails(569-587)get_emails_by_category(565-567)get_all_emails(561-563)get_email_by_id(248-259)get_db(665-672)server/python_backend/email_routes.py (2)
create_email(95-149)update_email(153-187)server/python_backend/ai_engine.py (2)
analyze_email(97-119)to_dict(35-49)server/python_nlp/smart_filters.py (1)
apply_filters_to_email_data(1253-1293)
server/python_backend/tests/test_filter_routes.py (3)
server/python_nlp/smart_filters.py (4)
EmailFilter(16-29)add_custom_filter(579-600)create_intelligent_filters(312-328)prune_ineffective_filters(602-684)server/python_backend/database.py (2)
get_recent_emails(590-593)get_db(665-672)server/python_backend/tests/test_email_routes.py (1)
client(62-66)
server/python_backend/tests/test_action_routes.py (2)
server/python_backend/models.py (1)
ActionItem(135-140)server/python_backend/ai_engine.py (1)
analyze_email(97-119)
server/aiRoutes.test.ts (2)
server/python-bridge.ts (2)
MappedNLPResult(30-30)pythonNLP(303-303)server/storage.ts (1)
storage(304-304)
server/categoryRoutes.test.ts (1)
server/storage.ts (1)
storage(304-304)
server/gmailRoutes.test.ts (1)
server/gmail-ai-service.ts (1)
gmailAIService(403-403)
server/aiRoutes.ts (3)
server/python-bridge.ts (2)
pythonNLP(303-303)MappedNLPResult(30-30)server/storage.ts (1)
storage(304-304)shared/schema.ts (1)
categories(11-17)
🪛 Pylint (3.3.7)
server/python_nlp/analysis_components/urgency_model.py
[refactor] 7-7: Too few public methods (1/2)
(R0903)
server/python_nlp/analysis_components/sentiment_model.py
[refactor] 16-16: Too few public methods (1/2)
(R0903)
server/python_nlp/analysis_components/topic_model.py
[refactor] 7-7: Too few public methods (1/2)
(R0903)
server/python_nlp/analysis_components/intent_model.py
[refactor] 7-7: Too few public methods (1/2)
(R0903)
server/python_nlp/tests/analysis_components/test_intent_model.py
[error] 57-57: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_intent_model, line 57)'
(E0001)
server/python_nlp/tests/analysis_components/test_topic_model.py
[error] 57-57: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_topic_model, line 57)'
(E0001)
server/python_backend/models.py
[refactor] 131-131: Too few public methods (0/2)
(R0903)
[refactor] 135-135: Too few public methods (0/2)
(R0903)
server/python_backend/tests/test_category_routes.py
[error] 78-78: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_category_routes, line 78)'
(E0001)
server/python_backend/tests/test_dashboard_routes.py
[error] 115-115: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_dashboard_routes, line 115)'
(E0001)
server/python_backend/tests/test_ai_engine.py
[error] 133-133: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_ai_engine, line 133)'
(E0001)
server/python_nlp/tests/analysis_components/test_sentiment_model.py
[error] 100-100: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_sentiment_model, line 100)'
(E0001)
server/python_nlp/tests/analysis_components/test_urgency_model.py
[error] 67-67: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_urgency_model, line 67)'
(E0001)
server/python_backend/tests/test_gmail_routes.py
[error] 111-111: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_gmail_routes, line 111)'
(E0001)
server/python_backend/tests/test_email_routes.py
[error] 169-169: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_email_routes, line 169)'
(E0001)
server/python_backend/tests/test_filter_routes.py
[error] 118-118: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_filter_routes, line 118)'
(E0001)
server/python_backend/tests/test_action_routes.py
[error] 85-85: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_action_routes, line 85)'
(E0001)
server/python_nlp/gmail_service.py
[error] 377-377: Parsing failed: ''{' was never closed (gmail_service, line 377)'
(E0001)
🪛 Ruff (0.11.9)
server/python_nlp/tests/analysis_components/test_intent_model.py
57-57: SyntaxError: Expected ',', found name
57-57: SyntaxError: Expected ',', found name
server/python_nlp/tests/analysis_components/test_topic_model.py
57-57: SyntaxError: Expected ',', found name
57-57: SyntaxError: Expected ',', found name
server/python_backend/category_routes.py
17-17: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
32-32: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
42-42: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
49-49: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
65-65: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
75-75: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
server/python_backend/tests/test_category_routes.py
78-78: SyntaxError: Expected ',', found name
78-78: SyntaxError: Expected ',', found name
server/python_backend/tests/test_dashboard_routes.py
115-115: SyntaxError: Expected ',', found name
115-115: SyntaxError: Expected ',', found name
server/python_backend/action_routes.py
57-57: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
server/python_backend/tests/test_ai_engine.py
133-133: SyntaxError: Expected ',', found name
133-133: SyntaxError: Expected ',', found name
server/python_backend/dashboard_routes.py
16-16: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
37-37: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
47-47: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
64-64: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
server/python_nlp/tests/analysis_components/test_sentiment_model.py
100-100: SyntaxError: Expected ',', found name
100-100: SyntaxError: Expected ',', found name
server/python_nlp/tests/analysis_components/test_urgency_model.py
67-67: SyntaxError: Expected ',', found name
67-67: SyntaxError: Expected ',', found name
server/python_backend/tests/test_gmail_routes.py
111-111: SyntaxError: Expected ',', found name
111-111: SyntaxError: Expected ',', found name
server/python_backend/tests/test_email_routes.py
169-169: SyntaxError: Expected ',', found name
169-169: SyntaxError: Expected ',', found name
server/python_backend/tests/test_filter_routes.py
118-118: SyntaxError: Expected ',', found name
118-118: SyntaxError: Expected ',', found name
server/python_backend/tests/test_action_routes.py
85-85: SyntaxError: Expected ',', found name
85-85: SyntaxError: Expected ',', found name
server/python_backend/email_routes.py
26-26: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
48-48: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
58-58: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
62-62: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
81-81: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
91-91: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
99-99: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
139-139: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
149-149: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
157-157: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
177-177: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
187-187: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
server/python_backend/ai_engine.py
70-70: Undefined name DatabaseManager
(F821)
97-97: Undefined name DatabaseManager
(F821)
server/python_backend/filter_routes.py
2-2: typing.List imported but unused
Remove unused import
(F401)
2-2: typing.Dict imported but unused
Remove unused import
(F401)
2-2: typing.Any imported but unused
Remove unused import
(F401)
38-38: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
65-65: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
69-69: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
99-99: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
109-109: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
129-129: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
server/python_backend/gmail_routes.py
2-2: typing.List imported but unused
Remove unused import
(F401)
2-2: typing.Dict imported but unused
Remove unused import
(F401)
2-2: typing.Any imported but unused
Remove unused import
(F401)
84-84: Do not use bare except
(E722)
109-109: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
119-119: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
136-136: Do not use bare except
(E722)
161-161: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
171-171: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
189-189: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
207-207: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
🪛 Biome (1.9.4)
server/dashboardRoutes.test.ts
[error] 73-73: expected , but instead found of
Remove of
(parse)
[error] 73-73: expected , but instead found server
Remove server
(parse)
server/emailRoutes.test.ts
[error] 260-260: expected , but instead found of
Remove of
(parse)
[error] 260-260: expected , but instead found server
Remove server
(parse)
server/activityRoutes.test.ts
[error] 103-103: expected , but instead found of
Remove of
(parse)
[error] 103-103: expected , but instead found server
Remove server
(parse)
server/aiRoutes.test.ts
[error] 217-217: expected , but instead found of
Remove of
(parse)
[error] 217-217: expected , but instead found server
Remove server
(parse)
server/categoryRoutes.test.ts
[error] 149-149: expected , but instead found of
Remove of
(parse)
[error] 149-149: expected , but instead found server
Remove server
(parse)
server/gmailRoutes.test.ts
[error] 168-168: expected , but instead found of
Remove of
(parse)
[error] 168-168: expected , but instead found server
Remove server
(parse)
server/aiRoutes.ts
[error] 46-46: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🪛 GitHub Check: CodeQL
server/python_backend/dashboard_routes.py
[warning] 54-54: Information exposure through an exception
Stack trace information flows to this location and may be exposed to an external user.
🔇 Additional comments (11)
server/python_nlp/analysis_components/topic_model.py (1)
7-7: Rename aligns with naming conventions
The class has been correctly renamed fromTopicAnalyzertoTopicModel, matching other components. Ensure all imports and references (e.g., innlp_engine.py) are updated accordingly.server/python_nlp/analysis_components/intent_model.py (1)
7-7: Consistent renaming toIntentModel
The class rename is in line with the updated analyzer-to-model convention. Verify thatNLPEngineinstantiation and all imports are updated to useIntentModel.server/python_nlp/analysis_components/sentiment_model.py (1)
16-16: Class rename toSentimentModelis correct
The rename maintains consistency with other model components. Confirm that tests andnlp_engine.pyimportSentimentModelinstead of the old name.server/python_nlp/analysis_components/urgency_model.py (1)
7-7: Analyzer-to-Model rename applied correctly
UrgencyAnalyzer→UrgencyModelis consistent with the other components. Ensure downstream code usesUrgencyModel.server/ai-engine.ts (1)
15-15: AddedcategoryIdto schema—verify usage
You addedcategoryIdas optional inaiAnalysisSchema, but neitherLocalNLPModelnorHuggingFaceModelpopulate this field. Confirm that your Python bridge or other data mappers supplycategoryId, or update the TS models to propagate it.Run:
rg "categoryId" -n serverto locate where this property must be mapped in your codebase.
server/python-bridge.ts (2)
27-27: LGTM! Proper implementation of category ID mapping.The optional
category_idfield and its mapping tocategoryIdcorrectly follow the snake_case to camelCase conversion pattern used throughout this file.Also applies to: 57-57
30-30: Verify that the AIAnalysis type includes categoryId.The comment suggests that
AIAnalysisnow includescategoryId, but this should be verified against the actual type definition.#!/bin/bash # Description: Verify that AIAnalysis type includes categoryId field # Search for the aiAnalysisSchema definition to confirm categoryId is included ast-grep --pattern 'const aiAnalysisSchema = z.object({ $$$ })' # Also search for any direct references to categoryId in the schema rg -A 5 -B 5 "aiAnalysisSchema.*categoryId|categoryId.*aiAnalysisSchema"server/routes.ts (1)
3-9: Excellent modularization of routes!The refactoring successfully separates route logic into dedicated modules, improving code organization and maintainability. The mounting pattern follows Express best practices.
Also applies to: 22-28
server/python_nlp/tests/analysis_components/test_topic_model.py (1)
1-56: Well-structured test suite with comprehensive coverage!The tests properly mock the sklearn model and cover all major scenarios including model success, failure with fallback, and keyword-based analysis. Good use of assertions to verify the expected behavior.
server/python_nlp/tests/analysis_components/test_urgency_model.py (1)
1-44: 👍 Comprehensive coverageThe tests exercise both the ML and fallback paths and correctly mock model behaviour. Nice use of
logging.disableto keep the output clean.server/python_backend/action_routes.py (1)
47-57: Preserve original traceback when re-raisingWhen you wrap an exception you lose the causal chain. Pass the original exception with
from e:- raise HTTPException(status_code=500, detail=f"Failed to extract action items: {str(e)}") + raise HTTPException( + status_code=500, + detail=f"Failed to extract action items: {e}" + ) from e[ suggest_nitpick ]
| router.get("/stats", async (_req, res) => { | ||
| try { | ||
| console.time("storage.getDashboardStats"); | ||
| const stats = await storage.getDashboardStats(); | ||
| console.timeEnd("storage.getDashboardStats"); | ||
| res.json(stats); | ||
| } catch (error) { | ||
| res.status(500).json({ message: "Failed to fetch dashboard stats" }); | ||
| } | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Ensure console.time() is always balanced with console.timeEnd()
If an exception is raised after console.time() but before console.timeEnd(), the timer label will remain open, polluting stdout and making later timing logs unreadable.
Wrap the timing calls in a finally block so the timer is stopped even on failure.
router.get("/stats", async (_req, res) => {
- try {
- console.time("storage.getDashboardStats");
- const stats = await storage.getDashboardStats();
- console.timeEnd("storage.getDashboardStats");
- res.json(stats);
- } catch (error) {
- res.status(500).json({ message: "Failed to fetch dashboard stats" });
- }
+ console.time("storage.getDashboardStats");
+ try {
+ const stats = await storage.getDashboardStats();
+ res.json(stats);
+ } catch (error) {
+ res.status(500).json({ message: "Failed to fetch dashboard stats" });
+ } finally {
+ console.timeEnd("storage.getDashboardStats");
+ }
});📝 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.
| router.get("/stats", async (_req, res) => { | |
| try { | |
| console.time("storage.getDashboardStats"); | |
| const stats = await storage.getDashboardStats(); | |
| console.timeEnd("storage.getDashboardStats"); | |
| res.json(stats); | |
| } catch (error) { | |
| res.status(500).json({ message: "Failed to fetch dashboard stats" }); | |
| } | |
| }); | |
| router.get("/stats", async (_req, res) => { | |
| console.time("storage.getDashboardStats"); | |
| try { | |
| const stats = await storage.getDashboardStats(); | |
| res.json(stats); | |
| } catch (error) { | |
| res.status(500).json({ message: "Failed to fetch dashboard stats" }); | |
| } finally { | |
| console.timeEnd("storage.getDashboardStats"); | |
| } | |
| }); |
🤖 Prompt for AI Agents
In server/dashboardRoutes.ts around lines 7 to 16, the console.time() call is
not guaranteed to be followed by console.timeEnd() if an exception occurs, which
leaves the timer label open and pollutes stdout. To fix this, move
console.time() before the try block and place console.timeEnd() inside a finally
block after the try-catch, ensuring the timer is always stopped regardless of
success or failure.
| console.time("storage.getAllCategories"); | ||
| const categories = await storage.getAllCategories(); | ||
| console.timeEnd("storage.getAllCategories"); | ||
| res.json(categories); | ||
| } catch (error) { | ||
| res.status(500).json({ message: "Failed to fetch categories" }); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Balance console.time() / console.timeEnd() in the error path
Same timing-leak concern as in dashboardRoutes.ts. Move console.timeEnd() to a finally clause.
🤖 Prompt for AI Agents
In server/categoryRoutes.ts around lines 11 to 17, the console.timeEnd call is
missing in the error path, causing an unbalanced timing measurement. To fix
this, move the console.timeEnd("storage.getAllCategories") call into a finally
block so it executes regardless of success or failure, ensuring balanced timing
calls.
| router.put("/:id", async (req, res) => { | ||
| try { | ||
| const id = parseInt(req.params.id); | ||
| const updateData = req.body; | ||
| console.time("storage.updateCategory"); | ||
| const category = await storage.updateCategory(id, updateData); | ||
| console.timeEnd("storage.updateCategory"); | ||
|
|
||
| if (!category) { | ||
| return res.status(404).json({ message: "Category not found" }); | ||
| } | ||
|
|
||
| res.json(category); | ||
| } catch (error) { | ||
| res.status(500).json({ message: "Failed to update category" }); | ||
| } | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Validate id and request body when updating a category
parseInt(req.params.id) may return NaN, which will reach the storage layer unchecked. There is also no schema validation for updateData, risking silent acceptance of invalid fields.
+const id = Number(req.params.id);
+if (!Number.isInteger(id)) {
+ return res.status(400).json({ message: "Invalid category id" });
+}
+
+// Whitelist permitted fields
+const updateSchema = insertCategorySchema.partial();
+const updateData = updateSchema.parse(req.body);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In server/categoryRoutes.ts around lines 36 to 52, the code does not validate
the parsed id or the updateData request body before calling
storage.updateCategory. Add a check after parsing the id to verify it is a valid
number and return a 400 error if not. Also, implement schema validation for
updateData using a validation library or manual checks to ensure only valid
fields are accepted, returning a 400 error if validation fails. This prevents
invalid data from reaching the storage layer.
| console.time("gmailAIService.getPerformanceMetrics"); | ||
| const metrics = await gmailAIService.getPerformanceMetrics(); | ||
| console.timeEnd("gmailAIService.getPerformanceMetrics"); | ||
| if (metrics) { |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Move timer cleanup to finally
Same issue: if gmailAIService.getPerformanceMetrics() throws, the console.timeEnd never executes.
🤖 Prompt for AI Agents
In server/performanceRoutes.ts around lines 10 to 13, the console.timeEnd call
is not guaranteed to run if gmailAIService.getPerformanceMetrics() throws an
error. To fix this, wrap the timing code in a try-finally block where
console.time starts before the try, the await call is inside the try, and
console.timeEnd is called in the finally block to ensure it always executes
regardless of errors.
| if __name__ == '__main__': | ||
| unittest.main() | ||
| [end of server/python_nlp/tests/analysis_components/test_intent_model.py] |
There was a problem hiding this comment.
Remove stray sentinel line causing SyntaxError
The literal line
[end of server/python_nlp/tests/analysis_components/test_intent_model.py]
is not a comment and breaks the test suite.
- [end of server/python_nlp/tests/analysis_components/test_intent_model.py]
+# (file end marker removed)📝 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.
| if __name__ == '__main__': | |
| unittest.main() | |
| [end of server/python_nlp/tests/analysis_components/test_intent_model.py] | |
| if __name__ == '__main__': | |
| unittest.main() | |
| # (file end marker removed) |
🧰 Tools
🪛 Ruff (0.11.9)
57-57: SyntaxError: Expected ',', found name
57-57: SyntaxError: Expected ',', found name
🪛 Pylint (3.3.7)
[error] 57-57: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_intent_model, line 57)'
(E0001)
🤖 Prompt for AI Agents
In server/python_nlp/tests/analysis_components/test_intent_model.py at lines 55
to 57, remove the stray line '[end of
server/python_nlp/tests/analysis_components/test_intent_model.py]' as it is not
a comment and causes a SyntaxError, breaking the test suite.
| assert response.json() == mock_performance_data | ||
| mock_gmail_service_instance.get_performance_metrics.assert_called_once() | ||
|
|
||
| [end of server/python_backend/tests/test_gmail_routes.py] |
There was a problem hiding this comment.
Remove non-Python sentinel line
[end of server_python_backend/tests/test_gmail_routes.py] is included in the file body, producing a SyntaxError.
Delete this marker so the test module is valid Python.
🧰 Tools
🪛 Ruff (0.11.9)
111-111: SyntaxError: Expected ',', found name
111-111: SyntaxError: Expected ',', found name
🪛 Pylint (3.3.7)
[error] 111-111: Parsing failed: 'invalid syntax. Perhaps you forgot a comma? (test_gmail_routes, line 111)'
(E0001)
🤖 Prompt for AI Agents
In server/python_backend/tests/test_gmail_routes.py at line 111, remove the
non-Python sentinel line '[end of
server_python_backend/tests/test_gmail_routes.py]' as it causes a SyntaxError.
Delete this line entirely to ensure the test module contains only valid Python
code.
| except: # Broad except for decoding issues | ||
| error_details_dict = {"message": "Failed to decode Gmail error content."} | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Replace bare except: with explicit exception
Catching everything hides programmer errors and breaks Ctrl-C.
Use except Exception as err: instead and log err.
🧰 Tools
🪛 Ruff (0.11.9)
84-84: Do not use bare except
(E722)
🤖 Prompt for AI Agents
In server/python_backend/gmail_routes.py around lines 84 to 86, replace the bare
except clause with an explicit exception handler by changing `except:` to
`except Exception as err:` and log the caught exception `err` to provide error
details and avoid hiding programmer errors or interfering with keyboard
interrupts.
| # Services are now initialized within their respective route files | ||
| # or kept here if they are used by multiple route files or for general app setup. | ||
| gmail_service = GmailAIService() # Used by gmail_routes | ||
| filter_manager = SmartFilterManager() # Used by filter_routes | ||
| ai_engine = AdvancedAIEngine() # Used by email_routes, action_routes | ||
| performance_monitor = PerformanceMonitor() # Used by all routes via @performance_monitor.track | ||
|
|
||
| # Import routers | ||
| from . import email_routes, category_routes, gmail_routes, filter_routes, action_routes, dashboard_routes | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Break the implicit circular import – move service singletons to a dedicated dependency module.
main.py instantiates gmail_service, filter_manager, ai_engine, and performance_monitor, then immediately imports router modules that are likely to import these very symbols back from main.
This tight coupling works only while the import order stays untouched; a small refactor can easily trigger a ImportError: partially initialised module.
Create a deps.py (or similar) that instantiates the singletons and have both main.py and each router import from it:
# deps.py
gmail_service = GmailAIService()
filter_manager = SmartFilterManager()
ai_engine = AdvancedAIEngine()
performance_monitor = PerformanceMonitor()# main.py
from . import deps
...
app.include_router(email_routes.router)This removes the bidirectional dependency and clarifies ownership of shared resources.
🤖 Prompt for AI Agents
In server/python_backend/main.py around lines 60 to 69, the service singletons
are instantiated directly in main.py and then router modules are imported,
causing a circular import risk. To fix this, create a new module named deps.py
where you instantiate gmail_service, filter_manager, ai_engine, and
performance_monitor. Then, in main.py and all router modules, import these
singletons from deps.py instead of main.py. This breaks the circular import
dependency and clarifies ownership of shared resources.
| details: `${successCount}/${emailIds.length} emails successfully analyzed and categorized`, | ||
| timestamp: new Date().toISOString(), | ||
| icon: "fas fa-layer-group", | ||
| iconBg: "bg-purple-50 text-purple-600", | ||
| }); | ||
| console.timeEnd("storage.createActivity_batch_analyze"); | ||
|
|
||
| res.json({ | ||
| success: true, | ||
| results, | ||
| summary: { | ||
| total: emailIds.length, | ||
| processed: results.length, | ||
| successful: successCount, | ||
| failed: results.length - successCount |
There was a problem hiding this comment.
🛠️ Refactor suggestion
success/total mismatch when more than 10 IDs are supplied.
emailIds.length is used in the summary and activity details, but only the first 10 IDs are processed (slice(0, 10)).
If 30 IDs are submitted the summary will read “10/30 processed”, which is misleading.
Track the actual processed count:
-const results = [];
+const results: typeof results = [];
const processedIds = emailIds.slice(0, 10);
...
-const successCount = results.filter(r => r.success).length;
+const successCount = results.filter(r => r.success).length;
...
-details: `${successCount}/${emailIds.length} emails successfully analyzed and categorized`,
+details: `${successCount}/${processedIds.length} emails successfully analyzed and categorized`,
...
- total: emailIds.length,
- processed: results.length,
+ total: processedIds.length,
+ processed: results.length,Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In server/aiRoutes.ts around lines 186 to 200, the summary and activity details
use emailIds.length as the total count, but only the first 10 IDs are processed
due to slicing. This causes a mismatch in the success/total counts when more
than 10 IDs are supplied. To fix this, replace emailIds.length with the actual
number of processed IDs (e.g., the length of the sliced array) in both the
summary and activity details to accurately reflect the processed count.
| // Manual categorization (existing logic) | ||
| // analysis remains undefined here | ||
| const { categoryId, confidence } = req.body; | ||
|
|
||
| console.time("storage.updateEmail_categorize_manual"); // Unique label | ||
| const updatedEmail = await storage.updateEmail(emailId, { | ||
| categoryId: categoryId, | ||
| confidence: confidence || 95, | ||
| }); | ||
| console.timeEnd("storage.updateEmail_categorize_manual"); | ||
|
|
There was a problem hiding this comment.
Validate categoryId when performing manual categorisation.
In the manual branch you call storage.updateEmail with the possibly undefined categoryId.
If the client forgets to send it, the API silently sets categoryId to null, leading to orphaned emails.
Add an explicit guard:
-const { categoryId, confidence } = req.body;
+const { categoryId, confidence } = req.body;
+if (!categoryId) {
+ return res.status(400).json({ message: "categoryId is required for manual categorisation" });
+}📝 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.
| // Manual categorization (existing logic) | |
| // analysis remains undefined here | |
| const { categoryId, confidence } = req.body; | |
| console.time("storage.updateEmail_categorize_manual"); // Unique label | |
| const updatedEmail = await storage.updateEmail(emailId, { | |
| categoryId: categoryId, | |
| confidence: confidence || 95, | |
| }); | |
| console.timeEnd("storage.updateEmail_categorize_manual"); | |
| // Manual categorization (existing logic) | |
| // analysis remains undefined here | |
| const { categoryId, confidence } = req.body; | |
| if (!categoryId) { | |
| return res.status(400).json({ | |
| message: "categoryId is required for manual categorisation", | |
| }); | |
| } | |
| console.time("storage.updateEmail_categorize_manual"); // Unique label | |
| const updatedEmail = await storage.updateEmail(emailId, { | |
| categoryId: categoryId, | |
| confidence: confidence || 95, | |
| }); | |
| console.timeEnd("storage.updateEmail_categorize_manual"); |
🤖 Prompt for AI Agents
In server/aiRoutes.ts around lines 88 to 98, the manual categorization code
calls storage.updateEmail with a potentially undefined categoryId from req.body,
which can cause orphaned emails if missing. Add an explicit check to validate
that categoryId is present and valid before calling updateEmail, and return an
error response if it is missing or invalid to prevent silent failures.
There was a problem hiding this comment.
Hey @MasumRab - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `server/python_nlp/gmail_service.py:34` </location>
<code_context>
+ def __init__(self,
</code_context>
<issue_to_address>
Default instantiation of DatabaseManager may cause issues in some environments.
If configuration is needed, make db_manager a required argument or raise a clear error on failed instantiation.
</issue_to_address>
### Comment 2
<location> `server/python_backend/ai_engine.py:70` </location>
<code_context>
- """Analyze email content with AI by calling the NLPEngine directly."""
- logger.info(f"Initiating AI analysis for email with subject: '{subject[:50]}...' using direct NLPEngine call.")
+
+ async def _match_category_id(self, ai_categories: List[str], db: 'DatabaseManager') -> Optional[int]:
+ """Matches AI suggested category strings to database categories."""
+ if not ai_categories:
+ return None
+
+ # This import is problematic for circular deps if DatabaseManager imports AIEngine.
</code_context>
<issue_to_address>
Category matching is case-insensitive but may not handle synonyms or partial matches.
Since the logic only checks for substrings, it may fail when synonyms or different naming conventions are used. Consider implementing a more robust matching method or maintaining a mapping table to improve accuracy.
Suggested implementation:
```python
async def _match_category_id(self, ai_categories: List[str], db: 'DatabaseManager') -> Optional[int]:
"""Matches AI suggested category strings to database categories, using synonyms and robust matching."""
if not ai_categories:
return None
# Synonym mapping: keys are normalized AI category names, values are normalized DB category names
CATEGORY_SYNONYMS = {
"billing": "invoices",
"invoice": "invoices",
"payment": "payments",
"support": "customer support",
"help": "customer support",
"bug": "issue",
"problem": "issue",
"purchase": "orders",
"order": "orders",
# Add more as needed
}
def normalize(s: str) -> str:
return s.strip().lower()
try:
all_db_categories = await db.get_all_categories() # db.get_all_categories() was adapted from get_categories
if not all_db_categories:
return None
```
```python
all_db_categories = await db.get_all_categories() # db.get_all_categories() was adapted from get_categories
if not all_db_categories:
return None
# Build a normalized lookup for DB categories: {normalized_name: (id, original_name)}
db_cat_lookup = {normalize(cat['name']): (cat['id'], cat['name']) for cat in all_db_categories}
for ai_cat in ai_categories:
norm_ai_cat = normalize(ai_cat)
# 1. Try direct match
if norm_ai_cat in db_cat_lookup:
return db_cat_lookup[norm_ai_cat][0]
# 2. Try synonym mapping
if norm_ai_cat in CATEGORY_SYNONYMS:
mapped = CATEGORY_SYNONYMS[norm_ai_cat]
if mapped in db_cat_lookup:
return db_cat_lookup[mapped][0]
# 3. Try substring/partial match
for db_norm, (db_id, db_orig) in db_cat_lookup.items():
if norm_ai_cat in db_norm or db_norm in norm_ai_cat:
return db_id
return None
```
</issue_to_address>
### Comment 3
<location> `server/python_backend/email_routes.py:93` </location>
<code_context>
+ )
+ raise HTTPException(status_code=500, detail="Failed to fetch email")
+
+@router.post("/api/emails", response_model=EmailResponse) # Changed to EmailResponse
+@performance_monitor.track
+async def create_email(
</code_context>
<issue_to_address>
Return 201 status code for created emails
Add `status_code=201` to the decorator or set it in the response to indicate resource creation.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
@router.post("/api/emails", response_model=EmailResponse) # Changed to EmailResponse
=======
@router.post("/api/emails", response_model=EmailResponse, status_code=201) # Changed to EmailResponse, added status_code=201
>>>>>>> REPLACE
</suggested_fix>
### Comment 4
<location> `server/python_backend/category_routes.py:44` </location>
<code_context>
+ )
+ raise HTTPException(status_code=500, detail="Failed to fetch categories")
+
+@router.post("/api/categories", response_model=CategoryResponse) # Changed to CategoryResponse
+@performance_monitor.track
+async def create_category(
</code_context>
<issue_to_address>
Return 201 status code for created categories
Add `status_code=201` to the POST decorator to return the standard HTTP 201 Created response for new categories.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
@router.post("/api/categories", response_model=CategoryResponse) # Changed to CategoryResponse
=======
@router.post("/api/categories", response_model=CategoryResponse, status_code=201) # Changed to CategoryResponse and returns 201 Created
>>>>>>> REPLACE
</suggested_fix>
### Comment 5
<location> `server/python_backend/filter_routes.py:47` </location>
<code_context>
+ try:
+ description = filter_request_model.criteria.get("description", "")
+
+ new_filter_object = filter_manager.add_custom_filter(
+ name=filter_request_model.name,
+ description=description,
</code_context>
<issue_to_address>
Missing await on add_custom_filter call
Please add 'await' before 'filter_manager.add_custom_filter(...)' to ensure the coroutine is executed properly.
</issue_to_address>
### Comment 6
<location> `server/python_backend/filter_routes.py:18` </location>
<code_context>
+filter_manager = SmartFilterManager() # Initialize filter manager
+performance_monitor = PerformanceMonitor() # Initialize performance monitor
+
+@router.get("/api/filters")
+@performance_monitor.track
+async def get_filters(request: Request):
</code_context>
<issue_to_address>
Define response_model for GET filters route
This enhances both API documentation and response validation.
Suggested implementation:
```python
from .models import FilterRequest, FilterListResponse # Changed from .main to .models; Import response model
```
```python
@router.get("/api/filters", response_model=FilterListResponse)
@performance_monitor.track
async def get_filters(request: Request):
```
If `FilterListResponse` is not yet defined in `.models`, you should define it as a Pydantic model, for example:
```python
from pydantic import BaseModel
from typing import List, Dict, Any
class FilterListResponse(BaseModel):
filters: List[Dict[str, Any]]
```
Or, if you have an `EmailFilter` Pydantic model:
```python
class FilterListResponse(BaseModel):
filters: List[EmailFilter]
```
Make sure to adjust the import and model definition as needed to match your codebase.
</issue_to_address>
### Comment 7
<location> `server/python_backend/filter_routes.py:111` </location>
<code_context>
+ )
+ raise HTTPException(status_code=500, detail="Failed to generate filters")
+
+@router.post("/api/filters/prune")
+@performance_monitor.track
+async def prune_filters(request: Request):
</code_context>
<issue_to_address>
Add response_model to prune endpoint
This helps ensure the endpoint returns data in the correct format and improves API documentation.
Suggested implementation:
```python
from pydantic import BaseModel
class PruneFiltersResponse(BaseModel):
# Adjust fields as appropriate for your actual return structure
pruned_count: int
details: dict = {}
@router.post("/api/filters/prune", response_model=PruneFiltersResponse)
@performance_monitor.track
async def prune_filters(request: Request):
```
- You may need to adjust the fields of `PruneFiltersResponse` to match the actual structure of the data returned by `filter_manager.prune_ineffective_filters()`.
- Ensure that the return value from `prune_filters` is a dict compatible with the response model, e.g., `{"pruned_count": ..., "details": ...}`.
- If a response model already exists elsewhere in your codebase, import and use it instead of redefining it.
</issue_to_address>
### Comment 8
<location> `server/python_backend/dashboard_routes.py:49` </location>
<code_context>
+ )
+ raise HTTPException(status_code=500, detail="Failed to fetch dashboard stats")
+
+@router.get("/api/performance/overview")
+async def get_performance_overview(request: Request):
+ """Get real-time performance overview"""
</code_context>
<issue_to_address>
Add performance tracking to overview endpoint
For consistency with other dashboard routes, consider adding the `@performance_monitor.track` decorator here.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
@router.get("/api/performance/overview")
async def get_performance_overview(request: Request):
=======
@router.get("/api/performance/overview")
@performance_monitor.track
async def get_performance_overview(request: Request):
>>>>>>> REPLACE
</suggested_fix>
### Comment 9
<location> `server/python_backend/email_routes.py:30` </location>
<code_context>
+):
+ """Get emails with optional filtering"""
+ try:
+ if search:
+ emails = await db.search_emails(search)
+ elif category_id:
</code_context>
<issue_to_address>
Search filters override category filter
If you want to support filtering by both search and category, update the query logic to combine these filters.
Suggested implementation:
```python
try:
if search and category_id:
emails = await db.search_emails_by_category(search, category_id)
elif search:
emails = await db.search_emails(search)
elif category_id:
emails = await db.get_emails_by_category(category_id)
else:
emails = await db.get_all_emails()
```
You will need to implement the `search_emails_by_category(search: str, category_id: int)` method in your `DatabaseManager` class to support searching emails by both search term and category. If such a method already exists, ensure it is used here. If not, you will need to add it to your database access layer.
</issue_to_address>
### Comment 10
<location> `server/emailRoutes.ts:55` </location>
<code_context>
+router.post("/", async (req, res) => {
+ try {
+ const emailData = insertEmailSchema.parse(req.body);
+ console.time("storage.createEmail");
+ const email = await storage.createEmail(emailData);
+ console.timeEnd("storage.createEmail");
</code_context>
<issue_to_address>
Use structured monitoring instead of console.time
A centralized monitoring tool will provide more robust and production-ready performance metrics than console-based timing.
Suggested implementation:
```typescript
const transaction = Sentry.startTransaction({ name: "storage.createEmail" });
let email;
try {
email = await storage.createEmail(emailData);
transaction.finish();
} catch (err) {
transaction.finish();
throw err;
}
```
1. Ensure that `import * as Sentry from "@sentry/node";` (or your monitoring tool's import) is present at the top of the file.
2. If you use a different monitoring tool, adjust the transaction/span API accordingly.
3. If you want more granular timing, you can use Sentry's child spans or similar constructs in your monitoring tool.
</issue_to_address>
### Comment 11
<location> `server/emailRoutes.ts:68` </location>
<code_context>
+ }
+});
+
+router.put("/:id", async (req, res) => {
+ try {
+ const id = parseInt(req.params.id);
</code_context>
<issue_to_address>
Validate `id` parameter before use
Return a 400 error if `id` is not a valid integer to prevent passing `NaN` to the storage layer.
</issue_to_address>
### Comment 12
<location> `server/performanceRoutes.ts:13` </location>
<code_context>
+ console.time("gmailAIService.getPerformanceMetrics");
+ const metrics = await gmailAIService.getPerformanceMetrics();
+ console.timeEnd("gmailAIService.getPerformanceMetrics");
+ if (metrics) {
+ res.json(metrics);
+ } else {
</code_context>
<issue_to_address>
Explicitly check for null/undefined metrics
Since an empty object is truthy, the else block won't execute. Use `metrics != null` or check for a required property to properly detect missing data.
</issue_to_address>
### Comment 13
<location> `server/activityRoutes.ts:13` </location>
<code_context>
+ try {
+ const { limit } = req.query;
+ console.time("storage.getRecentActivities");
+ const activities = await storage.getRecentActivities(
+ limit ? parseInt(limit as string) : undefined
+ );
</code_context>
<issue_to_address>
Handle invalid `limit` query parameter
Validate or default `limit` if `parseInt` returns `NaN` to prevent issues when calling storage.
</issue_to_address>
### Comment 14
<location> `server/categoryRoutes.ts:36` </location>
<code_context>
+ }
+});
+
+router.put("/:id", async (req, res) => {
+ try {
+ const id = parseInt(req.params.id);
</code_context>
<issue_to_address>
Validate update payload with schema
Consider validating the PUT request body with a partial schema to restrict updates to allowed fields.
Suggested implementation:
```typescript
import { categorySchema } from "../schemas/categorySchema";
router.put("/:id", async (req, res) => {
try {
const id = parseInt(req.params.id);
// Validate update payload with partial schema
const parseResult = categorySchema.partial().safeParse(req.body);
if (!parseResult.success) {
return res.status(400).json({ message: "Invalid update data", errors: parseResult.error.errors });
}
const updateData = parseResult.data;
console.time("storage.updateCategory");
const category = await storage.updateCategory(id, updateData);
console.timeEnd("storage.updateCategory");
if (!category) {
return res.status(404).json({ message: "Category not found" });
}
res.json(category);
```
- Make sure you have a `categorySchema` defined in `../schemas/categorySchema` and that it is a Zod schema.
- Adjust the import path if your schema is located elsewhere.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| def __init__(self, | ||
| rate_config: Optional[RateLimitConfig] = None, | ||
| advanced_ai_engine: Optional[AdvancedAIEngine] = None, # Typed hint | ||
| db_manager: Optional[DatabaseManager] = None): # Added db_manager | ||
| self.collector = GmailDataCollector(rate_config) | ||
| self.metadata_extractor = GmailMetadataExtractor() | ||
| self.data_strategy = DataCollectionStrategy() | ||
| # self.model_trainer = ModelTrainer() # Potentially remove if not used directly | ||
| # self.prompt_engineer = PromptEngineer() # Potentially remove | ||
| self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}") | ||
|
|
||
| # This is key for the refactoring: GmailAIService will use AdvancedAIEngine | ||
| # If not passed, a placeholder or default might be instantiated, or an error raised. | ||
| # For now, let's assume it's required or a default is handled by the caller. | ||
| self.advanced_ai_engine = advanced_ai_engine | ||
| if advanced_ai_engine: |
There was a problem hiding this comment.
issue (bug_risk): Default instantiation of DatabaseManager may cause issues in some environments.
If configuration is needed, make db_manager a required argument or raise a clear error on failed instantiation.
| async def _match_category_id(self, ai_categories: List[str], db: 'DatabaseManager') -> Optional[int]: | ||
| """Matches AI suggested category strings to database categories.""" | ||
| if not ai_categories: | ||
| return None |
There was a problem hiding this comment.
suggestion: Category matching is case-insensitive but may not handle synonyms or partial matches.
Since the logic only checks for substrings, it may fail when synonyms or different naming conventions are used. Consider implementing a more robust matching method or maintaining a mapping table to improve accuracy.
Suggested implementation:
async def _match_category_id(self, ai_categories: List[str], db: 'DatabaseManager') -> Optional[int]:
"""Matches AI suggested category strings to database categories, using synonyms and robust matching."""
if not ai_categories:
return None
# Synonym mapping: keys are normalized AI category names, values are normalized DB category names
CATEGORY_SYNONYMS = {
"billing": "invoices",
"invoice": "invoices",
"payment": "payments",
"support": "customer support",
"help": "customer support",
"bug": "issue",
"problem": "issue",
"purchase": "orders",
"order": "orders",
# Add more as needed
}
def normalize(s: str) -> str:
return s.strip().lower()
try:
all_db_categories = await db.get_all_categories() # db.get_all_categories() was adapted from get_categories
if not all_db_categories:
return None all_db_categories = await db.get_all_categories() # db.get_all_categories() was adapted from get_categories
if not all_db_categories:
return None
# Build a normalized lookup for DB categories: {normalized_name: (id, original_name)}
db_cat_lookup = {normalize(cat['name']): (cat['id'], cat['name']) for cat in all_db_categories}
for ai_cat in ai_categories:
norm_ai_cat = normalize(ai_cat)
# 1. Try direct match
if norm_ai_cat in db_cat_lookup:
return db_cat_lookup[norm_ai_cat][0]
# 2. Try synonym mapping
if norm_ai_cat in CATEGORY_SYNONYMS:
mapped = CATEGORY_SYNONYMS[norm_ai_cat]
if mapped in db_cat_lookup:
return db_cat_lookup[mapped][0]
# 3. Try substring/partial match
for db_norm, (db_id, db_orig) in db_cat_lookup.items():
if norm_ai_cat in db_norm or db_norm in norm_ai_cat:
return db_id
return None| ) | ||
| raise HTTPException(status_code=500, detail="Failed to fetch email") | ||
|
|
||
| @router.post("/api/emails", response_model=EmailResponse) # Changed to EmailResponse |
There was a problem hiding this comment.
suggestion: Return 201 status code for created emails
Add status_code=201 to the decorator or set it in the response to indicate resource creation.
| @router.post("/api/emails", response_model=EmailResponse) # Changed to EmailResponse | |
| @router.post("/api/emails", response_model=EmailResponse, status_code=201) # Changed to EmailResponse, added status_code=201 |
| ) | ||
| raise HTTPException(status_code=500, detail="Failed to fetch categories") | ||
|
|
||
| @router.post("/api/categories", response_model=CategoryResponse) # Changed to CategoryResponse |
There was a problem hiding this comment.
suggestion: Return 201 status code for created categories
Add status_code=201 to the POST decorator to return the standard HTTP 201 Created response for new categories.
| @router.post("/api/categories", response_model=CategoryResponse) # Changed to CategoryResponse | |
| @router.post("/api/categories", response_model=CategoryResponse, status_code=201) # Changed to CategoryResponse and returns 201 Created |
| try: | ||
| description = filter_request_model.criteria.get("description", "") | ||
|
|
||
| new_filter_object = filter_manager.add_custom_filter( |
There was a problem hiding this comment.
issue (bug_risk): Missing await on add_custom_filter call
Please add 'await' before 'filter_manager.add_custom_filter(...)' to ensure the coroutine is executed properly.
| @performance_monitor.track | ||
| async def generate_intelligent_filters(request: Request, db: DatabaseManager = Depends(get_db)): | ||
| """Generate intelligent filters based on email patterns""" | ||
| try: |
There was a problem hiding this comment.
issue (code-quality): Explicitly raise from a previous error [×2] (raise-from-previous-error)
| @performance_monitor.track | ||
| async def prune_filters(request: Request): | ||
| """Prune ineffective filters""" | ||
| try: |
There was a problem hiding this comment.
issue (code-quality): We've found these issues:
- Inline variable that is immediately returned (
inline-immediately-returned-variable) - Explicitly raise from a previous error (
raise-from-previous-error)
| background_tasks: BackgroundTasks | ||
| ): | ||
| """Sync emails from Gmail with AI analysis""" | ||
| try: |
There was a problem hiding this comment.
issue (code-quality): We've found these issues:
- Use
except Exception:rather than bareexcept:(do-not-use-bare-except) - Explicitly raise from a previous error [×2] (
raise-from-previous-error)
| @performance_monitor.track | ||
| async def smart_retrieval(req: Request, request_model: SmartRetrievalRequest): # Renamed params | ||
| """Execute smart Gmail retrieval with multiple strategies""" | ||
| try: |
There was a problem hiding this comment.
issue (code-quality): We've found these issues:
- Inline variable that is immediately returned (
inline-immediately-returned-variable) - Use
except Exception:rather than bareexcept:(do-not-use-bare-except) - Explicitly raise from a previous error [×2] (
raise-from-previous-error)
| "error_detail": str(e), | ||
| }) | ||
| ) | ||
| raise HTTPException(status_code=500, detail="Failed to fetch strategies") |
There was a problem hiding this comment.
suggestion (code-quality): Explicitly raise from a previous error (raise-from-previous-error)
| raise HTTPException(status_code=500, detail="Failed to fetch strategies") | |
| raise HTTPException( | |
| status_code=500, detail="Failed to fetch strategies" | |
| ) from e |
This commit introduces several changes to enhance the structure and maintainability of the Node.js and Python backend services. Key changes include: - Refactored `server/routes.ts`: Split into multiple, more focused route files (e.g., `emailRoutes.ts`, `aiRoutes.ts`). - Refactored `server/python_backend/main.py`: Divided FastAPI endpoints into separate route modules (e.g., `email_routes.py`, `ai_routes.py`). - Refactored `server/python_nlp/nlp_engine.py`: Renamed analyzer classes to model classes (e.g., `SentimentModel`) and ensured they encapsulate model loading and prediction logic. - Consolidated Logic: Centralized the AI-driven category name to category ID matching logic within the Python `AdvancedAIEngine`. This removes redundancy previously present in the Node.js AI routes. - Organized Python Models: Consolidated Pydantic models used in the Python backend into `server/python_backend/models.py`. - Added Unit Tests: Introduced new unit tests for the refactored components in both Node.js (Jest) and Python (FastAPI TestClient, pytest) to ensure correctness and prevent regressions. These changes aim to make the codebase easier to understand, navigate, and maintain, while also reducing redundancy and improving the separation of concerns.
Refactor: Improve modularity and readability of backend services.
Refactor: Improve modularity and readability of backend services.
This commit introduces several changes to enhance the structure and maintainability of the Node.js and Python backend services.
Key changes include:
server/routes.ts: Split into multiple, more focused route files (e.g.,emailRoutes.ts,aiRoutes.ts).server/python_backend/main.py: Divided FastAPI endpoints into separate route modules (e.g.,email_routes.py,ai_routes.py).server/python_nlp/nlp_engine.py: Renamed analyzer classes to model classes (e.g.,SentimentModel) and ensured they encapsulate model loading and prediction logic.AdvancedAIEngine. This removes redundancy previously present in the Node.js AI routes.server/python_backend/models.py.These changes aim to make the codebase easier to understand, navigate, and maintain, while also reducing redundancy and improving the separation of concerns.
Summary by Sourcery
Refactor backend codebases to improve modularity, readability, and maintainability by splitting monolithic route files into focused modules, centralizing AI logic, consolidating shared models, and renaming components for clarity.
Enhancements:
Tests:
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Tests
Documentation
End-users will benefit from a more robust, modular, and feature-rich API with enhanced AI capabilities and improved reliability.