Spaces:
Build error
Build error
Validify-testbot-1
/
botbuilder-python
/libraries
/botbuilder-testing
/botbuilder
/testing
/dialog_test_logger.py
# Copyright (c) Microsoft Corporation. All rights reserved. | |
# Licensed under the MIT License. | |
import json | |
import logging | |
import time | |
import uuid | |
from datetime import datetime | |
from typing import Awaitable, Callable, List | |
from botbuilder.core import Middleware, TurnContext | |
from botbuilder.schema import Activity, ActivityTypes, ResourceResponse | |
class DialogTestLogger(Middleware): | |
""" | |
A middleware to output incoming and outgoing activities as json strings to the console during | |
unit tests. | |
""" | |
def __init__( | |
self, | |
log_func: Callable[..., None] = None, | |
json_indent: int = 4, | |
time_func: Callable[[], float] = None, | |
): | |
""" | |
Initialize a new instance of the dialog test logger. | |
:param log_func: A callable method or object that can log a message, | |
default to `logging.getLogger(__name__).info`. | |
:type log_func: Callable[..., None] | |
:param json_indent: An indent for json output, default indent is 4. | |
:type json_indent: int | |
:param time_func: A time function to record time spans, default to `time.monotonic`. | |
:type time_func: Callable[[], float] | |
""" | |
self._log = logging.getLogger(__name__).info if log_func is None else log_func | |
self._stopwatch_state_key = f"stopwatch.{uuid.uuid4()}" | |
self._json_indent = json_indent | |
self._time_func = time.monotonic if time_func is None else time_func | |
async def on_turn( | |
self, context: TurnContext, logic: Callable[[TurnContext], Awaitable] | |
): | |
context.turn_state[self._stopwatch_state_key] = self._time_func() | |
await self._log_incoming_activity(context, context.activity) | |
context.on_send_activities(self._send_activities_handler) | |
await logic() | |
async def _log_incoming_activity( | |
self, context: TurnContext, activity: Activity | |
) -> None: | |
self._log("") | |
if context.activity.type == ActivityTypes.message: | |
self._log("User: Text = %s", context.activity.text) | |
else: | |
self._log_activity_as_json(actor="User", activity=activity) | |
timestamp = self._get_timestamp() | |
self._log("-> ts: %s", timestamp) | |
async def _send_activities_handler( | |
self, | |
context: TurnContext, | |
activities: List[Activity], | |
next_send: Callable[[], Awaitable[None]], | |
) -> List[ResourceResponse]: | |
for activity in activities: | |
await self._log_outgoing_activity(context, activity) | |
responses = await next_send() | |
return responses | |
async def _log_outgoing_activity( | |
self, context: TurnContext, activity: Activity | |
) -> None: | |
self._log("") | |
start_time = context.turn_state[self._stopwatch_state_key] | |
if activity.type == ActivityTypes.message: | |
message = ( | |
f"Bot: Text = {activity.text}\r\n" | |
f" Speak = {activity.speak}\r\n" | |
f" InputHint = {activity.input_hint}" | |
) | |
self._log(message) | |
else: | |
self._log_activity_as_json(actor="Bot", activity=activity) | |
now = self._time_func() | |
mms = int(round((now - start_time) * 1000)) | |
timestamp = self._get_timestamp() | |
self._log("-> ts: %s elapsed %d ms", timestamp, mms) | |
def _log_activity_as_json(self, actor: str, activity: Activity) -> None: | |
activity_dict = activity.serialize() | |
activity_json = json.dumps(activity_dict, indent=self._json_indent) | |
message = f"{actor}: Activity = {activity.type}\r\n" f"{activity_json}" | |
self._log(message) | |
def _get_timestamp() -> str: | |
timestamp = datetime.now().strftime("%H:%M:%S") | |
return timestamp | |