annotations
3 TopicsGeneric Commentary Writeback in Power BI using Fabric User Data Functions
Business users often need to explain why a number changed, not just view the number. These explanations usually end up in Excel, emails, Teams messages, or PowerPoint and become disconnected from the actual Power BI report context. I built a generic commentary writeback framework using Microsoft Fabric User Data Functions. The idea is simple: Selected business context + metric + period = one unique comment context. A user can select a period and metric, choose a row in a matrix or use report slicers, enter a comment, and save it directly from Power BI. For example: 🎯Target achieved at 104.5% — performance is above plan due to stronger volume and improved customer mix. What makes it generic? Instead of hardcoding Cost Center, Profit Center, Customer, Material, and every other dimension into the writeback function, Power BI dynamically creates a grain key from the current report context. This means the same framework can support commentary at different levels, such as Cost Center + Profit Center or Cost Center + Profit Center + Material, without redesigning the writeback function for every combination. Versioning and audit Comments are stored in a versioned backend table. Every insert, update, or delete creates a new version. A latest-comment view returns only the current active comment to Power BI, while the complete history remains available for audit and traceability. I used Databricks as the backend for this demo, but the same design can be extended to Fabric Warehouse, Azure SQL, or another suitable SQL-based storage layer. Why I built this The goal was to bring business commentary closer to the data itself and make explanations contextual, reusable, and auditable. This pattern can be useful for finance variance commentary, forecast assumptions, sales performance notes, planning annotations, and other enterprise reporting scenarios. The most interesting part for me was using Fabric User Data Functions with Power BI Translytical Task Flows to turn a report from a read-only analytical experience into an actionable workflow. Would love to hear how others are approaching commentary and writeback scenarios in Power BI and Microsoft Fabric. slindsay Praful_Potphode tharunkumarRTK Natarajan_M165Views4likes0CommentsData annotations
With Translytical task flows you can enable datapoint annotation directly within your report. For example, in the report below you can add, edit or delete annotations about each month’s sales data. Here we can see that to add a new data annotation, you select the datapoint, input your comment, and then submit, and it appears immediately on the report. Data annotation scenarios may consist of up to three user data functions to: Add Annotation: Add a new datapoint annotation Edit Annotation: Update an exisiting annotation Delete Annotation: Delete a specific annotation Here is the user data function for the Add Annotation scenario: import fabric.functions as fn import logging import calendar udf = fn.UserDataFunctions() @udf.connection(argName="sqlDB",alias="Translytical") @udf.function() def AddAnnotation(sqlDB: fn.FabricSqlConnection, date: str, commentdate: str, comment: str, user: str) -> str: logging.info('Python UDF trigger function processed a request.') # month abbr to YYYY-MM-DD month_num = list(calendar.month_abbr).index(date) formatted_date = f"{2023}-{month_num:02d}-10" data = (commentdate, formatted_date, user, comment) # Establish a connection to the SQL database connection = sqlDB.connect() cursor = connection.cursor() logging.info("Adding comment ... ") # Insert data into the table insert_query = "INSERT INTO [dbo].[DataReasoning] ([Date_Created],[Date_Month],[User],[Comment]) VALUES (?, ?, ?, ?);" cursor.execute(insert_query, data) logging.info("Comment was added") # Commit the transaction connection.commit() # Close the connection cursor.close() connection.close() return "Comment was successfully added" Here is the user data function for the Edit Annotation scenario: import fabric.functions as fn import logging import calendar udf = fn.UserDataFunctions() @udf.connection(argName="sqlDB",alias="Translytical") @udf.function() def EditAnnotation(sqlDB: fn.FabricSqlConnection, comment: str, commentdate: str, user: str, newcomment :str ) -> str: logging.info('Python UDF trigger function processed a request.') data = (newcomment, commentdate, user, comment) # Establish a connection to the SQL database connection = sqlDB.connect() cursor = connection.cursor() # Insert data into the table logging.info("Updating comment") update_query = " UPDATE [dbo].[DataReasoning] SET [Comment] = ?, [Date_Created] = ?, [User] = ? WHERE [Comment] = ?;" cursor.execute(update_query, data) logging.info("Comment was updated") # Commit the transaction connection.commit() # Close the connection cursor.close() connection.close() return "Comment was successfully updated" Here is the user data function for the Delete Annotation scenario: import fabric.functions as fn import logging udf = fn.UserDataFunctions() @udf.connection(argName="sqlDB",alias="Translytical") @udf.function() def DeleteAnnotation(sqlDB: fn.FabricSqlConnection, comment: str ) -> str: logging.info('Python UDF trigger function processed a request.') # Establish a connection to the SQL database connection = sqlDB.connect() cursor = connection.cursor() # Delete comment logging.info("Deleting comment ... ") delete_query = "DELETE FROM [dbo].[DataReasoning] WHERE [Comment] = ?" cursor.execute(delete_query,comment) logging.info("Comment was deleted") # Commit the transaction connection.commit() # Close the connection cursor.close() connection.close() return "Comment was successfully deleted" Feel free to use this code as inspiration for your data annotations scenarios!Intelliventra – Smart Event Management Application
Intelliventra lets participants easily browse all sessions and add them to their personal agenda. Attendees can view session details such as time, room, track, and speaker. The page also allows attendees to add,remove sessions. It is designed to be simple, clear, and works for any event, from small workshops to large conferences.20KViews14likes0Comments