Wednesday, 1 January 2025

Prompt Engineering with Microsoft Copilot

Prompting Microsoft Copilot: Prompting is asking the copilot to give you information.

Four parts to cover in your prompt> 1. goal 2. context 3. source 4. expectation

1. Goal - what do I want, e.g., tell copilot what we want

I want tactics for pricing my SaaS products

2. Context - Why do I need it

e.g., this is a new product in the B2C space

3. Sources - optional - ask from specific resources

e.g. provide my company stats and ask to compare to my market (from the LLM)

4. Expectations - Table of info, summary, or even an image(maybe not)

M365 Copilot app.

Apps that M365 Copilot embedded with if you have the M365 licence include: 
1. Word, 2. Excel, 3. PowerPoint, 4. Outlook, and 5. Teams

1. Word

Word Copilot is part of the M365 copilot branding.  Generates scaffolding/first draft.

2. Excel

Use in Excel with your own data - allows easy query using English/natural language.

3. PowerPoint

Draft a presentation using org themes based on prompts. You can also use Copilot to add images you create using Copilot (DALL-e).


Wednesday, 4 December 2024

Power Platform licensing capability Notes

 Power Apps Premium Licensing Costs (4 Dec 2024): 

The retail price is $20/user per month, but it drops to $12/user/month for 2k licenses per org.  Large customers can negotiate as always.  Premium connectors & 40k requests per user (24-hours rolling)

Power Apps Pricing | Microsoft Power Platform

Power Automate Premium allows 40k requests per user (per 24-hour rolling), and the E365 allows 6k requests. Circa $18 TBC.

Dynamics 365 professional allows 40k requests per user (per 24-hour rolling) and gives the user premium Power Apps licensing.

Tuesday, 19 November 2024

Playwright Series - Post 4 - 6 Min walkthru of Playwright testing with Azure Monitor

Overview: Install VS Code and Playwright Extensions, create tests, set MFA for Canvas Apps/Power Apps, loop through Power App applications and check the home page is loading, write logs to Azure App Insights and show via the Azure Dashboard.


6 min - annotated Playwright setup and use video


========================

Playwright Series

Saturday, 9 November 2024

Playwright Series - Post 3 - Add App Insights logging inside your tests

 

Run > npm install @microsoft/applicationinsights-web

Add the code to log from Playwright to your App Insights as shown below:


=========================

Playwright Series

Friday, 8 November 2024

Playwright series - Post 2 - Refactored TS code for Consciously verify Apps in Production

Overview: Create a function with Playwright tests that loops through all my production apps, logs in, and validates the Page title load on each app's home page.

Steps:

1. Create the spec.ts code that reads app.json to loop thru and validate sites

2. Record and Store the session state for all future test runs (Used for MFA in the tests runs)

3. Create an apps.json file containing URLs to open and validate


4. After running the test, you'll see that the 3 tests were completed successfully. In my case, there were 2 Power Apps with MFA enabled and an anonymous public website that had been checked.

Optional
Create short cuts to run your tests using PowerShell
PS C:\repos\PW> npx playwright test -g "Prod-CanvasApps" --project=chromium --headed

Next Steps:

Run continuously using the Azure Playwright Service.

=========================

Playwright Series

Playwright Post 1 - Overview of E2E testing using Playwright

Playwright Post 2 - Continuously Test/Monitor Canvas apps and website with MFA enabled (this post)

Playwright Post 3 - Add App Insights logging inside your Playwright tests 

Playwright Post 4 - 6 Min walkthru of Playwright testing with Azure Monitor


Wednesday, 6 November 2024

Fix links within Pdf files when moving from a File share to web hosting

Overview: Build a console to help migrate more than 80k PDF document internal links. The client used a DFS SMB file share to hold index PDFs and multiple documents that needed to be moved to a SharePoint document library.

Hypothesis: Loop through all pdf's in a folder; if there are links, identify the file server links and convert them to web links so they work in the new SharePoint document library.  Various tools were identified as possible solutions but came up short in the migration.  Two good tools are Replace Magic & PDF-XChange Editor.

Resolution: Below is the C# code I wrote to change the links in the VS code.  The debugger was useful as there were many different types of links within the plethora of PDFs.


C# Code

using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Annot;
using iText.Kernel.Pdf.Action;
using iText.Bouncycastle.Crypto;  // pdf fails at runtime periodically without the directive
using System.Text.RegularExpressions;
class Program
{
    static void Main(string[] args)
    {
        string folderPath = @"C:\Users\PaulBeck\Downloads\Software\LinkConvert\ConvertLinksCsharp\"; // Replace with your folder path          
        Console.WriteLine("Please enter a folder Path: e.g. " + folderPath);
        string inputPath = Console.ReadLine();        
        Console.WriteLine("Last Path: e.g. Childfolder2");
        string inputPathVol = Console.ReadLine();
        if (inputPath.Length>5)  {
            folderPath = inputPath;   }
        string[] pdfFiles = Directory.GetFiles(folderPath, "*.pdf");
        foreach (string file in pdfFiles)
        {
            Uri fileUri = new Uri(file);
            string directory = Path.GetDirectoryName(file);
            string filename = Path.GetFileNameWithoutExtension(file);
            string extension = Path.GetExtension(file);           
            string newFilename = $"{filename}_new{extension}";  // Create the new filename
            string newFilePath = Path.Combine(directory, newFilename); // Combine the directory and new filename to form the new URL
            UpdatePdf(fileUri.AbsoluteUri,newFilePath, inputPathVol);
        }
    }
private static void UpdatePdf(string inputFilePath, string outputFilePath, string lastPathPart)
{
    var varInnerName = "";
    PdfDocument pdfDoc = new PdfDocument(new PdfReader(inputFilePath), new PdfWriter(outputFilePath));
        for (int i = 1; i <= pdfDoc.GetNumberOfPages(); i++)        // Iterate through the pages
        {
            var page = pdfDoc.GetPage(i);
            var annotations = page.GetAnnotations();
            foreach (var annotation in annotations)            // Iterate through the annotations
            {
                if (annotation.GetSubtype().Equals(PdfName.Link))
                {
                    var linkAnnotation = (PdfLinkAnnotation)annotation;
                    var action = linkAnnotation.GetAction();                                                        
                    if (action is PdfDictionary dictionary)
                    {
                        foreach (var key in dictionary.KeySet())
                        {
                            var value = dictionary.Get(key);
                            Console.WriteLine($"{key}: {value}");
                            var varPdfNameF = dictionary.Get(PdfName.F);
                            if (varPdfNameF is PdfDictionary varF2Dict)      {
                                varInnerName = varF2Dict.Get(PdfName.F).ToString();  }
                        }
                    }
                    else
                    {   Console.WriteLine("No URL found.");    }  
if (action != null && (action.Get(PdfName.S).Equals(PdfName.GoToR) || action.Get(PdfName.S).Equals(PdfName.Launch)))                
                      {
                        var varF = action.GetAsString(PdfName.F)?.ToString() ?? "";
                        var uri = $"https://radimaging.sharepoint.com/sites/Documents/Standards/{lastPathPart}/{varInnerName}";          
                       string pattern = @"\.\./";
                       if(uri.Contains("../"))    // string cleanUrl1 = Regex.Replace(uri, pattern, string.Empty);
                       {          
                        uri = $"https://radimaging.sharepoint.com/sites/Documents/Standards/{varInnerName}";
                        uri= Regex.Replace(uri, pattern, string.Empty);                       
                       }
                        var newAction = PdfAction.CreateURI(uri);
                        if (varF.Length > 20)      {
                            newAction = PdfAction.CreateURI(varF);     }
                        linkAnnotation.SetAction(newAction);
                    }           
                }
            }
        }
        pdfDoc.Close();
        Console.WriteLine("PDF links updated successfully!");
    }
}