def stats(self, request: request.Request) -> response.Response: filter = Filter(request=request) team = request.user.team assert team is not None date_from, date_to = parse_timestamps(filter) prop_filters, prop_filter_params = parse_prop_clauses( filter.properties, team.pk) result = sync_execute( GET_ELEMENTS.format(date_from=date_from, date_to=date_to, query=prop_filters), { "team_id": team.id, **prop_filter_params }, ) return response.Response([{ "count": elements[1], "hash": None, "elements": [ ElementSerializer(element).data for element in chain_to_elements(elements[0]) ], } for elements in result])
def test_broken_class_names(self): elements = chain_to_elements("a........small") self.assertEqual(elements[0].tag_name, "a") self.assertEqual(elements[0].attr_class, ["small"]) elements_string = elements_to_string(elements=[ Element(tag_name="a", href="/a-url", attr_class=['small"', "xy:z"], attributes={"attr_class": 'xyz small"'}) ]) elements = chain_to_elements(elements_string) self.assertEqual(elements[0].tag_name, "a") self.assertEqual(elements[0].href, "/a-url") self.assertEqual(elements[0].attr_class, ["small", "xy:z"])
def stats(self, request: request.Request, **kwargs) -> response.Response: filter = Filter(request=request, team=self.team) date_from, date_to, date_params = parse_timestamps( filter, team_id=self.team.pk) prop_filters, prop_filter_params = parse_prop_grouped_clauses( team_id=self.team.pk, property_group=filter.property_groups) result = sync_execute( GET_ELEMENTS.format(date_from=date_from, date_to=date_to, query=prop_filters), { "team_id": self.team.pk, **prop_filter_params, **date_params }, ) return response.Response([{ "count": elements[1], "hash": None, "elements": [ ElementSerializer(element).data for element in chain_to_elements(elements[0]) ], } for elements in result])
def test_elements_to_string(self) -> None: self.maxDiff = None elements_string = elements_to_string(elements=[ Element( tag_name="a", href="/a-url", attr_class=["small"], text="bla bla", attributes={ "prop": "value", "number": 33, "data-attr": 'something " that; could mess up', "style": "min-height: 100vh;", }, nth_child=1, nth_of_type=0, ), Element(tag_name="button", attr_class=["btn", "btn-primary"], nth_child=0, nth_of_type=0), Element(tag_name="div", nth_child=0, nth_of_type=0), Element( tag_name="div", nth_child=0, nth_of_type=0, attr_id="nested", ), ], ) self.assertEqual( elements_string, ";".join([ r'a.small:data-attr="something \" that; could mess up"href="/a-url"nth-child="1"nth-of-type="0"number="33"prop="value"style="min-height: 100vh;"text="bla bla"', 'button.btn.btn-primary:nth-child="0"nth-of-type="0"', 'div:nth-child="0"nth-of-type="0"', 'div:attr_id="nested"nth-child="0"nth-of-type="0"', ]), ) elements = chain_to_elements(elements_string) self.assertEqual(elements[0].tag_name, "a") self.assertEqual(elements[0].href, "/a-url") self.assertEqual(elements[0].attr_class, ["small"]) self.assertDictEqual( elements[0].attributes, { "prop": "value", "number": "33", "data-attr": r"something \" that; could mess up", "style": "min-height: 100vh;", }, ) self.assertEqual(elements[0].nth_child, 1) self.assertEqual(elements[0].nth_of_type, 0) self.assertEqual(elements[1].attr_class, ["btn", "btn-primary"]) self.assertEqual(elements[3].attr_id, "nested")
def post_event_to_webhook_ee(self: Task, event: Dict[str, Any], team_id: int, site_url: str) -> None: team = Team.objects.get(pk=team_id) elements_list = chain_to_elements(event.get("elements_chain", "")) _event = Event.objects.create(event=event["event"], distinct_id=event["distinct_id"], properties=event["properties"], team=team, site_url=site_url, **({ "timestamp": event["timestamp"] } if event["timestamp"] else {}), **({ "elements": elements_list })) actions = cast( Sequence[Action], Action.objects.filter(team_id=team_id, post_to_slack=True).all()) if not site_url: site_url = settings.SITE_URL for action in actions: qs = Event.objects.filter(pk=_event.pk).query_db_by_action(action) if not qs: continue # REST hooks action.on_perform(_event) # webhooks if not team.slack_incoming_webhook: continue message_text, message_markdown = get_formatted_message( action, _event, site_url, ) if determine_webhook_type(team) == "slack": message = { "text": message_text, "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": message_markdown }, }, ], } else: message = { "text": message_markdown, } requests.post(team.slack_incoming_webhook, verify=False, json=message) _event.delete()
def post_event_to_webhook_ee(self: Task, event: Dict[str, Any], team_id: int, site_url: str) -> None: if not site_url: site_url = settings.SITE_URL timer = statsd.Timer("%s_posthog_cloud" % (settings.STATSD_PREFIX,)) timer.start() team = Team.objects.select_related("organization").get(pk=team_id) elements_list = chain_to_elements(event.get("elements_chain", "")) ephemeral_postgres_event = Event.objects.create( event=event["event"], distinct_id=event["distinct_id"], properties=event["properties"], team=team, site_url=site_url, **({"timestamp": event["timestamp"]} if event["timestamp"] else {}), **({"elements": elements_list}) ) try: is_zapier_available = team.organization.is_feature_available("zapier") actionFilters = {"team_id": team_id} if not is_zapier_available: if not team.slack_incoming_webhook: return # Exit this task if neither Zapier nor webhook URL are available else: actionFilters["post_to_slack"] = True # We only need to fire for actions that are posted to webhook URL for action in cast(Sequence[Action], Action.objects.filter(**actionFilters).all()): qs = Event.objects.filter(pk=ephemeral_postgres_event.pk).query_db_by_action(action) if not qs: continue # REST hooks if is_zapier_available: action.on_perform(ephemeral_postgres_event) # webhooks if team.slack_incoming_webhook and action.post_to_slack: message_text, message_markdown = get_formatted_message(action, ephemeral_postgres_event, site_url) if determine_webhook_type(team) == "slack": message = { "text": message_text, "blocks": [{"type": "section", "text": {"type": "mrkdwn", "text": message_markdown}}], } else: message = { "text": message_markdown, } statsd.Counter("%s_posthog_cloud_hooks_web_fired" % (settings.STATSD_PREFIX)).increment() requests.post(team.slack_incoming_webhook, verify=False, json=message) except: raise finally: timer.stop("hooks_processed_for_event") ephemeral_postgres_event.delete()
def serialize_event_with_property(self, event: str) -> EventDefinition: """ Format the event name for display. """ if not self.support_autocapture_elements(): return EventDefinition(event=event, properties={}, elements=[]) event_name, property_name, property_value = event.split("::") if event_name == AUTOCAPTURE_EVENT and property_name == "elements_chain": event_type, elements_chain = property_value.split(self.ELEMENTS_DIVIDER) return EventDefinition( event=event, properties={self.AUTOCAPTURE_EVENT_TYPE: event_type}, elements=cast(list, ElementSerializer(chain_to_elements(elements_chain), many=True).data), ) return EventDefinition(event=event, properties={}, elements=[])
def get_elements(self, event): if not event[6]: return [] return ElementSerializer(chain_to_elements(event[6]), many=True).data
def get_elements(event_id: Union[int, UUID]) -> List[Element]: return chain_to_elements( sync_execute("select elements_chain from events where uuid = %(id)s", {"id": event_id})[0][0])