Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -31,49 +31,90 @@ SUPPORTED_LANGUAGES = {
|
|
31 |
|
32 |
# ===== ASPECT CONFIGURATION =====
|
33 |
aspect_map = {
|
34 |
-
# Location
|
35 |
"location": ["location", "near", "close", "access", "transport", "distance", "area", "tsim sha tsui", "kowloon"],
|
36 |
"view": ["view", "scenery", "vista", "panorama", "outlook", "skyline"],
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
"room
|
41 |
-
|
42 |
-
|
43 |
-
"
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
# Facilities
|
47 |
-
"dining": ["breakfast", "dinner", "restaurant", "meal", "food"],
|
48 |
-
"
|
49 |
-
|
|
|
|
|
|
|
50 |
# Technical
|
51 |
-
"Wi-Fi": ["wifi", "internet", "connection"],
|
52 |
-
"AC": ["air conditioning", "AC", "temperature"]
|
|
|
|
|
|
|
|
|
|
|
53 |
}
|
54 |
|
55 |
aspect_responses = {
|
56 |
-
"location": "We're delighted you enjoyed our prime location
|
57 |
-
"view": "It's wonderful to hear you appreciated the views from
|
58 |
-
"room comfort": "Our team
|
59 |
-
"room cleanliness": "Your
|
60 |
-
"staff service": "Your
|
61 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
}
|
63 |
|
64 |
improvement_actions = {
|
65 |
-
"AC": "
|
66 |
-
"housekeeping": "
|
67 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
}
|
69 |
|
70 |
# ===== MODEL CONFIGURATION =====
|
71 |
TRANSLATION_MODELS = {
|
|
|
72 |
'zh-en': 'Helsinki-NLP/opus-mt-zh-en',
|
73 |
'ja-en': 'Helsinki-NLP/opus-mt-ja-en',
|
74 |
'ko-en': 'Helsinki-NLP/opus-mt-ko-en',
|
75 |
'fr-en': 'Helsinki-NLP/opus-mt-fr-en',
|
76 |
'de-en': 'Helsinki-NLP/opus-mt-de-en',
|
|
|
|
|
77 |
'en-zh': 'Helsinki-NLP/opus-mt-en-zh',
|
78 |
'en-ja': 'Helsinki-NLP/opus-mt-en-ja',
|
79 |
'en-ko': 'Helsinki-NLP/opus-mt-en-ko',
|
@@ -148,39 +189,50 @@ def detect_aspects(text, aspect_classifier):
|
|
148 |
return []
|
149 |
|
150 |
def generate_response(sentiment, aspects, original_text):
|
|
|
151 |
guest_name = ""
|
|
|
152 |
name_match = re.search(r"(Mr\.|Ms\.|Mrs\.)\s(\w+)", original_text, re.IGNORECASE)
|
|
|
|
|
153 |
if name_match:
|
154 |
guest_name = f" {name_match.group(2)}"
|
155 |
-
|
|
|
|
|
156 |
if sentiment['label'] == 1:
|
157 |
response = f"""Dear{guest_name if guest_name else ' Valued Guest'},
|
158 |
|
159 |
-
Thank you for choosing The Kimberley Hotel Hong Kong."""
|
160 |
|
|
|
161 |
added_aspects = set()
|
162 |
for aspect, _ in sorted(aspects, key=lambda x: float(x[1][:-1]), reverse=True):
|
163 |
if aspect in aspect_responses:
|
164 |
-
|
|
|
|
|
|
|
165 |
added_aspects.add(aspect)
|
166 |
-
if len(added_aspects) >=
|
167 |
break
|
168 |
|
169 |
response += "\n\nWe look forward to welcoming you back.\n\nBest regards,"
|
170 |
else:
|
171 |
response = f"""Dear{guest_name if guest_name else ' Guest'},
|
172 |
|
173 |
-
Thank you for your feedback."""
|
174 |
|
|
|
175 |
added_improvements = set()
|
176 |
for aspect, _ in sorted(aspects, key=lambda x: float(x[1][:-1]), reverse=True):
|
177 |
if aspect in improvement_actions:
|
178 |
-
response += f"\n\nRegarding
|
179 |
added_improvements.add(aspect)
|
180 |
if len(added_improvements) >= 2:
|
181 |
break
|
182 |
|
183 |
-
response += "\n\nPlease contact
|
184 |
|
185 |
return response + "\nSam Tse\nGuest Relations Manager\nThe Kimberley Hotel Hong Kong"
|
186 |
|
@@ -199,6 +251,7 @@ def main():
|
|
199 |
.char-counter { font-size: 12px; color: #666; text-align: right; margin-top: -15px; }
|
200 |
.char-counter.warning { color: #ff6b6b; }
|
201 |
.result-box { border-left: 4px solid #003366; padding: 15px; background-color: #f9f9f9; margin: 20px 0; }
|
|
|
202 |
</style>
|
203 |
""", unsafe_allow_html=True)
|
204 |
|
@@ -252,7 +305,7 @@ def main():
|
|
252 |
|
253 |
sentiment = analyze_sentiment(analysis_text, sentiment_model, tokenizer)
|
254 |
aspects = detect_aspects(analysis_text, aspect_classifier)
|
255 |
-
response = generate_response(sentiment, aspects,
|
256 |
|
257 |
# Translate response back if needed
|
258 |
if review_lang != 'en':
|
@@ -274,7 +327,7 @@ def main():
|
|
274 |
st.markdown("### Key Aspects")
|
275 |
if aspects:
|
276 |
for aspect, score in sorted(aspects, key=lambda x: float(x[1][:-1]), reverse=True):
|
277 |
-
st.markdown(f"-
|
278 |
else:
|
279 |
st.markdown("_No specific aspects detected_")
|
280 |
|
|
|
31 |
|
32 |
# ===== ASPECT CONFIGURATION =====
|
33 |
aspect_map = {
|
34 |
+
# Location related
|
35 |
"location": ["location", "near", "close", "access", "transport", "distance", "area", "tsim sha tsui", "kowloon"],
|
36 |
"view": ["view", "scenery", "vista", "panorama", "outlook", "skyline"],
|
37 |
+
"parking": ["parking", "valet", "garage", "car park", "vehicle"],
|
38 |
+
|
39 |
+
# Room related
|
40 |
+
"room comfort": ["comfortable", "bed", "pillows", "mattress", "linens", "cozy", "hard", "soft"],
|
41 |
+
"room cleanliness": ["clean", "dirty", "spotless", "stains", "hygiene", "sanitation", "dusty"],
|
42 |
+
"room amenities": ["amenities", "minibar", "coffee", "tea", "fridge", "facilities", "tv", "kettle"],
|
43 |
+
"bathroom": ["bathroom", "shower", "toilet", "sink", "towel", "faucet", "toiletries"],
|
44 |
+
|
45 |
+
# Service related
|
46 |
+
"staff service": ["staff", "friendly", "helpful", "rude", "welcoming", "employee", "manager"],
|
47 |
+
"reception": ["reception", "check-in", "check-out", "front desk", "welcome", "registration"],
|
48 |
+
"housekeeping": ["housekeeping", "maid", "cleaning", "towels", "service", "turndown"],
|
49 |
+
"concierge": ["concierge", "recommendation", "advice", "tips", "guidance", "directions"],
|
50 |
+
"room service": ["room service", "food delivery", "order", "meal", "tray"],
|
51 |
+
|
52 |
# Facilities
|
53 |
+
"dining": ["breakfast", "dinner", "restaurant", "meal", "food", "buffet", "lunch"],
|
54 |
+
"bar": ["bar", "drinks", "cocktail", "wine", "lounge", "happy hour"],
|
55 |
+
"pool": ["pool", "swimming", "jacuzzi", "sun lounger", "deck", "towels"],
|
56 |
+
"spa": ["spa", "massage", "treatment", "relax", "wellness", "sauna"],
|
57 |
+
"fitness": ["gym", "fitness", "exercise", "workout", "training", "weights"],
|
58 |
+
|
59 |
# Technical
|
60 |
+
"Wi-Fi": ["wifi", "internet", "connection", "online", "network", "speed"],
|
61 |
+
"AC": ["air conditioning", "AC", "temperature", "heating", "cooling", "ventilation"],
|
62 |
+
"elevator": ["elevator", "lift", "escalator", "vertical transport", "wait"],
|
63 |
+
|
64 |
+
# Value
|
65 |
+
"pricing": ["price", "expensive", "cheap", "value", "rate", "cost", "worth"],
|
66 |
+
"extra charges": ["charge", "fee", "bill", "surcharge", "additional", "hidden"]
|
67 |
}
|
68 |
|
69 |
aspect_responses = {
|
70 |
+
"location": "We're delighted you enjoyed our prime location and convenient access to local attractions.",
|
71 |
+
"view": "It's wonderful to hear you appreciated the beautiful views from our property.",
|
72 |
+
"room comfort": "Our team is thrilled you found your room comfortable and inviting.",
|
73 |
+
"room cleanliness": "Your commendation of our cleanliness standards means a lot to our housekeeping staff.",
|
74 |
+
"staff service": "Your kind words about our team, especially {staff_name}, have been shared with them.",
|
75 |
+
"reception": "We're pleased our front desk team made your arrival/departure seamless.",
|
76 |
+
"spa": "Our spa practitioners will be delighted you enjoyed their treatments.",
|
77 |
+
"pool": "We're glad you had a refreshing time at our pool facilities.",
|
78 |
+
"dining": "Thank you for appreciating our culinary offerings - we've shared your feedback with our chefs.",
|
79 |
+
"concierge": "We're happy our concierge could enhance your stay with local insights.",
|
80 |
+
"fitness": "It's great to hear you made use of our well-equipped fitness center.",
|
81 |
+
"room service": "We're pleased our in-room dining met your expectations for quality and timeliness.",
|
82 |
+
"parking": "We're glad our parking facilities met your needs during your stay.",
|
83 |
+
"bathroom": "We appreciate your feedback about our bathroom amenities and cleanliness.",
|
84 |
+
"bar": "Thank you for your comments about our bar service and beverage selection.",
|
85 |
+
"housekeeping": "Your feedback about our housekeeping service has been shared with the team.",
|
86 |
+
"Wi-Fi": "We're pleased our internet service met your connectivity needs.",
|
87 |
+
"elevator": "We're glad our elevator service provided convenient access during your stay."
|
88 |
}
|
89 |
|
90 |
improvement_actions = {
|
91 |
+
"AC": "completed a full inspection and maintenance of all AC units",
|
92 |
+
"housekeeping": "retrained our housekeeping team and adjusted schedules",
|
93 |
+
"bathroom": "conducted deep cleaning and maintenance on all bathrooms",
|
94 |
+
"parking": "implemented new key management protocols with our valet service",
|
95 |
+
"dining": "reviewed our menu pricing and quality with the culinary team",
|
96 |
+
"reception": "provided additional customer service training to our front desk",
|
97 |
+
"elevator": "performed full servicing and testing of all elevators",
|
98 |
+
"room amenities": "begun upgrading in-room amenities based on guest feedback",
|
99 |
+
"noise": "initiated soundproofing improvements in affected areas",
|
100 |
+
"pricing": "started a comprehensive review of our pricing structure",
|
101 |
+
"Wi-Fi": "are upgrading our network infrastructure for better connectivity",
|
102 |
+
"bar": "have reviewed our beverage service and inventory procedures",
|
103 |
+
"staff service": "have implemented additional staff training programs",
|
104 |
+
"room service": "have optimized our food delivery processes",
|
105 |
+
"fitness": "are upgrading our gym equipment based on guest feedback"
|
106 |
}
|
107 |
|
108 |
# ===== MODEL CONFIGURATION =====
|
109 |
TRANSLATION_MODELS = {
|
110 |
+
# Translations to English
|
111 |
'zh-en': 'Helsinki-NLP/opus-mt-zh-en',
|
112 |
'ja-en': 'Helsinki-NLP/opus-mt-ja-en',
|
113 |
'ko-en': 'Helsinki-NLP/opus-mt-ko-en',
|
114 |
'fr-en': 'Helsinki-NLP/opus-mt-fr-en',
|
115 |
'de-en': 'Helsinki-NLP/opus-mt-de-en',
|
116 |
+
|
117 |
+
# Translations from English
|
118 |
'en-zh': 'Helsinki-NLP/opus-mt-en-zh',
|
119 |
'en-ja': 'Helsinki-NLP/opus-mt-en-ja',
|
120 |
'en-ko': 'Helsinki-NLP/opus-mt-en-ko',
|
|
|
189 |
return []
|
190 |
|
191 |
def generate_response(sentiment, aspects, original_text):
|
192 |
+
# Personalization
|
193 |
guest_name = ""
|
194 |
+
staff_name = ""
|
195 |
name_match = re.search(r"(Mr\.|Ms\.|Mrs\.)\s(\w+)", original_text, re.IGNORECASE)
|
196 |
+
staff_match = re.search(r"(receptionist|manager|concierge|chef)\s(\w+)", original_text, re.IGNORECASE)
|
197 |
+
|
198 |
if name_match:
|
199 |
guest_name = f" {name_match.group(2)}"
|
200 |
+
if staff_match:
|
201 |
+
staff_name = staff_match.group(2)
|
202 |
+
|
203 |
if sentiment['label'] == 1:
|
204 |
response = f"""Dear{guest_name if guest_name else ' Valued Guest'},
|
205 |
|
206 |
+
Thank you for choosing The Kimberley Hotel Hong Kong and for sharing your feedback."""
|
207 |
|
208 |
+
# Add relevant aspect responses
|
209 |
added_aspects = set()
|
210 |
for aspect, _ in sorted(aspects, key=lambda x: float(x[1][:-1]), reverse=True):
|
211 |
if aspect in aspect_responses:
|
212 |
+
response_text = aspect_responses[aspect]
|
213 |
+
if "{staff_name}" in response_text and staff_name:
|
214 |
+
response_text = response_text.format(staff_name=staff_name)
|
215 |
+
response += "\n\n" + response_text
|
216 |
added_aspects.add(aspect)
|
217 |
+
if len(added_aspects) >= 3:
|
218 |
break
|
219 |
|
220 |
response += "\n\nWe look forward to welcoming you back.\n\nBest regards,"
|
221 |
else:
|
222 |
response = f"""Dear{guest_name if guest_name else ' Guest'},
|
223 |
|
224 |
+
Thank you for your feedback. We sincerely apologize for any inconvenience you experienced."""
|
225 |
|
226 |
+
# Add improvement actions
|
227 |
added_improvements = set()
|
228 |
for aspect, _ in sorted(aspects, key=lambda x: float(x[1][:-1]), reverse=True):
|
229 |
if aspect in improvement_actions:
|
230 |
+
response += f"\n\nRegarding the {aspect}, we've {improvement_actions[aspect]}."
|
231 |
added_improvements.add(aspect)
|
232 |
if len(added_improvements) >= 2:
|
233 |
break
|
234 |
|
235 |
+
response += "\n\nPlease contact our Guest Relations team if we can assist you further.\n\nSincerely,"
|
236 |
|
237 |
return response + "\nSam Tse\nGuest Relations Manager\nThe Kimberley Hotel Hong Kong"
|
238 |
|
|
|
251 |
.char-counter { font-size: 12px; color: #666; text-align: right; margin-top: -15px; }
|
252 |
.char-counter.warning { color: #ff6b6b; }
|
253 |
.result-box { border-left: 4px solid #003366; padding: 15px; background-color: #f9f9f9; margin: 20px 0; }
|
254 |
+
.aspect-badge { background-color: #e6f2ff; padding: 2px 8px; border-radius: 4px; display: inline-block; margin: 2px; }
|
255 |
</style>
|
256 |
""", unsafe_allow_html=True)
|
257 |
|
|
|
305 |
|
306 |
sentiment = analyze_sentiment(analysis_text, sentiment_model, tokenizer)
|
307 |
aspects = detect_aspects(analysis_text, aspect_classifier)
|
308 |
+
response = generate_response(sentiment, aspects, review) # Use original text for name extraction
|
309 |
|
310 |
# Translate response back if needed
|
311 |
if review_lang != 'en':
|
|
|
327 |
st.markdown("### Key Aspects")
|
328 |
if aspects:
|
329 |
for aspect, score in sorted(aspects, key=lambda x: float(x[1][:-1]), reverse=True):
|
330 |
+
st.markdown(f'<div class="aspect-badge">{aspect} ({score})</div>', unsafe_allow_html=True)
|
331 |
else:
|
332 |
st.markdown("_No specific aspects detected_")
|
333 |
|