change(api): assist stats api moved to ee (#1598)
This commit is contained in:
parent
bd2bcd378b
commit
325d893db5
11 changed files with 149 additions and 140 deletions
|
|
@ -3,7 +3,6 @@ from apscheduler.triggers.interval import IntervalTrigger
|
||||||
|
|
||||||
from chalicelib.core import telemetry
|
from chalicelib.core import telemetry
|
||||||
from chalicelib.core import weekly_report, jobs, health
|
from chalicelib.core import weekly_report, jobs, health
|
||||||
from chalicelib.core import assist_stats
|
|
||||||
|
|
||||||
|
|
||||||
async def run_scheduled_jobs() -> None:
|
async def run_scheduled_jobs() -> None:
|
||||||
|
|
@ -26,10 +25,6 @@ async def weekly_health_cron() -> None:
|
||||||
health.weekly_cron()
|
health.weekly_cron()
|
||||||
|
|
||||||
|
|
||||||
async def assist_events_aggregates_cron() -> None:
|
|
||||||
assist_stats.insert_aggregated_data()
|
|
||||||
|
|
||||||
|
|
||||||
cron_jobs = [
|
cron_jobs = [
|
||||||
{"func": telemetry_cron, "trigger": CronTrigger(day_of_week="*"),
|
{"func": telemetry_cron, "trigger": CronTrigger(day_of_week="*"),
|
||||||
"misfire_grace_time": 60 * 60, "max_instances": 1},
|
"misfire_grace_time": 60 * 60, "max_instances": 1},
|
||||||
|
|
@ -40,7 +35,5 @@ cron_jobs = [
|
||||||
{"func": health_cron, "trigger": IntervalTrigger(hours=0, minutes=30, start_date="2023-04-01 0:0:0", jitter=300),
|
{"func": health_cron, "trigger": IntervalTrigger(hours=0, minutes=30, start_date="2023-04-01 0:0:0", jitter=300),
|
||||||
"misfire_grace_time": 60 * 60, "max_instances": 1},
|
"misfire_grace_time": 60 * 60, "max_instances": 1},
|
||||||
{"func": weekly_health_cron, "trigger": CronTrigger(day_of_week="sun", hour=5),
|
{"func": weekly_health_cron, "trigger": CronTrigger(day_of_week="sun", hour=5),
|
||||||
"misfire_grace_time": 60 * 60, "max_instances": 1},
|
"misfire_grace_time": 60 * 60, "max_instances": 1}
|
||||||
{"func": assist_events_aggregates_cron,
|
|
||||||
"trigger": IntervalTrigger(hours=1, start_date="2023-04-01 0:0:0", jitter=10), }
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from chalicelib.core import log_tool_rollbar, sourcemaps, events, sessions_assig
|
||||||
log_tool_stackdriver, reset_password, log_tool_cloudwatch, log_tool_sentry, log_tool_sumologic, log_tools, sessions, \
|
log_tool_stackdriver, reset_password, log_tool_cloudwatch, log_tool_sentry, log_tool_sumologic, log_tools, sessions, \
|
||||||
log_tool_newrelic, announcements, log_tool_bugsnag, weekly_report, integration_jira_cloud, integration_github, \
|
log_tool_newrelic, announcements, log_tool_bugsnag, weekly_report, integration_jira_cloud, integration_github, \
|
||||||
assist, mobile, tenants, boarding, notifications, webhook, users, \
|
assist, mobile, tenants, boarding, notifications, webhook, users, \
|
||||||
custom_metrics, saved_search, integrations_global, assist_stats
|
custom_metrics, saved_search, integrations_global
|
||||||
from chalicelib.core.collaboration_msteams import MSTeams
|
from chalicelib.core.collaboration_msteams import MSTeams
|
||||||
from chalicelib.core.collaboration_slack import Slack
|
from chalicelib.core.collaboration_slack import Slack
|
||||||
from or_dependencies import OR_context, OR_role
|
from or_dependencies import OR_context, OR_role
|
||||||
|
|
@ -860,59 +860,3 @@ async def check_recording_status(project_id: int):
|
||||||
def health_check():
|
def health_check():
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
@public_app.get('/{project_id}/assist-stats/avg', tags=["assist-stats"])
|
|
||||||
def get_assist_stats_avg(
|
|
||||||
project_id: int,
|
|
||||||
startTimestamp: int = None,
|
|
||||||
endTimestamp: int = None,
|
|
||||||
userId: str = None
|
|
||||||
):
|
|
||||||
return assist_stats.get_averages(
|
|
||||||
project_id=project_id,
|
|
||||||
start_timestamp=startTimestamp,
|
|
||||||
end_timestamp=endTimestamp,
|
|
||||||
user_id=userId)
|
|
||||||
|
|
||||||
|
|
||||||
@public_app.get(
|
|
||||||
'/{project_id}/assist-stats/top-members',
|
|
||||||
tags=["assist-stats"],
|
|
||||||
response_model=schemas.AssistStatsTopMembersResponse
|
|
||||||
)
|
|
||||||
def get_assist_stats_top_members(
|
|
||||||
project_id: int,
|
|
||||||
startTimestamp: int = None,
|
|
||||||
endTimestamp: int = None,
|
|
||||||
sort: Optional[str] = Query(default="sessionsAssisted",
|
|
||||||
description="Sort options: " + ", ".join(assist_stats.event_type_mapping)),
|
|
||||||
order: str = "desc",
|
|
||||||
userId: int = None,
|
|
||||||
page: int = 0,
|
|
||||||
limit: int = 5
|
|
||||||
):
|
|
||||||
return assist_stats.get_top_members(
|
|
||||||
project_id=project_id,
|
|
||||||
start_timestamp=startTimestamp,
|
|
||||||
end_timestamp=endTimestamp,
|
|
||||||
sort_by=sort,
|
|
||||||
sort_order=order,
|
|
||||||
user_id=userId,
|
|
||||||
page=page,
|
|
||||||
limit=limit
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@public_app.post(
|
|
||||||
'/{project_id}/assist-stats/sessions',
|
|
||||||
tags=["assist-stats"],
|
|
||||||
response_model=schemas.AssistStatsSessionsResponse
|
|
||||||
)
|
|
||||||
def get_assist_stats_sessions(
|
|
||||||
project_id: int,
|
|
||||||
data: schemas.AssistStatsSessionsRequest = Body(...),
|
|
||||||
):
|
|
||||||
return assist_stats.get_sessions(
|
|
||||||
project_id=project_id,
|
|
||||||
data=data
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -1581,75 +1581,3 @@ class ModuleStatus(BaseModel):
|
||||||
"offline-recordings", "alerts"] = Field(..., description="Possible values: notes, bugs, live")
|
"offline-recordings", "alerts"] = Field(..., description="Possible values: notes, bugs, live")
|
||||||
status: bool = Field(...)
|
status: bool = Field(...)
|
||||||
|
|
||||||
|
|
||||||
class AssistStatsAverage(BaseModel):
|
|
||||||
key: str = Field(...)
|
|
||||||
avg: float = Field(...)
|
|
||||||
chartData: List[dict] = Field(...)
|
|
||||||
|
|
||||||
|
|
||||||
class AssistStatsMember(BaseModel):
|
|
||||||
name: str
|
|
||||||
count: int
|
|
||||||
assist_duration: Optional[int] = Field(default=0)
|
|
||||||
call_duration: Optional[int] = Field(default=0)
|
|
||||||
control_duration: Optional[int] = Field(default=0)
|
|
||||||
assist_count: Optional[int] = Field(default=0)
|
|
||||||
|
|
||||||
|
|
||||||
class AssistStatsSessionAgent(BaseModel):
|
|
||||||
name: str
|
|
||||||
id: int
|
|
||||||
|
|
||||||
|
|
||||||
class AssistStatsTopMembersResponse(BaseModel):
|
|
||||||
total: int
|
|
||||||
list: List[AssistStatsMember]
|
|
||||||
|
|
||||||
|
|
||||||
class AssistStatsSessionRecording(BaseModel):
|
|
||||||
recordId: int = Field(...)
|
|
||||||
name: str = Field(...)
|
|
||||||
duration: int = Field(...)
|
|
||||||
|
|
||||||
|
|
||||||
class AssistStatsSession(BaseModel):
|
|
||||||
sessionId: str = Field(...)
|
|
||||||
timestamp: int = Field(...)
|
|
||||||
teamMembers: List[AssistStatsSessionAgent] = Field(...)
|
|
||||||
assistDuration: Optional[int] = Field(default=0)
|
|
||||||
callDuration: Optional[int] = Field(default=0)
|
|
||||||
controlDuration: Optional[int] = Field(default=0)
|
|
||||||
# recordings: list[AssistStatsSessionRecording] = Field(default=[])
|
|
||||||
|
|
||||||
|
|
||||||
assist_sort_options = ["timestamp", "assist_duration", "call_duration", "control_duration"]
|
|
||||||
|
|
||||||
|
|
||||||
class AssistStatsSessionsRequest(BaseModel):
|
|
||||||
startTimestamp: int = Field(...)
|
|
||||||
endTimestamp: int = Field(...)
|
|
||||||
limit: Optional[int] = Field(default=10)
|
|
||||||
page: Optional[int] = Field(default=1)
|
|
||||||
sort: Optional[str] = Field(default="timestamp",
|
|
||||||
enum=assist_sort_options)
|
|
||||||
order: Optional[str] = Field(default="desc", choices=["desc", "asc"])
|
|
||||||
userId: Optional[int] = Field(default=None)
|
|
||||||
|
|
||||||
@field_validator("sort")
|
|
||||||
def validate_sort(cls, v):
|
|
||||||
if v not in assist_sort_options:
|
|
||||||
raise ValueError(f"Invalid sort option. Allowed options: {', '.join(assist_sort_options)}")
|
|
||||||
return v
|
|
||||||
|
|
||||||
@field_validator("order")
|
|
||||||
def validate_order(cls, v):
|
|
||||||
if v not in ["desc", "asc"]:
|
|
||||||
raise ValueError("Invalid order option. Must be 'desc' or 'asc'.")
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
class AssistStatsSessionsResponse(BaseModel):
|
|
||||||
total: int = Field(...)
|
|
||||||
page: int = Field(...)
|
|
||||||
list: List[AssistStatsSession] = Field(default=[])
|
|
||||||
|
|
|
||||||
1
ee/api/.gitignore
vendored
1
ee/api/.gitignore
vendored
|
|
@ -251,7 +251,6 @@ Pipfile.lock
|
||||||
/routers/subs/__init__.py
|
/routers/subs/__init__.py
|
||||||
/routers/__init__.py
|
/routers/__init__.py
|
||||||
/chalicelib/core/assist.py
|
/chalicelib/core/assist.py
|
||||||
/chalicelib/core/assist_stats.py
|
|
||||||
/auth/__init__.py
|
/auth/__init__.py
|
||||||
/auth/auth_apikey.py
|
/auth/auth_apikey.py
|
||||||
/build.sh
|
/build.sh
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,6 @@ rm -rf ./crons/__init__.py
|
||||||
rm -rf ./routers/subs/__init__.py
|
rm -rf ./routers/subs/__init__.py
|
||||||
rm -rf ./routers/__init__.py
|
rm -rf ./routers/__init__.py
|
||||||
rm -rf ./chalicelib/core/assist.py
|
rm -rf ./chalicelib/core/assist.py
|
||||||
rm -rf ./chalicelib/core/assist_stats.py
|
|
||||||
rm -rf ./auth/__init__.py
|
rm -rf ./auth/__init__.py
|
||||||
rm -rf ./auth/auth_apikey.py
|
rm -rf ./auth/auth_apikey.py
|
||||||
rm -rf ./build.sh
|
rm -rf ./build.sh
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,19 @@
|
||||||
from apscheduler.triggers.interval import IntervalTrigger
|
from apscheduler.triggers.interval import IntervalTrigger
|
||||||
|
|
||||||
from chalicelib.utils import events_queue
|
from chalicelib.utils import events_queue
|
||||||
|
from chalicelib.core import assist_stats
|
||||||
|
|
||||||
|
|
||||||
async def pg_events_queue() -> None:
|
async def pg_events_queue() -> None:
|
||||||
events_queue.global_queue.force_flush()
|
events_queue.global_queue.force_flush()
|
||||||
|
|
||||||
|
|
||||||
|
async def assist_events_aggregates_cron() -> None:
|
||||||
|
assist_stats.insert_aggregated_data()
|
||||||
|
|
||||||
|
|
||||||
ee_cron_jobs = [
|
ee_cron_jobs = [
|
||||||
{"func": pg_events_queue, "trigger": IntervalTrigger(minutes=5), "misfire_grace_time": 20, "max_instances": 1},
|
{"func": pg_events_queue, "trigger": IntervalTrigger(minutes=5), "misfire_grace_time": 20, "max_instances": 1},
|
||||||
|
{"func": assist_events_aggregates_cron,
|
||||||
|
"trigger": IntervalTrigger(hours=1, start_date="2023-04-01 0:0:0", jitter=10), }
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from chalicelib.core import roles, traces, assist_records, sessions
|
from chalicelib.core import roles, traces, assist_records, sessions
|
||||||
from chalicelib.core import unlock, signals
|
from chalicelib.core import unlock, signals
|
||||||
from chalicelib.core import sessions_insights
|
from chalicelib.core import sessions_insights, assist_stats
|
||||||
from chalicelib.utils import assist_helper
|
from chalicelib.utils import assist_helper
|
||||||
|
|
||||||
unlock.check()
|
unlock.check()
|
||||||
|
|
@ -135,3 +135,60 @@ def send_interactions(projectId: int, data: schemas.SignalsSchema = Body(...),
|
||||||
def sessions_search(projectId: int, data: schemas.GetInsightsSchema = Body(...),
|
def sessions_search(projectId: int, data: schemas.GetInsightsSchema = Body(...),
|
||||||
context: schemas.CurrentContext = Depends(OR_context)):
|
context: schemas.CurrentContext = Depends(OR_context)):
|
||||||
return {'data': sessions_insights.fetch_selected(data=data, project_id=projectId)}
|
return {'data': sessions_insights.fetch_selected(data=data, project_id=projectId)}
|
||||||
|
|
||||||
|
|
||||||
|
@public_app.get('/{project_id}/assist-stats/avg', tags=["assist-stats"])
|
||||||
|
def get_assist_stats_avg(
|
||||||
|
project_id: int,
|
||||||
|
startTimestamp: int = None,
|
||||||
|
endTimestamp: int = None,
|
||||||
|
userId: str = None
|
||||||
|
):
|
||||||
|
return assist_stats.get_averages(
|
||||||
|
project_id=project_id,
|
||||||
|
start_timestamp=startTimestamp,
|
||||||
|
end_timestamp=endTimestamp,
|
||||||
|
user_id=userId)
|
||||||
|
|
||||||
|
|
||||||
|
@public_app.get(
|
||||||
|
'/{project_id}/assist-stats/top-members',
|
||||||
|
tags=["assist-stats"],
|
||||||
|
response_model=schemas.AssistStatsTopMembersResponse
|
||||||
|
)
|
||||||
|
def get_assist_stats_top_members(
|
||||||
|
project_id: int,
|
||||||
|
startTimestamp: int = None,
|
||||||
|
endTimestamp: int = None,
|
||||||
|
sort: Optional[str] = Query(default="sessionsAssisted",
|
||||||
|
description="Sort options: " + ", ".join(assist_stats.event_type_mapping)),
|
||||||
|
order: str = "desc",
|
||||||
|
userId: int = None,
|
||||||
|
page: int = 0,
|
||||||
|
limit: int = 5
|
||||||
|
):
|
||||||
|
return assist_stats.get_top_members(
|
||||||
|
project_id=project_id,
|
||||||
|
start_timestamp=startTimestamp,
|
||||||
|
end_timestamp=endTimestamp,
|
||||||
|
sort_by=sort,
|
||||||
|
sort_order=order,
|
||||||
|
user_id=userId,
|
||||||
|
page=page,
|
||||||
|
limit=limit
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@public_app.post(
|
||||||
|
'/{project_id}/assist-stats/sessions',
|
||||||
|
tags=["assist-stats"],
|
||||||
|
response_model=schemas.AssistStatsSessionsResponse
|
||||||
|
)
|
||||||
|
def get_assist_stats_sessions(
|
||||||
|
project_id: int,
|
||||||
|
data: schemas.AssistStatsSessionsRequest = Body(...),
|
||||||
|
):
|
||||||
|
return assist_stats.get_sessions(
|
||||||
|
project_id=project_id,
|
||||||
|
data=data
|
||||||
|
)
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
from .schemas import *
|
from .schemas import *
|
||||||
from .schemas_ee import *
|
from .schemas_ee import *
|
||||||
|
from .assist_stats_schema import *
|
||||||
from . import overrides as _overrides
|
from . import overrides as _overrides
|
||||||
|
|
|
||||||
78
ee/api/schemas/assist_stats_schema.py
Normal file
78
ee/api/schemas/assist_stats_schema.py
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from pydantic import Field, field_validator
|
||||||
|
|
||||||
|
from .overrides import BaseModel, Enum, ORUnion
|
||||||
|
|
||||||
|
|
||||||
|
class AssistStatsAverage(BaseModel):
|
||||||
|
key: str = Field(...)
|
||||||
|
avg: float = Field(...)
|
||||||
|
chartData: List[dict] = Field(...)
|
||||||
|
|
||||||
|
|
||||||
|
class AssistStatsMember(BaseModel):
|
||||||
|
name: str
|
||||||
|
count: int
|
||||||
|
assist_duration: Optional[int] = Field(default=0)
|
||||||
|
call_duration: Optional[int] = Field(default=0)
|
||||||
|
control_duration: Optional[int] = Field(default=0)
|
||||||
|
assist_count: Optional[int] = Field(default=0)
|
||||||
|
|
||||||
|
|
||||||
|
class AssistStatsSessionAgent(BaseModel):
|
||||||
|
name: str
|
||||||
|
id: int
|
||||||
|
|
||||||
|
|
||||||
|
class AssistStatsTopMembersResponse(BaseModel):
|
||||||
|
total: int
|
||||||
|
list: List[AssistStatsMember]
|
||||||
|
|
||||||
|
|
||||||
|
class AssistStatsSessionRecording(BaseModel):
|
||||||
|
recordId: int = Field(...)
|
||||||
|
name: str = Field(...)
|
||||||
|
duration: int = Field(...)
|
||||||
|
|
||||||
|
|
||||||
|
class AssistStatsSession(BaseModel):
|
||||||
|
sessionId: str = Field(...)
|
||||||
|
timestamp: int = Field(...)
|
||||||
|
teamMembers: List[AssistStatsSessionAgent] = Field(...)
|
||||||
|
assistDuration: Optional[int] = Field(default=0)
|
||||||
|
callDuration: Optional[int] = Field(default=0)
|
||||||
|
controlDuration: Optional[int] = Field(default=0)
|
||||||
|
# recordings: list[AssistStatsSessionRecording] = Field(default=[])
|
||||||
|
|
||||||
|
|
||||||
|
assist_sort_options = ["timestamp", "assist_duration", "call_duration", "control_duration"]
|
||||||
|
|
||||||
|
|
||||||
|
class AssistStatsSessionsRequest(BaseModel):
|
||||||
|
startTimestamp: int = Field(...)
|
||||||
|
endTimestamp: int = Field(...)
|
||||||
|
limit: Optional[int] = Field(default=10)
|
||||||
|
page: Optional[int] = Field(default=1)
|
||||||
|
sort: Optional[str] = Field(default="timestamp",
|
||||||
|
enum=assist_sort_options)
|
||||||
|
order: Optional[str] = Field(default="desc", choices=["desc", "asc"])
|
||||||
|
userId: Optional[int] = Field(default=None)
|
||||||
|
|
||||||
|
@field_validator("sort")
|
||||||
|
def validate_sort(cls, v):
|
||||||
|
if v not in assist_sort_options:
|
||||||
|
raise ValueError(f"Invalid sort option. Allowed options: {', '.join(assist_sort_options)}")
|
||||||
|
return v
|
||||||
|
|
||||||
|
@field_validator("order")
|
||||||
|
def validate_order(cls, v):
|
||||||
|
if v not in ["desc", "asc"]:
|
||||||
|
raise ValueError("Invalid order option. Must be 'desc' or 'asc'.")
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
class AssistStatsSessionsResponse(BaseModel):
|
||||||
|
total: int = Field(...)
|
||||||
|
page: int = Field(...)
|
||||||
|
list: List[AssistStatsSession] = Field(default=[])
|
||||||
|
|
@ -156,3 +156,6 @@ class CardInsights(schemas.CardInsights):
|
||||||
|
|
||||||
|
|
||||||
CardSchema = ORUnion(Union[schemas.__cards_union_base, CardInsights], discriminator='metric_type')
|
CardSchema = ORUnion(Union[schemas.__cards_union_base, CardInsights], discriminator='metric_type')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue