Skip to content

Commit e84ab5c

Browse files
date issue fix
1 parent ec23bc8 commit e84ab5c

File tree

14 files changed

+235
-28
lines changed

14 files changed

+235
-28
lines changed

src/backend/app_config.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,22 @@ def get_ai_project_client(self):
182182
except Exception as exc:
183183
logging.error("Failed to create AIProjectClient: %s", exc)
184184
raise
185+
186+
def get_user_local_browser_language(self) -> str:
187+
"""Get the user's local browser language from environment variables.
188+
189+
Returns:
190+
The user's local browser language or 'en-US' if not set
191+
"""
192+
return self._get_optional("USER_LOCAL_BROWSER_LANGUAGE", "en-US")
193+
194+
def set_user_local_browser_language(self, language: str):
195+
"""Set the user's local browser language in environment variables.
196+
197+
Args:
198+
language: The language code to set (e.g., 'en-US')
199+
"""
200+
os.environ["USER_LOCAL_BROWSER_LANGUAGE"] = language
185201

186202

187203
# Create a global instance of AppConfig

src/backend/app_kernel.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
InputTask,
3030
PlanWithSteps,
3131
Step,
32+
UserLanguage
3233
)
3334

3435
# Updated import for KernelArguments
@@ -70,7 +71,7 @@
7071
# Add this near the top of your app.py, after initializing the app
7172
app.add_middleware(
7273
CORSMiddleware,
73-
allow_origins=[frontend_url],
74+
allow_origins=['*'],
7475
allow_credentials=True,
7576
allow_methods=["*"],
7677
allow_headers=["*"],
@@ -80,6 +81,39 @@
8081
app.add_middleware(HealthCheckMiddleware, password="", checks={})
8182
logging.info("Added health check middleware")
8283

84+
@app.post("/api/user_browser_language")
85+
async def user_browser_language_endpoint(
86+
user_language: UserLanguage,
87+
request: Request
88+
):
89+
"""
90+
Receive the user's browser language.
91+
92+
---
93+
tags:
94+
- User
95+
parameters:
96+
- name: language
97+
in: query
98+
type: string
99+
required: true
100+
description: The user's browser language
101+
responses:
102+
200:
103+
description: Language received successfully
104+
schema:
105+
type: object
106+
properties:
107+
status:
108+
type: string
109+
description: Confirmation message
110+
"""
111+
config.set_user_local_browser_language(user_language.language)
112+
113+
# Log the received language for the user
114+
logging.info(f"Received browser language '{user_language}' for user ")
115+
116+
return {"status": "Language received successfully"}
83117

84118
@app.post("/api/input_task")
85119
async def input_task_endpoint(input_task: InputTask, request: Request):

src/backend/kernel_tools/hr_tools.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,27 @@
66
import json
77
from typing import get_type_hints
88
from utils_date import format_date_for_user
9+
from app_config import config
910

1011

1112
class HrTools:
1213
# Define HR tools (functions)
13-
formatting_instructions = "Instructions: returning the output of this function call verbatim to the user in markdown. Then write AGENT SUMMARY: and then include a summary of what you did."
14+
selecetd_language= config.get_user_local_browser_language()
15+
formatting_instructions = "Instructions: returning the output of this function call verbatim to the user in markdown. Then write AGENT SUMMARY: and then include a summary of what you did. Convert all date strings in the following text to short date format with 3-letter month (MMM) in the {selecetd_language} locale (e.g., en-US, en-IN), remove time, and replace original dates with the formatted ones"
1416
agent_name = AgentType.HR.value
1517

1618
@staticmethod
1719
@kernel_function(description="Schedule an orientation session for a new employee.")
1820
async def schedule_orientation_session(employee_name: str, date: str) -> str:
19-
formatted_date = format_date_for_user(date)
21+
#formatted_date = format_date_for_user(date)
2022

2123
return (
2224
f"##### Orientation Session Scheduled\n"
2325
f"**Employee Name:** {employee_name}\n"
24-
f"**Date:** {formatted_date}\n\n"
26+
f"**Date:** {date}\n\n"
2527
f"Your orientation session has been successfully scheduled. "
2628
f"Please mark your calendar and be prepared for an informative session.\n"
27-
f"AGENT SUMMARY: I scheduled the orientation session for {employee_name} on {formatted_date}, as part of her onboarding process.\n"
29+
#f"AGENT SUMMARY: I scheduled the orientation session for {employee_name} on {formatted_date}, as part of her onboarding process.\n"
2830
f"{HrTools.formatting_instructions}"
2931
)
3032

src/backend/models/messages_kernel.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ class InputTask(KernelBaseModel):
262262

263263
session_id: str
264264
description: str # Initial goal
265+
266+
class UserLanguage(KernelBaseModel):
267+
language: str
265268

266269

267270
class ApprovalRequest(KernelBaseModel):

src/backend/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ azure-ai-evaluation
2323

2424
opentelemetry-exporter-otlp-proto-grpc
2525

26+
# Date and internationalization
27+
babel>=2.9.0
28+
2629
# Testing tools
2730
pytest>=8.2,<9 # Compatible version for pytest-asyncio
2831
pytest-asyncio==0.24.0
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
Quick test for the fixed utils_date.py functionality
3+
"""
4+
5+
import os
6+
from datetime import datetime
7+
from utils_date import format_date_for_user
8+
9+
def test_date_formatting():
10+
"""Test the date formatting function with various inputs"""
11+
12+
# Set up different language environments
13+
test_cases = [
14+
('en-US', '2025-07-29', 'US English'),
15+
('en-IN', '2025-07-29', 'Indian English'),
16+
('en-GB', '2025-07-29', 'British English'),
17+
('fr-FR', '2025-07-29', 'French'),
18+
('de-DE', '2025-07-29', 'German'),
19+
]
20+
21+
print("Testing date formatting with different locales:")
22+
print("=" * 50)
23+
24+
for locale, date_str, description in test_cases:
25+
os.environ['USER_LOCAL_BROWSER_LANGUAGE'] = locale
26+
try:
27+
result = format_date_for_user(date_str)
28+
print(f"{description} ({locale}): {result}")
29+
except Exception as e:
30+
print(f"{description} ({locale}): ERROR - {e}")
31+
32+
print("\n" + "=" * 50)
33+
print("Testing with datetime object:")
34+
35+
# Test with datetime object
36+
os.environ['USER_LOCAL_BROWSER_LANGUAGE'] = 'en-US'
37+
dt = datetime(2025, 7, 29, 14, 30, 0)
38+
result = format_date_for_user(dt)
39+
print(f"Datetime object: {result}")
40+
41+
print("\nTesting error handling:")
42+
print("=" * 30)
43+
44+
# Test error handling
45+
try:
46+
result = format_date_for_user('invalid-date-string')
47+
print(f"Invalid date: {result}")
48+
except Exception as e:
49+
print(f"Invalid date: ERROR - {e}")
50+
51+
if __name__ == "__main__":
52+
test_date_formatting()

src/backend/tests/test_utils_date_enhanced.py

Whitespace-only changes.

src/backend/utils_date.py

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,117 @@
33
import logging
44
from typing import Optional
55

6+
from app_config import config
7+
8+
# Try to import babel, with fallback if not available
9+
try:
10+
from babel.dates import format_datetime
11+
BABEL_AVAILABLE = True
12+
except ImportError:
13+
BABEL_AVAILABLE = False
14+
logging.warning("Babel library not available. Date formatting will use basic Python formatting.")
15+
16+
17+
def _format_date_fallback(date_obj: datetime, language_code: str) -> str:
18+
"""
19+
Fallback date formatting when babel is not available.
20+
21+
Args:
22+
date_obj (datetime): The datetime object to format
23+
language_code (str): Language code like 'en-US', 'en-IN', etc.
24+
25+
Returns:
26+
str: Formatted date string
27+
"""
28+
# Normalize the language code
29+
normalized_code = language_code.replace('-', '_')
30+
31+
# Define basic date formats for different locales
32+
locale_date_formats = {
33+
'en_IN': '%d %B, %Y', # 29 July, 2025
34+
'en_US': '%B %d, %Y', # July 29, 2025
35+
'en_GB': '%d %B %Y', # 29 July 2025
36+
'en_AU': '%d %B %Y', # 29 July 2025
37+
'en_CA': '%B %d, %Y', # July 29, 2025
38+
'es_ES': '%d de %B de %Y', # Would need Spanish month names
39+
'fr_FR': '%d %B %Y', # Would need French month names
40+
'de_DE': '%d. %B %Y', # Would need German month names
41+
'ja_JP': '%Y年%m月%d日', # 2025年07月29日
42+
'ko_KR': '%Y년 %m월 %d일', # 2025년 07월 29일
43+
'zh_CN': '%Y年%m月%d日', # 2025年07月29日
44+
}
45+
46+
# Get the format for the locale, default to US format
47+
date_format = locale_date_formats.get(normalized_code, '%B %d, %Y')
48+
49+
try:
50+
return date_obj.strftime(date_format)
51+
except Exception as e:
52+
logging.warning("Fallback date formatting failed: %s", str(e))
53+
return date_obj.strftime('%Y-%m-%d') # ISO format as last resort
54+
655

756
def format_date_for_user(date_str: str, user_locale: Optional[str] = None) -> str:
857
"""
958
Format date based on user's desktop locale preference.
1059
1160
Args:
12-
date_str (str): Date in ISO format (YYYY-MM-DD).
61+
date_str (str): Date in ISO format (YYYY-MM-DD) or datetime object.
1362
user_locale (str, optional): User's locale string, e.g., 'en_US', 'en_GB'.
1463
1564
Returns:
1665
str: Formatted date respecting locale or raw date if formatting fails.
1766
"""
1867
try:
19-
date_obj = datetime.strptime(date_str, "%Y-%m-%d")
20-
locale.setlocale(locale.LC_TIME, user_locale or '')
21-
return date_obj.strftime("%B %d, %Y")
68+
# Get user's browser language from config
69+
lang = config.get_user_local_browser_language() # e.g., 'en-US', 'fr-FR', 'de-DE'
70+
71+
# Parse the date string if it's a string, otherwise use as-is if it's already a datetime
72+
if isinstance(date_str, str):
73+
# Try different date formats
74+
date_formats = [
75+
"%Y-%m-%d", # 2025-07-29
76+
"%Y-%m-%d", # 2025-07-29 14:30:00
77+
"%Y-%m-%d", # 2025-07-29T14:30:00
78+
"%d/%m/%Y", # 29/07/2025
79+
"%m/%d/%Y", # 07/29/2025
80+
]
81+
82+
parsed_date = None
83+
for date_format in date_formats:
84+
try:
85+
parsed_date = datetime.strptime(date_str, date_format)
86+
break
87+
except ValueError:
88+
continue
89+
90+
if parsed_date is None:
91+
logging.warning("Could not parse date string: %s", date_str)
92+
return date_str # Return original string if parsing fails
93+
94+
date_to_format = parsed_date
95+
else:
96+
# Assume it's already a datetime object
97+
date_to_format = date_str
98+
99+
# Format the date using babel with the user's locale, or fallback to basic formatting
100+
if BABEL_AVAILABLE:
101+
try:
102+
# Babel expects locale in format like 'en_US', not 'en-US'
103+
babel_locale = lang.replace('-', '_')
104+
formatted_date = format_datetime(date_to_format, locale=babel_locale)
105+
except Exception as e:
106+
logging.warning("Babel formatting failed: %s. Using fallback formatting.", str(e))
107+
formatted_date = _format_date_fallback(date_to_format, lang)
108+
else:
109+
formatted_date = _format_date_fallback(date_to_format, lang)
110+
111+
print(
112+
f"Formatted date for user ######################### : {formatted_date} using locale: {lang},browser lang {config.get_user_local_browser_language()}, locale: {babel_locale}"
113+
)
114+
return formatted_date
115+
22116
except Exception as e:
23-
logging.warning(f"Date formatting failed for '{date_str}': {e}")
24-
return date_str
117+
logging.error("Error formatting date '%s': %s", date_str, str(e))
118+
# Return the original input if anything goes wrong
119+
return str(date_str)

src/frontend/src/api/apiClient.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,8 @@ const fetchWithAuth = async (url: string, method: string = "GET", body: BodyInit
4545
try {
4646
const apiUrl = getApiUrl();
4747
const finalUrl = `${apiUrl}${url}`;
48-
console.log('Final URL:', finalUrl);
49-
console.log('Request Options:', options);
5048
// Log the request details
5149
const response = await fetch(finalUrl, options);
52-
console.log('response', response);
5350

5451
if (!response.ok) {
5552
const errorText = await response.text();
@@ -58,8 +55,6 @@ const fetchWithAuth = async (url: string, method: string = "GET", body: BodyInit
5855

5956
const isJson = response.headers.get('content-type')?.includes('application/json');
6057
const responseData = isJson ? await response.json() : null;
61-
62-
console.log('Response JSON:', responseData);
6358
return responseData;
6459
} catch (error) {
6560
console.info('API Error:', (error as Error).message);
@@ -87,7 +82,6 @@ const fetchWithoutAuth = async (url: string, method: string = "POST", body: Body
8782
const errorText = await response.text();
8883
throw new Error(errorText || 'Login failed');
8984
}
90-
console.log('response', response);
9185
const isJson = response.headers.get('content-type')?.includes('application/json');
9286
return isJson ? await response.json() : null;
9387
} catch (error) {

src/frontend/src/api/apiService.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const API_ENDPOINTS = {
2121
APPROVE_STEPS: '/approve_step_or_steps',
2222
HUMAN_CLARIFICATION: '/human_clarification_on_plan',
2323
AGENT_MESSAGES: '/agent_messages',
24-
MESSAGES: '/messages'
24+
MESSAGES: '/messages',
25+
USER_BROWSER_LANGUAGE: '/user_browser_language'
2526
};
2627

2728
// Simple cache implementation
@@ -500,6 +501,18 @@ export class APIService {
500501

501502
return Math.round((completedSteps / plan.steps.length) * 100);
502503
}
504+
505+
/**
506+
* Send the user's browser language to the backend
507+
* @returns Promise with response object
508+
*/
509+
async sendUserBrowserLanguage(): Promise<{ status: string }> {
510+
const language = navigator.language || navigator.languages[0] || 'en';
511+
const response = await apiClient.post(API_ENDPOINTS.USER_BROWSER_LANGUAGE, {
512+
language
513+
});
514+
return response;
515+
}
503516
}
504517

505518
// Export a singleton instance

0 commit comments

Comments
 (0)