Merge branch 'dev' into api-pa-events-metrics

This commit is contained in:
Kraiem Taha Yassine 2025-01-23 12:21:12 +01:00 committed by GitHub
commit 1506630dda
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 88 additions and 45 deletions

View file

@ -209,14 +209,15 @@ def get_issues(project: schemas.ProjectContext, user_id: int, data: schemas.Card
def __get_global_card_info(data: schemas.CardSchema):
r = {"hideExcess": data.hide_excess, "compareTo": data.compare_to}
r = {"hideExcess": data.hide_excess, "compareTo": data.compare_to, "rows": data.rows}
return r
def __get_path_analysis_card_info(data: schemas.CardPathAnalysis):
r = {"start_point": [s.model_dump() for s in data.start_point],
"start_type": data.start_type,
"excludes": [e.model_dump() for e in data.excludes]}
"excludes": [e.model_dump() for e in data.excludes],
"rows": data.rows}
return r

View file

@ -12,33 +12,33 @@ logger = logging.getLogger(__name__)
def __transform_journey(rows, reverse_path=False):
total_100p = 0
number_of_step1 = 0
for r in rows:
if r["event_number_in_session"] > 1:
break
number_of_step1 += 1
total_100p += r["sessions_count"]
for i in range(len(rows)):
rows[i]["value"] = rows[i]["sessions_count"] * 100 / total_100p
nodes = []
nodes_values = []
links = []
drops = []
max_depth = 0
for r in rows:
source = f"{r['event_number_in_session']}_{r['event_type']}_{r['e_value']}"
r["value"] = r["sessions_count"] * 100 / total_100p
source = f"{r['event_number_in_session'] - 1}_{r['event_type']}_{r['e_value']}"
if source not in nodes:
nodes.append(source)
nodes_values.append({"depth": r['event_number_in_session'] - 1,
"name": r['e_value'],
"eventType": r['event_type']})
# if r['next_value']:
target = f"{r['event_number_in_session'] + 1}_{r['next_type']}_{r['next_value']}"
"eventType": r['event_type'],
"id": len(nodes_values)})
target = f"{r['event_number_in_session']}_{r['next_type']}_{r['next_value']}"
if target not in nodes:
nodes.append(target)
nodes_values.append({"depth": r['event_number_in_session'],
"name": r['next_value'],
"eventType": r['next_type']})
"eventType": r['next_type'],
"id": len(nodes_values)})
sr_idx = nodes.index(source)
tg_idx = nodes.index(target)
@ -52,6 +52,43 @@ def __transform_journey(rows, reverse_path=False):
link["target"] = sr_idx
links.append(link)
max_depth = r['event_number_in_session']
if r["next_type"] == "DROP":
for d in drops:
if d["depth"] == r['event_number_in_session']:
d["sessions_count"] += r["sessions_count"]
break
else:
drops.append({"depth": r['event_number_in_session'], "sessions_count": r["sessions_count"]})
for i in range(len(drops)):
if drops[i]["depth"] < max_depth:
source = f"{drops[i]['depth']}_DROP_None"
target = f"{drops[i]['depth'] + 1}_DROP_None"
sr_idx = nodes.index(source)
if i < len(drops) - 1 and drops[i]["depth"] + 1 == drops[i + 1]["depth"]:
tg_idx = nodes.index(target)
else:
nodes.append(target)
nodes_values.append({"depth": drops[i]["depth"] + 1,
"name": None,
"eventType": "DROP",
"id": len(nodes_values)})
tg_idx = len(nodes) - 1
link = {"eventType": "DROP",
"sessionsCount": drops[i]["sessions_count"],
"value": drops[i]["sessions_count"] * 100 / total_100p}
if not reverse_path:
link["source"] = sr_idx
link["target"] = tg_idx
else:
link["source"] = tg_idx
link["target"] = sr_idx
links.append(link)
return {"nodes": nodes_values,
"links": sorted(links, key=lambda x: (x["source"], x["target"]), reverse=False)}

View file

@ -23,6 +23,7 @@ JOURNEY_TYPES = {
}
# startPoints are computed before ranked_events to reduce the number of window functions over rows
# compute avg_time_from_previous at the same level as sessions_count (this was removed in v1.22)
# if start-point is selected, the selected event is ranked n°1

View file

@ -133,7 +133,7 @@ class _TimedSchema(BaseModel):
class NotificationsViewSchema(_TimedSchema):
ids: List[int] = Field(default=[])
ids: List[int] = Field(default_factory=list)
startTimestamp: Optional[int] = Field(default=None)
endTimestamp: Optional[int] = Field(default=None)
@ -545,7 +545,7 @@ class SessionSearchEventSchema2(BaseModel):
operator: Union[SearchEventOperator, ClickEventExtraOperator] = Field(...)
source: Optional[List[Union[ErrorSource, int, str]]] = Field(default=None)
sourceOperator: Optional[MathOperator] = Field(default=None)
filters: Optional[List[RequestGraphqlFilterSchema]] = Field(default=[])
filters: Optional[List[RequestGraphqlFilterSchema]] = Field(default_factory=list)
_remove_duplicate_values = field_validator('value', mode='before')(remove_duplicate_values)
_single_to_list_values = field_validator('value', mode='before')(single_to_list)
@ -579,7 +579,7 @@ class SessionSearchEventSchema2(BaseModel):
class SessionSearchFilterSchema(BaseModel):
is_event: Literal[False] = False
value: List[Union[IssueType, PlatformType, int, str]] = Field(default=[])
value: List[Union[IssueType, PlatformType, int, str]] = Field(default_factory=list)
type: FilterType = Field(...)
operator: Union[SearchEventOperator, MathOperator] = Field(...)
source: Optional[Union[ErrorSource, str]] = Field(default=None)
@ -658,8 +658,8 @@ Field(discriminator='is_event'), BeforeValidator(add_missing_is_event)]
class SessionsSearchPayloadSchema(_TimedSchema, _PaginatedSchema):
events: List[SessionSearchEventSchema2] = Field(default=[], doc_hidden=True)
filters: List[GroupedFilterType] = Field(default=[])
events: List[SessionSearchEventSchema2] = Field(default_factory=list, doc_hidden=True)
filters: List[GroupedFilterType] = Field(default_factory=list)
sort: str = Field(default="startTs")
order: SortOrderType = Field(default=SortOrderType.DESC)
events_order: Optional[SearchEventOrder] = Field(default=SearchEventOrder.THEN)
@ -816,7 +816,7 @@ Field(discriminator='is_event')]
class PathAnalysisSchema(_TimedSchema, _PaginatedSchema):
density: int = Field(default=7)
filters: List[ProductAnalyticsFilter] = Field(default=[])
filters: List[ProductAnalyticsFilter] = Field(default_factory=list)
type: Optional[str] = Field(default=None)
_transform_filters = field_validator('filters', mode='before') \
@ -928,10 +928,10 @@ class CardSessionsSchema(_TimedSchema, _PaginatedSchema):
startTimestamp: int = Field(default=TimeUTC.now(-7))
endTimestamp: int = Field(default=TimeUTC.now())
density: int = Field(default=7, ge=1, le=200)
series: List[CardSeriesSchema] = Field(default=[])
series: List[CardSeriesSchema] = Field(default_factory=list)
# events: List[SessionSearchEventSchema2] = Field(default=[], doc_hidden=True)
filters: List[GroupedFilterType] = Field(default=[])
# events: List[SessionSearchEventSchema2] = Field(default_factory=list, doc_hidden=True)
filters: List[GroupedFilterType] = Field(default_factory=list)
compare_to: Optional[List[str]] = Field(default=None)
@ -1037,9 +1037,11 @@ class __CardSchema(CardSessionsSchema):
view_type: Any
metric_type: MetricType = Field(...)
metric_of: Any
metric_value: List[IssueType] = Field(default=[])
metric_value: List[IssueType] = Field(default_factory=list)
# This is used to save the selected session for heatmaps
session_id: Optional[int] = Field(default=None)
# This is used to specify the number of top values for PathAnalysis
rows: int = Field(default=3, ge=1, le=10)
@computed_field
@property
@ -1185,14 +1187,15 @@ class CardPathAnalysis(__CardSchema):
metric_type: Literal[MetricType.PATH_ANALYSIS]
metric_of: MetricOfPathAnalysis = Field(default=MetricOfPathAnalysis.session_count)
view_type: MetricOtherViewType = Field(...)
metric_value: List[ProductAnalyticsSelectedEventType] = Field(default=[])
metric_value: List[ProductAnalyticsSelectedEventType] = Field(default_factory=list)
density: int = Field(default=4, ge=2, le=10)
rows: int = Field(default=3, ge=1, le=10)
start_type: Literal["start", "end"] = Field(default="start")
start_point: List[PathAnalysisSubFilterSchema] = Field(default=[])
excludes: List[PathAnalysisSubFilterSchema] = Field(default=[])
start_point: List[PathAnalysisSubFilterSchema] = Field(default_factory=list)
excludes: List[PathAnalysisSubFilterSchema] = Field(default_factory=list)
series: List[CardPathAnalysisSeriesSchema] = Field(default=[])
series: List[CardPathAnalysisSeriesSchema] = Field(default_factory=list)
@model_validator(mode="before")
@classmethod
@ -1258,13 +1261,13 @@ class ProjectConditions(BaseModel):
condition_id: Optional[int] = Field(default=None)
name: str = Field(...)
capture_rate: int = Field(..., ge=0, le=100)
filters: List[GroupedFilterType] = Field(default=[])
filters: List[GroupedFilterType] = Field(default_factory=list)
class ProjectSettings(BaseModel):
rate: int = Field(..., ge=0, le=100)
conditional_capture: bool = Field(default=False)
conditions: List[ProjectConditions] = Field(default=[])
conditions: List[ProjectConditions] = Field(default_factory=list)
class CreateDashboardSchema(BaseModel):
@ -1272,7 +1275,7 @@ class CreateDashboardSchema(BaseModel):
description: Optional[str] = Field(default='')
is_public: bool = Field(default=False)
is_pinned: bool = Field(default=False)
metrics: Optional[List[int]] = Field(default=[])
metrics: Optional[List[int]] = Field(default_factory=list)
class EditDashboardSchema(CreateDashboardSchema):
@ -1281,7 +1284,7 @@ class EditDashboardSchema(CreateDashboardSchema):
class UpdateWidgetPayloadSchema(BaseModel):
config: dict = Field(default={})
config: dict = Field(default_factory=dict)
class AddWidgetToDashboardPayloadSchema(UpdateWidgetPayloadSchema):
@ -1379,7 +1382,7 @@ class IntegrationType(str, Enum):
class SearchNoteSchema(_PaginatedSchema):
sort: str = Field(default="createdAt")
order: SortOrderType = Field(default=SortOrderType.DESC)
tags: Optional[List[str]] = Field(default=[])
tags: Optional[List[str]] = Field(default_factory=list)
shared_only: bool = Field(default=False)
mine_only: bool = Field(default=False)
search: Optional[str] = Field(default=None)
@ -1426,8 +1429,8 @@ class _HeatMapSearchEventRaw(SessionSearchEventSchema2):
class HeatMapSessionsSearch(SessionsSearchPayloadSchema):
events: Optional[List[_HeatMapSearchEventRaw]] = Field(default=[])
filters: List[Union[SessionSearchFilterSchema, _HeatMapSearchEventRaw]] = Field(default=[])
events: Optional[List[_HeatMapSearchEventRaw]] = Field(default_factory=list)
filters: List[Union[SessionSearchFilterSchema, _HeatMapSearchEventRaw]] = Field(default_factory=list)
@model_validator(mode="before")
@classmethod
@ -1442,14 +1445,14 @@ class HeatMapSessionsSearch(SessionsSearchPayloadSchema):
class HeatMapFilterSchema(BaseModel):
value: List[Literal[IssueType.CLICK_RAGE, IssueType.DEAD_CLICK]] = Field(default=[])
value: List[Literal[IssueType.CLICK_RAGE, IssueType.DEAD_CLICK]] = Field(default_factory=list)
type: Literal[FilterType.ISSUE] = Field(...)
operator: Literal[SearchEventOperator.IS, MathOperator.EQUAL] = Field(...)
class GetHeatMapPayloadSchema(_TimedSchema):
url: Optional[str] = Field(default=None)
filters: List[HeatMapFilterSchema] = Field(default=[])
filters: List[HeatMapFilterSchema] = Field(default_factory=list)
click_rage: bool = Field(default=False)
operator: Literal[SearchEventOperator.IS, SearchEventOperator.STARTS_WITH,
SearchEventOperator.CONTAINS, SearchEventOperator.ENDS_WITH] = Field(default=SearchEventOperator.STARTS_WITH)
@ -1470,7 +1473,7 @@ class FeatureFlagVariant(BaseModel):
class FeatureFlagConditionFilterSchema(BaseModel):
is_event: Literal[False] = False
type: FilterType = Field(...)
value: List[str] = Field(default=[], min_length=1)
value: List[str] = Field(default_factory=list, min_length=1)
operator: Union[SearchEventOperator, MathOperator] = Field(...)
source: Optional[str] = Field(default=None)
sourceOperator: Optional[Union[SearchEventOperator, MathOperator]] = Field(default=None)
@ -1486,7 +1489,7 @@ class FeatureFlagCondition(BaseModel):
condition_id: Optional[int] = Field(default=None)
name: str = Field(...)
rollout_percentage: Optional[int] = Field(default=0)
filters: List[FeatureFlagConditionFilterSchema] = Field(default=[])
filters: List[FeatureFlagConditionFilterSchema] = Field(default_factory=list)
class SearchFlagsSchema(_PaginatedSchema):
@ -1513,8 +1516,8 @@ class FeatureFlagSchema(BaseModel):
flag_type: FeatureFlagType = Field(default=FeatureFlagType.SINGLE_VARIANT)
is_persist: Optional[bool] = Field(default=False)
is_active: Optional[bool] = Field(default=True)
conditions: List[FeatureFlagCondition] = Field(default=[], min_length=1)
variants: List[FeatureFlagVariant] = Field(default=[])
conditions: List[FeatureFlagCondition] = Field(default_factory=list, min_length=1)
variants: List[FeatureFlagVariant] = Field(default_factory=list)
class ModuleType(str, Enum):

View file

@ -9,10 +9,11 @@ export class Conditions {
constructor(data?: Record<string, any>, isConditional?: boolean, isMobile?: boolean) {
makeAutoObservable(this);
console.log('data', data)
this.name = data?.name;
if (data && (data.rolloutPercentage || data.captureRate)) {
this.rolloutPercentage = data.rolloutPercentage ?? data.captureRate;
this.filter = new Filter(isConditional, isMobile).fromJson(data);
this.filter = new Filter([], isConditional, isMobile).fromJson(data);
}
}
@ -213,4 +214,4 @@ export default class FeatureFlag {
this.isActive = isEnabled;
this.setHasChanged(true)
}
}
}

View file

@ -85,12 +85,12 @@ export default class FilterItem {
if (this.isConditional) {
if (this.isMobile) {
_filter =
mobileConditionalFiltersMap[json.type] ||
mobileConditionalFiltersMap[json.source];
mobileConditionalFiltersMap[_filter.key] ||
mobileConditionalFiltersMap[_filter.source];
} else {
_filter =
conditionalFiltersMap[json.type] ||
conditionalFiltersMap[json.source];
conditionalFiltersMap[_filter.key] ||
conditionalFiltersMap[_filter.source];
}
}
if (mainFilterKey) {