databricks-tellr-app 0.3.11.dev6__tar.gz → 0.3.11.dev8__tar.gz

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.
Files changed (148) hide show
  1. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/PKG-INFO +1 -1
  2. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/databricks_tellr_app.egg-info/PKG-INFO +1 -1
  3. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/databricks_tellr_app.egg-info/SOURCES.txt +8 -0
  4. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/pyproject.toml +1 -1
  5. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/main.py +10 -1
  6. databricks_tellr_app-0.3.11.dev8/src/api/routes/admin_usage.py +69 -0
  7. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/feedback.py +27 -0
  8. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/sessions.py +5 -0
  9. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/services/feedback_service.py +43 -0
  10. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/services/session_manager.py +18 -0
  11. databricks_tellr_app-0.3.11.dev8/src/api/services/usage_events.py +117 -0
  12. databricks_tellr_app-0.3.11.dev8/src/api/services/usage_service.py +400 -0
  13. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/__init__.py +2 -0
  14. databricks_tellr_app-0.3.11.dev8/src/database/models/usage_event.py +39 -0
  15. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/README.md +0 -0
  16. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/databricks_tellr_app/__init__.py +0 -0
  17. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/databricks_tellr_app/run.py +0 -0
  18. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/databricks_tellr_app.egg-info/dependency_links.txt +0 -0
  19. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/databricks_tellr_app.egg-info/requires.txt +0 -0
  20. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/databricks_tellr_app.egg-info/top_level.txt +0 -0
  21. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/setup.cfg +0 -0
  22. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/setup.py +0 -0
  23. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/__init__.py +0 -0
  24. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/__init__.py +0 -0
  25. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/_otel_bootstrap.py +0 -0
  26. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/fixtures/tour_demo_deck.json +0 -0
  27. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/mcp_auth.py +0 -0
  28. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/mcp_server.py +0 -0
  29. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/middleware/__init__.py +0 -0
  30. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/middleware/request_logging.py +0 -0
  31. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/__init__.py +0 -0
  32. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/admin.py +0 -0
  33. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/agent_config.py +0 -0
  34. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/chat.py +0 -0
  35. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/deck_contributors.py +0 -0
  36. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/export.py +0 -0
  37. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/google_slides.py +0 -0
  38. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/images.py +0 -0
  39. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/local_version.py +0 -0
  40. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/profiles.py +0 -0
  41. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/settings/__init__.py +0 -0
  42. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/settings/contributors.py +0 -0
  43. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/settings/deck_prompts.py +0 -0
  44. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/settings/identities.py +0 -0
  45. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/settings/slide_styles.py +0 -0
  46. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/setup.py +0 -0
  47. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/slides.py +0 -0
  48. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/tools.py +0 -0
  49. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/tour.py +0 -0
  50. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/verification.py +0 -0
  51. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/routes/version.py +0 -0
  52. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/schemas/__init__.py +0 -0
  53. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/schemas/agent_config.py +0 -0
  54. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/schemas/feedback.py +0 -0
  55. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/schemas/requests.py +0 -0
  56. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/schemas/responses.py +0 -0
  57. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/schemas/settings/__init__.py +0 -0
  58. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/schemas/settings/requests.py +0 -0
  59. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/schemas/settings/responses.py +0 -0
  60. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/schemas/streaming.py +0 -0
  61. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/services/__init__.py +0 -0
  62. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/services/chat_service.py +0 -0
  63. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/services/export_job_queue.py +0 -0
  64. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/services/job_queue.py +0 -0
  65. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/services/session_naming.py +0 -0
  66. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/utils/__init__.py +0 -0
  67. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/api/utils/validation.py +0 -0
  68. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/__init__.py +0 -0
  69. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/config_loader.py +0 -0
  70. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/context_utils.py +0 -0
  71. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/database.py +0 -0
  72. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/databricks_client.py +0 -0
  73. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/defaults.py +0 -0
  74. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/encryption.py +0 -0
  75. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/init_default_profile.py +0 -0
  76. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/lakebase.py +0 -0
  77. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/migrate_profiles_to_agent_config.py +0 -0
  78. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/mlflow_agent_spans.py +0 -0
  79. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/mlflow_tracing.py +0 -0
  80. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/otel_logging.py +0 -0
  81. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/permission_context.py +0 -0
  82. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/prompt_modules.py +0 -0
  83. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/settings_db.py +0 -0
  84. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/core/user_context.py +0 -0
  85. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/__init__.py +0 -0
  86. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/deck_contributor.py +0 -0
  87. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/feedback.py +0 -0
  88. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/genie_space.py +0 -0
  89. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/google_global_credentials.py +0 -0
  90. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/google_oauth_token.py +0 -0
  91. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/identity.py +0 -0
  92. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/image.py +0 -0
  93. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/profile.py +0 -0
  94. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/profile_contributor.py +0 -0
  95. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/prompts.py +0 -0
  96. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/request_log.py +0 -0
  97. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/session.py +0 -0
  98. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/slide_deck_prompt.py +0 -0
  99. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/slide_style_library.py +0 -0
  100. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/database/models/user_preference.py +0 -0
  101. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/domain/__init__.py +0 -0
  102. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/domain/slide.py +0 -0
  103. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/domain/slide_deck.py +0 -0
  104. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/__init__.py +0 -0
  105. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/agent.py +0 -0
  106. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/agent_factory.py +0 -0
  107. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/config_service.py +0 -0
  108. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/config_validator.py +0 -0
  109. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/drive_uploader.py +0 -0
  110. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/evaluation/__init__.py +0 -0
  111. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/evaluation/llm_judge.py +0 -0
  112. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/genie_service.py +0 -0
  113. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/google_slides_auth.py +0 -0
  114. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/google_slides_prompts_defaults.py +0 -0
  115. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/html_to_google_slides.py +0 -0
  116. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/html_to_pptx.py +0 -0
  117. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/identity_provider.py +0 -0
  118. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/identity_providers/__init__.py +0 -0
  119. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/identity_providers/account_provider.py +0 -0
  120. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/identity_providers/local_provider.py +0 -0
  121. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/identity_providers/workspace_provider.py +0 -0
  122. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/image_service.py +0 -0
  123. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/image_tools.py +0 -0
  124. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/permission_service.py +0 -0
  125. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/pptx_from_html_huashu.py +0 -0
  126. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/pptx_from_records.py +0 -0
  127. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/pptx_prompts_defaults.py +0 -0
  128. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/profile_service.py +0 -0
  129. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/streaming_callback.py +0 -0
  130. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/tools/__init__.py +0 -0
  131. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/tools/agent_bricks_tool.py +0 -0
  132. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/tools/genie_tool.py +0 -0
  133. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/tools/mcp_tool.py +0 -0
  134. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/tools/model_endpoint_tool.py +0 -0
  135. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/tools/vector_tool.py +0 -0
  136. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/services/validator.py +0 -0
  137. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/__init__.py +0 -0
  138. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/css_utils.py +0 -0
  139. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/error_handling.py +0 -0
  140. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/html_safety.py +0 -0
  141. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/html_utils.py +0 -0
  142. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/image_utils.py +0 -0
  143. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/js_validator.py +0 -0
  144. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/logging_config.py +0 -0
  145. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/pi_filter.py +0 -0
  146. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/slide_hash.py +0 -0
  147. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/spotlight.py +0 -0
  148. {databricks_tellr_app-0.3.11.dev6 → databricks_tellr_app-0.3.11.dev8}/src/utils/text_caps.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.3.11.dev6
3
+ Version: 0.3.11.dev8
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databricks-tellr-app
3
- Version: 0.3.11.dev6
3
+ Version: 0.3.11.dev8
4
4
  Summary: Tellr application package for Databricks Apps
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -14,6 +14,7 @@ setup.py
14
14
  ./src/api/middleware/request_logging.py
15
15
  ./src/api/routes/__init__.py
16
16
  ./src/api/routes/admin.py
17
+ ./src/api/routes/admin_usage.py
17
18
  ./src/api/routes/agent_config.py
18
19
  ./src/api/routes/chat.py
19
20
  ./src/api/routes/deck_contributors.py
@@ -51,6 +52,8 @@ setup.py
51
52
  ./src/api/services/job_queue.py
52
53
  ./src/api/services/session_manager.py
53
54
  ./src/api/services/session_naming.py
55
+ ./src/api/services/usage_events.py
56
+ ./src/api/services/usage_service.py
54
57
  ./src/api/utils/__init__.py
55
58
  ./src/api/utils/validation.py
56
59
  ./src/core/__init__.py
@@ -86,6 +89,7 @@ setup.py
86
89
  ./src/database/models/session.py
87
90
  ./src/database/models/slide_deck_prompt.py
88
91
  ./src/database/models/slide_style_library.py
92
+ ./src/database/models/usage_event.py
89
93
  ./src/database/models/user_preference.py
90
94
  ./src/domain/__init__.py
91
95
  ./src/domain/slide.py
@@ -153,6 +157,7 @@ src/api/middleware/__init__.py
153
157
  src/api/middleware/request_logging.py
154
158
  src/api/routes/__init__.py
155
159
  src/api/routes/admin.py
160
+ src/api/routes/admin_usage.py
156
161
  src/api/routes/agent_config.py
157
162
  src/api/routes/chat.py
158
163
  src/api/routes/deck_contributors.py
@@ -190,6 +195,8 @@ src/api/services/feedback_service.py
190
195
  src/api/services/job_queue.py
191
196
  src/api/services/session_manager.py
192
197
  src/api/services/session_naming.py
198
+ src/api/services/usage_events.py
199
+ src/api/services/usage_service.py
193
200
  src/api/utils/__init__.py
194
201
  src/api/utils/validation.py
195
202
  src/core/__init__.py
@@ -225,6 +232,7 @@ src/database/models/request_log.py
225
232
  src/database/models/session.py
226
233
  src/database/models/slide_deck_prompt.py
227
234
  src/database/models/slide_style_library.py
235
+ src/database/models/usage_event.py
228
236
  src/database/models/user_preference.py
229
237
  src/domain/__init__.py
230
238
  src/domain/slide.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "databricks-tellr-app"
7
- version = "0.3.11.dev6"
7
+ version = "0.3.11.dev8"
8
8
  description = "Tellr application package for Databricks Apps"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -16,7 +16,7 @@ from fastapi.middleware.cors import CORSMiddleware
16
16
  from fastapi.responses import FileResponse
17
17
  from fastapi.staticfiles import StaticFiles
18
18
 
19
- from src.api.routes import admin, agent_config, chat, export, feedback, images, profiles, sessions, slides, tools, tour, verification, version, google_slides, setup, local_version
19
+ from src.api.routes import admin, admin_usage, agent_config, chat, export, feedback, images, profiles, sessions, slides, tools, tour, verification, version, google_slides, setup, local_version
20
20
  from src.api.routes.deck_contributors import router as deck_contributors_router
21
21
  from src.core.databricks_client import get_or_create_user_client, set_user_client
22
22
  from src.core.user_context import get_current_user as get_ctx_user, set_current_user
@@ -381,6 +381,14 @@ async def user_auth_middleware(request: Request, call_next):
381
381
  )
382
382
  set_permission_context(permission_ctx)
383
383
 
384
+ # Record login usage-event (visit-deduped, non-blocking, all envs)
385
+ if user_name:
386
+ try:
387
+ from src.api.services.usage_events import record_login
388
+ record_login(user_name)
389
+ except Exception as e:
390
+ logger.debug(f"Failed to record login usage event: {e}")
391
+
384
392
  # Record user login for local identity table (non-blocking)
385
393
  if user_id and user_name and IS_PRODUCTION:
386
394
  try:
@@ -411,6 +419,7 @@ app.add_middleware(RequestLoggingMiddleware)
411
419
 
412
420
  # Include API routers
413
421
  app.include_router(admin.router)
422
+ app.include_router(admin_usage.router)
414
423
  app.include_router(agent_config.router)
415
424
  app.include_router(chat.router)
416
425
  app.include_router(feedback.router)
@@ -0,0 +1,69 @@
1
+ """Admin usage-analytics endpoints (ungated, like the rest of /admin)."""
2
+
3
+ import logging
4
+
5
+ from fastapi import APIRouter, Depends, HTTPException, Query, status
6
+ from sqlalchemy.orm import Session
7
+
8
+ from src.api.services.usage_service import ALLOWED_WINDOWS, UsageService
9
+ from src.core.database import get_db
10
+
11
+ logger = logging.getLogger(__name__)
12
+ router = APIRouter(prefix="/api/admin/usage", tags=["admin-usage"])
13
+
14
+
15
+ def _validated_days(days: int) -> int:
16
+ if days not in ALLOWED_WINDOWS:
17
+ raise HTTPException(
18
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
19
+ detail=f"days must be one of {sorted(ALLOWED_WINDOWS)}",
20
+ )
21
+ return days
22
+
23
+
24
+ def _handle(callable_, *args, **kwargs):
25
+ try:
26
+ return callable_(*args, **kwargs)
27
+ except HTTPException:
28
+ raise
29
+ except Exception as e:
30
+ logger.error(f"Usage analytics error: {e}", exc_info=True)
31
+ raise HTTPException(
32
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
33
+ detail="Failed to compute usage analytics",
34
+ )
35
+
36
+
37
+ @router.get("/summary")
38
+ def get_summary(days: int = Query(default=7), db: Session = Depends(get_db)):
39
+ days = _validated_days(days)
40
+ return _handle(UsageService().get_summary, db, days=days)
41
+
42
+
43
+ @router.get("/daily")
44
+ def get_daily(days: int = Query(default=7), db: Session = Depends(get_db)):
45
+ days = _validated_days(days)
46
+ return _handle(UsageService().get_daily, db, days=days)
47
+
48
+
49
+ @router.get("/top-users")
50
+ def get_top_users(days: int = Query(default=7), db: Session = Depends(get_db)):
51
+ days = _validated_days(days)
52
+ return _handle(UsageService().get_top_users, db, days=days)
53
+
54
+
55
+ @router.get("/funnel")
56
+ def get_funnel(days: int = Query(default=7), db: Session = Depends(get_db)):
57
+ days = _validated_days(days)
58
+ return _handle(UsageService().get_funnel, db, days=days)
59
+
60
+
61
+ @router.get("/retention")
62
+ def get_retention(db: Session = Depends(get_db)):
63
+ return _handle(UsageService().get_retention, db)
64
+
65
+
66
+ @router.get("/heatmap")
67
+ def get_heatmap(days: int = Query(default=7), db: Session = Depends(get_db)):
68
+ days = _validated_days(days)
69
+ return _handle(UsageService().get_heatmap, db, days=days)
@@ -88,6 +88,33 @@ def get_stats_report(weeks: int = Query(default=12, ge=1, le=52), db: Session =
88
88
  )
89
89
 
90
90
 
91
+ @router.get("/list")
92
+ def list_feedback(
93
+ weeks: int = Query(default=12, ge=1, le=52),
94
+ category: str | None = Query(default=None),
95
+ severity: str | None = Query(default=None),
96
+ page: int = Query(default=1, ge=1),
97
+ page_size: int = Query(default=20, ge=1, le=100),
98
+ db: Session = Depends(get_db),
99
+ ):
100
+ try:
101
+ service = FeedbackService()
102
+ return service.list_feedback(
103
+ db=db,
104
+ weeks=weeks,
105
+ category=category,
106
+ severity=severity,
107
+ page=page,
108
+ page_size=page_size,
109
+ )
110
+ except Exception as e:
111
+ logger.error(f"Feedback list error: {e}", exc_info=True)
112
+ raise HTTPException(
113
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
114
+ detail="Failed to list feedback",
115
+ )
116
+
117
+
91
118
  @router.get("/report/summary")
92
119
  def get_feedback_summary(weeks: int = Query(default=4, ge=1, le=52), db: Session = Depends(get_db)):
93
120
  try:
@@ -25,6 +25,7 @@ from src.api.services.session_manager import (
25
25
  SessionNotFoundError,
26
26
  get_session_manager,
27
27
  )
28
+ from src.api.services.usage_events import record_deck_retrieved
28
29
  from src.core.database import get_db, get_db_session
29
30
  from src.core.permission_context import get_permission_context
30
31
  from src.core.user_context import get_current_user
@@ -467,6 +468,10 @@ async def get_session(session_id: str, db: Session = Depends(get_db)):
467
468
  # Check permission
468
469
  permission = _require_session_access(session, db, PermissionLevel.CAN_VIEW)
469
470
 
471
+ # Record deck-open usage event (deduped per user+deck, non-blocking)
472
+ if current_user:
473
+ record_deck_retrieved(current_user, session.get("id"))
474
+
470
475
  # Only the session creator sees chat messages — conversations are private
471
476
  is_creator = session.get("created_by") == current_user
472
477
  messages = []
@@ -215,6 +215,49 @@ class FeedbackService:
215
215
  "usage": usage,
216
216
  }
217
217
 
218
+ def list_feedback(
219
+ self,
220
+ db: Session,
221
+ weeks: int = 12,
222
+ category: str | None = None,
223
+ severity: str | None = None,
224
+ page: int = 1,
225
+ page_size: int = 20,
226
+ ) -> Dict[str, Any]:
227
+ """Paginated raw feedback conversations, newest first. Anonymous."""
228
+ cutoff = datetime.utcnow() - timedelta(weeks=weeks)
229
+ query = db.query(FeedbackConversation).filter(
230
+ FeedbackConversation.created_at >= cutoff
231
+ )
232
+ if category:
233
+ query = query.filter(FeedbackConversation.category == category)
234
+ if severity:
235
+ query = query.filter(FeedbackConversation.severity == severity)
236
+
237
+ total = query.count()
238
+ items = (
239
+ query.order_by(FeedbackConversation.created_at.desc())
240
+ .offset((page - 1) * page_size)
241
+ .limit(page_size)
242
+ .all()
243
+ )
244
+ return {
245
+ "items": [
246
+ {
247
+ "id": item.id,
248
+ "created_at": item.created_at.isoformat(),
249
+ "category": item.category,
250
+ "severity": item.severity,
251
+ "summary": item.summary,
252
+ "raw_conversation": item.raw_conversation,
253
+ }
254
+ for item in items
255
+ ],
256
+ "total": total,
257
+ "page": page,
258
+ "page_size": page_size,
259
+ }
260
+
218
261
  def get_feedback_summary(self, db: Session, weeks: int = 4) -> Dict[str, Any]:
219
262
  """Generate an AI summary of feedback conversations."""
220
263
  cutoff = datetime.utcnow() - timedelta(weeks=weeks)
@@ -689,6 +689,17 @@ class SessionManager:
689
689
  db.add(deck)
690
690
  db.flush()
691
691
 
692
+ # Record deck-created usage event (non-blocking)
693
+ try:
694
+ from src.api.services.usage_events import record_deck_created
695
+ from src.core.user_context import get_current_user as _get_user
696
+
697
+ record_deck_created(
698
+ modified_by or _get_user(), deck_owner.id
699
+ )
700
+ except Exception:
701
+ logger.debug("Failed to record deck_created event", exc_info=True)
702
+
692
703
  # Update activity on both the requesting session and the deck owner
693
704
  session.last_activity = datetime.utcnow()
694
705
  if session.id != deck_owner.id:
@@ -1781,6 +1792,13 @@ class SessionManager:
1781
1792
  def cleanup_expired_sessions(self) -> int:
1782
1793
  """Delete sessions that have exceeded TTL.
1783
1794
 
1795
+ WARNING: user_sessions is the app's durable usage history. This
1796
+ method is intentionally NOT scheduled anywhere — it is only
1797
+ reachable via the manual POST /api/sessions/cleanup endpoint.
1798
+ Do not wire it to a scheduler; doing so would destroy the
1799
+ history that the /admin usage dashboard's pre-event-log
1800
+ aggregations rely on.
1801
+
1784
1802
  Returns:
1785
1803
  Number of sessions deleted
1786
1804
  """
@@ -0,0 +1,117 @@
1
+ """Fire-and-forget usage-event recording with in-process dedup.
2
+
3
+ Mirrors the request_logs middleware pattern: writes happen on an executor
4
+ thread (or inline when already off the event loop) and never raise.
5
+
6
+ Dedup semantics (per spec):
7
+ - ``login``: one event per "visit" — the first authenticated request after
8
+ >= 30 minutes without any authenticated request (sliding window: every
9
+ request refreshes the timer, so continuous activity never re-emits).
10
+ - ``deck_retrieved``: one event per (username, session_id) per 30-minute window.
11
+ - ``deck_created``: never deduped.
12
+
13
+ Caches are per-process; a restart may produce occasional extra login
14
+ events, which is acceptable.
15
+ """
16
+
17
+ import asyncio
18
+ import logging
19
+ import time
20
+
21
+ from src.database.models.usage_event import (
22
+ EVENT_DECK_CREATED,
23
+ EVENT_DECK_RETRIEVED,
24
+ EVENT_LOGIN,
25
+ )
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ _DEDUP_WINDOW_SECONDS = 30 * 60
30
+
31
+ _login_cache: dict[str, float] = {}
32
+ _retrieval_cache: dict[tuple[str, str], float] = {}
33
+
34
+
35
+ def reset_dedup_caches() -> None:
36
+ """Clear dedup caches (test helper)."""
37
+ _login_cache.clear()
38
+ _retrieval_cache.clear()
39
+
40
+
41
+ def _write_event(username: str, event_type: str, session_id) -> None:
42
+ """Synchronously insert one usage event. Never raises."""
43
+ try:
44
+ from src.core.database import get_session_local
45
+ from src.database.models.usage_event import UsageEvent
46
+
47
+ session_factory = get_session_local()
48
+ db = session_factory()
49
+ try:
50
+ db.add(
51
+ UsageEvent(
52
+ username=username,
53
+ event_type=event_type,
54
+ session_id=session_id,
55
+ )
56
+ )
57
+ db.commit()
58
+ except Exception:
59
+ logger.debug("Failed to write usage event", exc_info=True)
60
+ db.rollback()
61
+ finally:
62
+ db.close()
63
+ except Exception:
64
+ logger.debug("Failed to write usage event", exc_info=True)
65
+
66
+
67
+ def _submit(username: str, event_type: str, session_id) -> None:
68
+ """Dispatch a write without blocking the caller.
69
+
70
+ On the event loop -> run_in_executor; in a worker thread (no running
71
+ loop) -> write inline, which is already off the loop.
72
+ """
73
+ try:
74
+ loop = asyncio.get_running_loop()
75
+ loop.run_in_executor(None, _write_event, username, event_type, session_id)
76
+ except RuntimeError:
77
+ _write_event(username, event_type, session_id)
78
+ except Exception:
79
+ logger.debug("Failed to submit usage event", exc_info=True)
80
+
81
+
82
+ def record_login(username) -> None:
83
+ """Record a login "visit": the first authenticated request after >=30
84
+ minutes without any authenticated request (sliding window).
85
+
86
+ Every call refreshes the cached timestamp, so continuous activity keeps
87
+ the window sliding and emits no further events until the user has been
88
+ inactive for a full window.
89
+ """
90
+ if not username:
91
+ return
92
+ now = time.monotonic()
93
+ last = _login_cache.get(username)
94
+ _login_cache[username] = now
95
+ if last is not None and (now - last) < _DEDUP_WINDOW_SECONDS:
96
+ return
97
+ _submit(username, EVENT_LOGIN, None)
98
+
99
+
100
+ def record_deck_created(username, session_id) -> None:
101
+ """Record a deck creation. Never deduped."""
102
+ if not username:
103
+ return
104
+ _submit(username, EVENT_DECK_CREATED, session_id)
105
+
106
+
107
+ def record_deck_retrieved(username, session_id) -> None:
108
+ """Record a deck open, deduped per (user, deck) per 30-minute window."""
109
+ if not username:
110
+ return
111
+ key = (username, str(session_id))
112
+ now = time.monotonic()
113
+ last = _retrieval_cache.get(key)
114
+ if last is not None and (now - last) < _DEDUP_WINDOW_SECONDS:
115
+ return
116
+ _retrieval_cache[key] = now
117
+ _submit(username, EVENT_DECK_RETRIEVED, session_id)