wappa 0.1.9__py3-none-any.whl → 0.1.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of wappa might be problematic. Click here for more details.
- wappa/__init__.py +4 -5
- wappa/api/controllers/webhook_controller.py +5 -2
- wappa/api/dependencies/__init__.py +0 -5
- wappa/api/middleware/error_handler.py +4 -4
- wappa/api/middleware/owner.py +11 -5
- wappa/api/routes/webhooks.py +2 -2
- wappa/cli/__init__.py +1 -1
- wappa/cli/examples/init/app/main.py +2 -1
- wappa/cli/examples/init/app/master_event.py +5 -3
- wappa/cli/examples/json_cache_example/app/__init__.py +1 -1
- wappa/cli/examples/json_cache_example/app/main.py +56 -44
- wappa/cli/examples/json_cache_example/app/master_event.py +181 -145
- wappa/cli/examples/json_cache_example/app/models/__init__.py +1 -1
- wappa/cli/examples/json_cache_example/app/models/json_demo_models.py +32 -51
- wappa/cli/examples/json_cache_example/app/scores/__init__.py +2 -2
- wappa/cli/examples/json_cache_example/app/scores/score_base.py +52 -46
- wappa/cli/examples/json_cache_example/app/scores/score_cache_statistics.py +70 -62
- wappa/cli/examples/json_cache_example/app/scores/score_message_history.py +41 -44
- wappa/cli/examples/json_cache_example/app/scores/score_state_commands.py +83 -71
- wappa/cli/examples/json_cache_example/app/scores/score_user_management.py +73 -57
- wappa/cli/examples/json_cache_example/app/utils/__init__.py +2 -2
- wappa/cli/examples/json_cache_example/app/utils/cache_utils.py +54 -56
- wappa/cli/examples/json_cache_example/app/utils/message_utils.py +85 -80
- wappa/cli/examples/openai_transcript/app/main.py +2 -1
- wappa/cli/examples/openai_transcript/app/master_event.py +31 -22
- wappa/cli/examples/openai_transcript/app/openai_utils/__init__.py +1 -1
- wappa/cli/examples/openai_transcript/app/openai_utils/audio_processing.py +37 -24
- wappa/cli/examples/redis_cache_example/app/__init__.py +1 -1
- wappa/cli/examples/redis_cache_example/app/main.py +56 -44
- wappa/cli/examples/redis_cache_example/app/master_event.py +181 -145
- wappa/cli/examples/redis_cache_example/app/models/redis_demo_models.py +31 -50
- wappa/cli/examples/redis_cache_example/app/scores/__init__.py +2 -2
- wappa/cli/examples/redis_cache_example/app/scores/score_base.py +52 -46
- wappa/cli/examples/redis_cache_example/app/scores/score_cache_statistics.py +70 -62
- wappa/cli/examples/redis_cache_example/app/scores/score_message_history.py +41 -44
- wappa/cli/examples/redis_cache_example/app/scores/score_state_commands.py +83 -71
- wappa/cli/examples/redis_cache_example/app/scores/score_user_management.py +73 -57
- wappa/cli/examples/redis_cache_example/app/utils/__init__.py +2 -2
- wappa/cli/examples/redis_cache_example/app/utils/cache_utils.py +54 -56
- wappa/cli/examples/redis_cache_example/app/utils/message_utils.py +85 -80
- wappa/cli/examples/simple_echo_example/app/__init__.py +1 -1
- wappa/cli/examples/simple_echo_example/app/main.py +41 -33
- wappa/cli/examples/simple_echo_example/app/master_event.py +78 -57
- wappa/cli/examples/wappa_full_example/app/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/handlers/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/handlers/command_handlers.py +134 -126
- wappa/cli/examples/wappa_full_example/app/handlers/message_handlers.py +237 -229
- wappa/cli/examples/wappa_full_example/app/handlers/state_handlers.py +170 -148
- wappa/cli/examples/wappa_full_example/app/main.py +51 -39
- wappa/cli/examples/wappa_full_example/app/master_event.py +179 -120
- wappa/cli/examples/wappa_full_example/app/models/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/models/state_models.py +113 -104
- wappa/cli/examples/wappa_full_example/app/models/user_models.py +92 -76
- wappa/cli/examples/wappa_full_example/app/models/webhook_metadata.py +109 -83
- wappa/cli/examples/wappa_full_example/app/utils/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/utils/cache_utils.py +132 -113
- wappa/cli/examples/wappa_full_example/app/utils/media_handler.py +175 -132
- wappa/cli/examples/wappa_full_example/app/utils/metadata_extractor.py +126 -87
- wappa/cli/main.py +9 -4
- wappa/core/__init__.py +18 -23
- wappa/core/config/settings.py +7 -5
- wappa/core/events/default_handlers.py +1 -1
- wappa/core/factory/wappa_builder.py +38 -25
- wappa/core/plugins/redis_plugin.py +1 -3
- wappa/core/plugins/wappa_core_plugin.py +7 -6
- wappa/core/types.py +12 -12
- wappa/core/wappa_app.py +10 -8
- wappa/database/__init__.py +3 -4
- wappa/domain/enums/messenger_platform.py +1 -2
- wappa/domain/factories/media_factory.py +5 -20
- wappa/domain/factories/message_factory.py +5 -20
- wappa/domain/factories/messenger_factory.py +2 -4
- wappa/domain/interfaces/cache_interface.py +7 -7
- wappa/domain/interfaces/media_interface.py +2 -5
- wappa/domain/models/media_result.py +1 -3
- wappa/domain/models/platforms/platform_config.py +1 -3
- wappa/messaging/__init__.py +9 -12
- wappa/messaging/whatsapp/handlers/whatsapp_media_handler.py +20 -22
- wappa/models/__init__.py +27 -35
- wappa/persistence/__init__.py +12 -15
- wappa/persistence/cache_factory.py +0 -1
- wappa/persistence/json/__init__.py +1 -1
- wappa/persistence/json/cache_adapters.py +37 -25
- wappa/persistence/json/handlers/state_handler.py +60 -52
- wappa/persistence/json/handlers/table_handler.py +51 -49
- wappa/persistence/json/handlers/user_handler.py +71 -55
- wappa/persistence/json/handlers/utils/file_manager.py +42 -39
- wappa/persistence/json/handlers/utils/key_factory.py +1 -1
- wappa/persistence/json/handlers/utils/serialization.py +13 -11
- wappa/persistence/json/json_cache_factory.py +4 -8
- wappa/persistence/json/storage_manager.py +66 -79
- wappa/persistence/memory/__init__.py +1 -1
- wappa/persistence/memory/cache_adapters.py +37 -25
- wappa/persistence/memory/handlers/state_handler.py +62 -52
- wappa/persistence/memory/handlers/table_handler.py +59 -53
- wappa/persistence/memory/handlers/user_handler.py +75 -55
- wappa/persistence/memory/handlers/utils/key_factory.py +1 -1
- wappa/persistence/memory/handlers/utils/memory_store.py +75 -71
- wappa/persistence/memory/handlers/utils/ttl_manager.py +59 -67
- wappa/persistence/memory/memory_cache_factory.py +3 -7
- wappa/persistence/memory/storage_manager.py +52 -62
- wappa/persistence/redis/cache_adapters.py +27 -21
- wappa/persistence/redis/ops.py +11 -11
- wappa/persistence/redis/redis_client.py +4 -6
- wappa/persistence/redis/redis_manager.py +12 -4
- wappa/processors/factory.py +5 -5
- wappa/schemas/factory.py +2 -5
- wappa/schemas/whatsapp/message_types/errors.py +3 -12
- wappa/schemas/whatsapp/validators.py +3 -3
- wappa/webhooks/__init__.py +17 -18
- wappa/webhooks/factory.py +3 -5
- wappa/webhooks/whatsapp/__init__.py +10 -13
- wappa/webhooks/whatsapp/message_types/audio.py +0 -4
- wappa/webhooks/whatsapp/message_types/document.py +1 -9
- wappa/webhooks/whatsapp/message_types/errors.py +3 -12
- wappa/webhooks/whatsapp/message_types/location.py +1 -21
- wappa/webhooks/whatsapp/message_types/sticker.py +1 -5
- wappa/webhooks/whatsapp/message_types/text.py +0 -6
- wappa/webhooks/whatsapp/message_types/video.py +1 -20
- wappa/webhooks/whatsapp/status_models.py +2 -2
- wappa/webhooks/whatsapp/validators.py +3 -3
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/METADATA +362 -8
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/RECORD +126 -126
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/WHEEL +0 -0
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/entry_points.txt +0 -0
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,24 +5,23 @@ Contains models for user profiles, message history, and interaction statistics.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from datetime import datetime
|
|
8
|
-
from typing import Dict, List, Optional
|
|
9
8
|
|
|
10
9
|
from pydantic import BaseModel, Field, field_validator
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class UserProfile(BaseModel):
|
|
14
13
|
"""User profile model for tracking user information and statistics."""
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
# Basic user information
|
|
17
16
|
phone_number: str
|
|
18
|
-
user_name:
|
|
19
|
-
profile_name:
|
|
20
|
-
|
|
17
|
+
user_name: str | None = None
|
|
18
|
+
profile_name: str | None = None
|
|
19
|
+
|
|
21
20
|
# Timestamps
|
|
22
21
|
first_seen: datetime = Field(default_factory=datetime.now)
|
|
23
22
|
last_seen: datetime = Field(default_factory=datetime.now)
|
|
24
|
-
last_message_timestamp:
|
|
25
|
-
|
|
23
|
+
last_message_timestamp: datetime | None = None
|
|
24
|
+
|
|
26
25
|
# Statistics
|
|
27
26
|
total_messages: int = 0
|
|
28
27
|
message_count: int = 0 # Alias for compatibility
|
|
@@ -31,29 +30,29 @@ class UserProfile(BaseModel):
|
|
|
31
30
|
interactive_messages: int = 0
|
|
32
31
|
location_messages: int = 0
|
|
33
32
|
contact_messages: int = 0
|
|
34
|
-
|
|
33
|
+
|
|
35
34
|
# Interaction history
|
|
36
35
|
total_interactions: int = 0
|
|
37
36
|
button_clicks: int = 0
|
|
38
37
|
list_selections: int = 0
|
|
39
|
-
|
|
38
|
+
|
|
40
39
|
# Special command usage
|
|
41
|
-
commands_used:
|
|
42
|
-
|
|
40
|
+
commands_used: dict[str, int] = Field(default_factory=dict)
|
|
41
|
+
|
|
43
42
|
# User preferences
|
|
44
43
|
preferred_language: str = "en"
|
|
45
|
-
timezone:
|
|
46
|
-
|
|
44
|
+
timezone: str | None = None
|
|
45
|
+
|
|
47
46
|
# Flags
|
|
48
47
|
is_first_time_user: bool = True
|
|
49
48
|
has_received_welcome: bool = False
|
|
50
|
-
|
|
51
|
-
@field_validator(
|
|
49
|
+
|
|
50
|
+
@field_validator("phone_number", mode="before")
|
|
52
51
|
@classmethod
|
|
53
52
|
def validate_phone_number(cls, v):
|
|
54
53
|
"""Convert phone number to string if it's an integer."""
|
|
55
54
|
return str(v) if v is not None else v
|
|
56
|
-
|
|
55
|
+
|
|
57
56
|
def increment_message_count(self, message_type: str = "text") -> None:
|
|
58
57
|
"""Increment the message count and update statistics."""
|
|
59
58
|
self.total_messages += 1
|
|
@@ -61,11 +60,18 @@ class UserProfile(BaseModel):
|
|
|
61
60
|
self.last_seen = datetime.now()
|
|
62
61
|
self.last_message_timestamp = datetime.now()
|
|
63
62
|
self.is_first_time_user = False
|
|
64
|
-
|
|
63
|
+
|
|
65
64
|
# Update message type counters
|
|
66
65
|
if message_type in ["text"]:
|
|
67
66
|
self.text_messages += 1
|
|
68
|
-
elif message_type in [
|
|
67
|
+
elif message_type in [
|
|
68
|
+
"image",
|
|
69
|
+
"video",
|
|
70
|
+
"audio",
|
|
71
|
+
"voice",
|
|
72
|
+
"document",
|
|
73
|
+
"sticker",
|
|
74
|
+
]:
|
|
69
75
|
self.media_messages += 1
|
|
70
76
|
elif message_type in ["interactive"]:
|
|
71
77
|
self.interactive_messages += 1
|
|
@@ -73,40 +79,42 @@ class UserProfile(BaseModel):
|
|
|
73
79
|
self.location_messages += 1
|
|
74
80
|
elif message_type in ["contact", "contacts"]:
|
|
75
81
|
self.contact_messages += 1
|
|
76
|
-
|
|
82
|
+
|
|
77
83
|
def increment_interactions(self, interaction_type: str = "general") -> None:
|
|
78
84
|
"""Increment total interactions and specific interaction counters."""
|
|
79
85
|
self.total_interactions += 1
|
|
80
86
|
self.last_seen = datetime.now()
|
|
81
|
-
|
|
87
|
+
|
|
82
88
|
if interaction_type == "button":
|
|
83
89
|
self.button_clicks += 1
|
|
84
90
|
elif interaction_type == "list":
|
|
85
91
|
self.list_selections += 1
|
|
86
|
-
|
|
92
|
+
|
|
87
93
|
def increment_command_usage(self, command: str) -> None:
|
|
88
94
|
"""Increment usage counter for a specific command."""
|
|
89
95
|
if command not in self.commands_used:
|
|
90
96
|
self.commands_used[command] = 0
|
|
91
97
|
self.commands_used[command] += 1
|
|
92
98
|
self.last_seen = datetime.now()
|
|
93
|
-
|
|
94
|
-
def update_profile_info(
|
|
99
|
+
|
|
100
|
+
def update_profile_info(
|
|
101
|
+
self, user_name: str | None = None, profile_name: str | None = None
|
|
102
|
+
) -> None:
|
|
95
103
|
"""Update user profile information if new data is available."""
|
|
96
104
|
if user_name and user_name != self.user_name:
|
|
97
105
|
self.user_name = user_name
|
|
98
|
-
|
|
106
|
+
|
|
99
107
|
if profile_name and profile_name != self.profile_name:
|
|
100
108
|
self.profile_name = profile_name
|
|
101
|
-
|
|
109
|
+
|
|
102
110
|
self.last_seen = datetime.now()
|
|
103
|
-
|
|
111
|
+
|
|
104
112
|
def mark_welcome_sent(self) -> None:
|
|
105
113
|
"""Mark that the welcome message has been sent to this user."""
|
|
106
114
|
self.has_received_welcome = True
|
|
107
115
|
self.is_first_time_user = False
|
|
108
116
|
self.last_seen = datetime.now()
|
|
109
|
-
|
|
117
|
+
|
|
110
118
|
def get_display_name(self) -> str:
|
|
111
119
|
"""Get the best available display name for this user."""
|
|
112
120
|
if self.user_name:
|
|
@@ -115,8 +123,8 @@ class UserProfile(BaseModel):
|
|
|
115
123
|
return self.profile_name
|
|
116
124
|
else:
|
|
117
125
|
return self.phone_number
|
|
118
|
-
|
|
119
|
-
def get_activity_summary(self) ->
|
|
126
|
+
|
|
127
|
+
def get_activity_summary(self) -> dict[str, any]:
|
|
120
128
|
"""Get a summary of user activity."""
|
|
121
129
|
return {
|
|
122
130
|
"user_id": self.phone_number,
|
|
@@ -127,75 +135,79 @@ class UserProfile(BaseModel):
|
|
|
127
135
|
"media": self.media_messages,
|
|
128
136
|
"interactive": self.interactive_messages,
|
|
129
137
|
"location": self.location_messages,
|
|
130
|
-
"contact": self.contact_messages
|
|
138
|
+
"contact": self.contact_messages,
|
|
131
139
|
},
|
|
132
140
|
"interactions": {
|
|
133
141
|
"total": self.total_interactions,
|
|
134
142
|
"buttons": self.button_clicks,
|
|
135
|
-
"lists": self.list_selections
|
|
143
|
+
"lists": self.list_selections,
|
|
136
144
|
},
|
|
137
145
|
"commands_used": self.commands_used,
|
|
138
146
|
"first_seen": self.first_seen.isoformat(),
|
|
139
147
|
"last_seen": self.last_seen.isoformat(),
|
|
140
148
|
"is_active_user": self.total_messages >= 5,
|
|
141
|
-
"is_new_user": self.is_first_time_user or self.total_messages <= 3
|
|
149
|
+
"is_new_user": self.is_first_time_user or self.total_messages <= 3,
|
|
142
150
|
}
|
|
143
151
|
|
|
144
152
|
|
|
145
153
|
class UserSession(BaseModel):
|
|
146
154
|
"""User session model for tracking current conversation context."""
|
|
147
|
-
|
|
155
|
+
|
|
148
156
|
user_id: str
|
|
149
|
-
session_id: str = Field(
|
|
150
|
-
|
|
157
|
+
session_id: str = Field(
|
|
158
|
+
default_factory=lambda: f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
159
|
+
)
|
|
160
|
+
|
|
151
161
|
# Session timestamps
|
|
152
162
|
session_start: datetime = Field(default_factory=datetime.now)
|
|
153
163
|
last_activity: datetime = Field(default_factory=datetime.now)
|
|
154
|
-
|
|
164
|
+
|
|
155
165
|
# Session statistics
|
|
156
166
|
messages_in_session: int = 0
|
|
157
|
-
commands_in_session:
|
|
167
|
+
commands_in_session: list[str] = Field(default_factory=list)
|
|
158
168
|
interactions_in_session: int = 0
|
|
159
|
-
|
|
169
|
+
|
|
160
170
|
# Current context
|
|
161
|
-
last_message_type:
|
|
162
|
-
last_command_used:
|
|
163
|
-
current_state:
|
|
164
|
-
|
|
171
|
+
last_message_type: str | None = None
|
|
172
|
+
last_command_used: str | None = None
|
|
173
|
+
current_state: str | None = None # For tracking active interactive states
|
|
174
|
+
|
|
165
175
|
# Session metadata
|
|
166
|
-
user_agent:
|
|
167
|
-
platform_version:
|
|
168
|
-
|
|
169
|
-
def update_activity(
|
|
176
|
+
user_agent: str | None = None
|
|
177
|
+
platform_version: str | None = None
|
|
178
|
+
|
|
179
|
+
def update_activity(
|
|
180
|
+
self, message_type: str = None, command: str = None, interaction: bool = False
|
|
181
|
+
) -> None:
|
|
170
182
|
"""Update session activity."""
|
|
171
183
|
self.last_activity = datetime.now()
|
|
172
184
|
self.messages_in_session += 1
|
|
173
|
-
|
|
185
|
+
|
|
174
186
|
if message_type:
|
|
175
187
|
self.last_message_type = message_type
|
|
176
|
-
|
|
188
|
+
|
|
177
189
|
if command:
|
|
178
190
|
self.last_command_used = command
|
|
179
191
|
self.commands_in_session.append(command)
|
|
180
|
-
|
|
192
|
+
|
|
181
193
|
if interaction:
|
|
182
194
|
self.interactions_in_session += 1
|
|
183
|
-
|
|
195
|
+
|
|
184
196
|
def set_current_state(self, state: str = None) -> None:
|
|
185
197
|
"""Set the current interactive state."""
|
|
186
198
|
self.current_state = state
|
|
187
199
|
self.last_activity = datetime.now()
|
|
188
|
-
|
|
200
|
+
|
|
189
201
|
def is_session_active(self, timeout_minutes: int = 30) -> bool:
|
|
190
202
|
"""Check if the session is still active based on last activity."""
|
|
191
203
|
time_diff = datetime.now() - self.last_activity
|
|
192
204
|
return time_diff.total_seconds() < (timeout_minutes * 60)
|
|
193
|
-
|
|
205
|
+
|
|
194
206
|
def get_session_duration_seconds(self) -> int:
|
|
195
207
|
"""Get the current session duration in seconds."""
|
|
196
208
|
return int((self.last_activity - self.session_start).total_seconds())
|
|
197
|
-
|
|
198
|
-
def get_session_summary(self) ->
|
|
209
|
+
|
|
210
|
+
def get_session_summary(self) -> dict[str, any]:
|
|
199
211
|
"""Get a summary of the current session."""
|
|
200
212
|
return {
|
|
201
213
|
"session_id": self.session_id,
|
|
@@ -209,64 +221,64 @@ class UserSession(BaseModel):
|
|
|
209
221
|
"current_state": self.current_state,
|
|
210
222
|
"is_active": self.is_session_active(),
|
|
211
223
|
"session_start": self.session_start.isoformat(),
|
|
212
|
-
"last_activity": self.last_activity.isoformat()
|
|
224
|
+
"last_activity": self.last_activity.isoformat(),
|
|
213
225
|
}
|
|
214
226
|
|
|
215
227
|
|
|
216
228
|
class UserStatistics(BaseModel):
|
|
217
229
|
"""Aggregate statistics for all users."""
|
|
218
|
-
|
|
230
|
+
|
|
219
231
|
total_users: int = 0
|
|
220
232
|
active_users_today: int = 0
|
|
221
233
|
active_users_week: int = 0
|
|
222
234
|
new_users_today: int = 0
|
|
223
|
-
|
|
235
|
+
|
|
224
236
|
total_messages: int = 0
|
|
225
237
|
messages_today: int = 0
|
|
226
|
-
|
|
238
|
+
|
|
227
239
|
# Message type distribution
|
|
228
|
-
message_type_stats:
|
|
229
|
-
|
|
240
|
+
message_type_stats: dict[str, int] = Field(default_factory=dict)
|
|
241
|
+
|
|
230
242
|
# Command usage statistics
|
|
231
|
-
command_usage_stats:
|
|
232
|
-
|
|
243
|
+
command_usage_stats: dict[str, int] = Field(default_factory=dict)
|
|
244
|
+
|
|
233
245
|
# Interactive feature usage
|
|
234
246
|
button_usage: int = 0
|
|
235
247
|
list_usage: int = 0
|
|
236
|
-
|
|
248
|
+
|
|
237
249
|
# Timestamps
|
|
238
250
|
last_updated: datetime = Field(default_factory=datetime.now)
|
|
239
|
-
|
|
251
|
+
|
|
240
252
|
def update_stats(self, user_profile: UserProfile) -> None:
|
|
241
253
|
"""Update statistics with data from a user profile."""
|
|
242
254
|
self.total_users += 1 if user_profile.is_first_time_user else 0
|
|
243
255
|
self.total_messages += user_profile.total_messages
|
|
244
|
-
|
|
256
|
+
|
|
245
257
|
# Update message type stats
|
|
246
258
|
for msg_type, count in {
|
|
247
259
|
"text": user_profile.text_messages,
|
|
248
260
|
"media": user_profile.media_messages,
|
|
249
261
|
"interactive": user_profile.interactive_messages,
|
|
250
262
|
"location": user_profile.location_messages,
|
|
251
|
-
"contact": user_profile.contact_messages
|
|
263
|
+
"contact": user_profile.contact_messages,
|
|
252
264
|
}.items():
|
|
253
265
|
if msg_type not in self.message_type_stats:
|
|
254
266
|
self.message_type_stats[msg_type] = 0
|
|
255
267
|
self.message_type_stats[msg_type] += count
|
|
256
|
-
|
|
268
|
+
|
|
257
269
|
# Update command usage stats
|
|
258
270
|
for command, count in user_profile.commands_used.items():
|
|
259
271
|
if command not in self.command_usage_stats:
|
|
260
272
|
self.command_usage_stats[command] = 0
|
|
261
273
|
self.command_usage_stats[command] += count
|
|
262
|
-
|
|
274
|
+
|
|
263
275
|
# Update interaction stats
|
|
264
276
|
self.button_usage += user_profile.button_clicks
|
|
265
277
|
self.list_usage += user_profile.list_selections
|
|
266
|
-
|
|
278
|
+
|
|
267
279
|
self.last_updated = datetime.now()
|
|
268
|
-
|
|
269
|
-
def get_summary(self) ->
|
|
280
|
+
|
|
281
|
+
def get_summary(self) -> dict[str, any]:
|
|
270
282
|
"""Get a comprehensive statistics summary."""
|
|
271
283
|
return {
|
|
272
284
|
"overview": {
|
|
@@ -274,14 +286,18 @@ class UserStatistics(BaseModel):
|
|
|
274
286
|
"active_users_today": self.active_users_today,
|
|
275
287
|
"new_users_today": self.new_users_today,
|
|
276
288
|
"total_messages": self.total_messages,
|
|
277
|
-
"messages_today": self.messages_today
|
|
289
|
+
"messages_today": self.messages_today,
|
|
278
290
|
},
|
|
279
291
|
"message_distribution": self.message_type_stats,
|
|
280
|
-
"popular_commands": dict(
|
|
292
|
+
"popular_commands": dict(
|
|
293
|
+
sorted(
|
|
294
|
+
self.command_usage_stats.items(), key=lambda x: x[1], reverse=True
|
|
295
|
+
)
|
|
296
|
+
),
|
|
281
297
|
"interactive_usage": {
|
|
282
298
|
"button_clicks": self.button_usage,
|
|
283
299
|
"list_selections": self.list_usage,
|
|
284
|
-
"total_interactions": self.button_usage + self.list_usage
|
|
300
|
+
"total_interactions": self.button_usage + self.list_usage,
|
|
285
301
|
},
|
|
286
|
-
"last_updated": self.last_updated.isoformat()
|
|
287
|
-
}
|
|
302
|
+
"last_updated": self.last_updated.isoformat(),
|
|
303
|
+
}
|