public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Entity Search Custom Skill: C# HTTP trigger function processed a request.");

            string skillName = executionContext.FunctionName;
            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(new BadRequestObjectResult($"{skillName} - Invalid request record array."));
            }

            string bingApiKey = Environment.GetEnvironmentVariable(bingApiKeySetting, EnvironmentVariableTarget.Process);

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var entityName = inRecord.Data["name"] as string;
                string uri     = bingApiEndpoint + "?q=" + Uri.EscapeDataString(entityName) + "&mkt=en-us&count=10&offset=0&safesearch=Moderate";

                IEnumerable <BingEntity> entities =
                    await WebApiSkillHelpers.Fetch <BingEntity>(uri, "Ocp-Apim-Subscription-Key", bingApiKey, "entities.value");

                ExtractTopEntityMetadata(entities, outRecord.Data);
                outRecord.Data["entities"] = entities;
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
Exemple #2
0
        public async Task <IActionResult> RunVideoIndexer(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Video Indexer Custom Skill: C# HTTP trigger function processed a request");

            var skillName      = executionContext.FunctionName;
            var requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(new BadRequestObjectResult($"{skillName} - Invalid request record array."));
            }

            var response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName,
                                                                               requestRecords,
                                                                               async (inRecord, outRecord) =>
            {
                var encodedVideoUrl = (string)inRecord.Data["metadata_storage_path"];
                var videoUrl        = UrlSafeBase64Decode(encodedVideoUrl);
                var videoName       = (string)inRecord.Data["metadata_storage_name"];
                var sasKey          = await _videoIndexerBlobClient.GetSasKey(videoUrl);
                var videoId         = await _videoIndexerClient.SubmitVideoIndexingJob(videoUrl + sasKey, encodedVideoUrl, videoName);
                log.LogInformation("Uploaded video {VideoName} - Video Id in indexer: {Id}", videoName, videoId);
                outRecord.Data["videoId"] = videoId;
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> RunAnalyzeForm(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Analyze Form Custom Skill: C# HTTP trigger function processed a request.");

            string skillName = executionContext.FunctionName;
            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(new BadRequestObjectResult($"{skillName} - Invalid request record array."));
            }

            string formsRecognizerApiKey = Environment.GetEnvironmentVariable(formsRecognizerApiKeySetting, EnvironmentVariableTarget.Process);
            string modelId = Environment.GetEnvironmentVariable(modelIdSetting, EnvironmentVariableTarget.Process);

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var formUrl      = inRecord.Data["formUrl"] as string;
                var formSasToken = inRecord.Data["formSasToken"] as string;

                // Fetch the document
                byte[] documentBytes;
                using (var webClient = new WebClient())
                {
                    documentBytes = await webClient.DownloadDataTaskAsync(new Uri(formUrl + formSasToken));
                }

                string uri = formsRecognizerApiEndpoint + "/models/" + Uri.EscapeDataString(modelId) + "/analyze";

                List <Page> pages =
                    (await WebApiSkillHelpers.FetchAsync <Page>(
                         uri,
                         apiKeyHeader: "Ocp-Apim-Subscription-Key",
                         apiKey: formsRecognizerApiKey,
                         collectionPath: "pages",
                         method: HttpMethod.Post,
                         postBody: documentBytes,
                         contentType))
                    .ToList();

                foreach (KeyValuePair <string, string> kvp in fieldMappings)
                {
                    var value = GetField(pages, kvp.Key);
                    if (!string.IsNullOrWhiteSpace(value))
                    {
                        outRecord.Data[kvp.Value] = value;
                    }
                }
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> RunCustomVision(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Custom Vision Custom Skill: C# HTTP trigger function processed a request.");

            string skillName = executionContext.FunctionName;
            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(new BadRequestObjectResult($"{skillName} - Invalid request record array."));
            }


            var predictionUrl = Environment.GetEnvironmentVariable(customVisionPredictionUrlSetting, EnvironmentVariableTarget.Process);
            var predictionKey = Environment.GetEnvironmentVariable(customVisionApiKeySetting, EnvironmentVariableTarget.Process);
            var maxPages      = Convert.ToInt32(Environment.GetEnvironmentVariable(maxPagesSetting, EnvironmentVariableTarget.Process));

            if (maxPages == 0)
            {
                maxPages = 1;
            }
            var threshold = Convert.ToDouble(Environment.GetEnvironmentVariable(minProbabilityThresholdSetting, EnvironmentVariableTarget.Process));

            if (threshold == 0.0)
            {
                threshold = 0.5;
            }

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var pages       = inRecord.Data["pages"] as JArray;
                var tags        = new List <string>();
                var predictions = new List <object>();
                foreach (var page in pages.Take(maxPages))
                {
                    var pageBinaryData  = Convert.FromBase64String(page.ToString());
                    var pagePredictions = await GetPredictionsForImageAsync(pageBinaryData, predictionUrl, predictionKey);

                    var analyzeResult = JsonConvert.DeserializeObject <AnalyzeResult>(pagePredictions);
                    var pageTags      = analyzeResult.Predictions.Where(p => p.Probability >= threshold).Select(p => p.TagName).Distinct();
                    tags.AddRange(pageTags);
                }

                outRecord.Data["tags"] = tags.Distinct().ToArray();
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> RunStoreImage(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Image Store Custom Skill: C# HTTP trigger function processed a request.");

            string skillName = executionContext.FunctionName;
            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null || requestRecords.Count() != 1)
            {
                return(new BadRequestObjectResult($"{skillName} - Invalid request record array: Skill requires exactly 1 image per request."));
            }

            string blobStorageConnectionString = Environment.GetEnvironmentVariable(blobStorageConnectionStringSetting, EnvironmentVariableTarget.Process);
            string blobContainerName           = String.IsNullOrEmpty(req.Headers[blobContainerHeaderName])
                ? Environment.GetEnvironmentVariable(blobStorageContainerNameSetting, EnvironmentVariableTarget.Process)
                : (string)req.Headers[blobContainerHeaderName];

            if (String.IsNullOrEmpty(blobStorageConnectionString) || String.IsNullOrEmpty(blobContainerName))
            {
                return(new BadRequestObjectResult($"{skillName} - Information for the blob storage account is missing"));
            }
            var imageStore = new BlobImageStore(blobStorageConnectionString, blobContainerName);

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var imageData = inRecord.Data["imageData"] as string;
                var imageName = inRecord.Data["imageName"] as string;
                if (String.IsNullOrEmpty(imageName))
                {
                    imageName = Guid.NewGuid().ToString();
                }
                var mimeType = inRecord.Data["mimeType"] as string;
                if (String.IsNullOrEmpty(mimeType))
                {
                    mimeType = "image/jpeg";
                }
                string imageUri = await imageStore.UploadToBlobAsync(imageData, imageName, mimeType);
                outRecord.Data["imageStoreUri"] = imageUri;
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> RunGeoPointFromName(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Geo Point From Name Custom skill: C# HTTP trigger function processed a request.");

            string skillName = executionContext.FunctionName;
            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(new BadRequestObjectResult($"{skillName} - Invalid request record array."));
            }

            string azureMapsKey = Environment.GetEnvironmentVariable(azureMapsKeySetting, EnvironmentVariableTarget.Process);

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var address = inRecord.Data["address"] as string;
                string uri  = azureMapsUri
                              + "?api-version=1.0&query=" + Uri.EscapeDataString(address)
                              + "&subscription-key=" + Uri.EscapeDataString(azureMapsKey);

                IEnumerable <Geography> geographies =
                    await WebApiSkillHelpers.FetchAsync <Geography>(uri, "results");

                if (geographies.FirstOrDefault() is Geography mainGeoPoint)
                {
                    outRecord.Data["mainGeoPoint"] = new {
                        Type        = "Point",
                        Coordinates = new double[] { mainGeoPoint.Position.Lon, mainGeoPoint.Position.Lat }
                    };
                }
                outRecord.Data["results"] = geographies;
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
Exemple #7
0
        public static async Task <IActionResult> RunAnalyzeForm(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            log.LogInformation("Analyze Form Custom Skill: C# HTTP trigger function processed a request.");

            string skillName = executionContext.FunctionName;
            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(new BadRequestObjectResult($"{skillName} - Invalid request record array."));
            }

            string formsRecognizerEndpointUrl = Environment.GetEnvironmentVariable(formsRecognizerApiEndpointSetting, EnvironmentVariableTarget.Process).TrimEnd('/');
            string formsRecognizerApiKey      = Environment.GetEnvironmentVariable(formsRecognizerApiKeySetting, EnvironmentVariableTarget.Process);


            // string modelId = Environment.GetEnvironmentVariable(modelIdSetting, EnvironmentVariableTarget.Process);

            int retryDelay  = int.TryParse(Environment.GetEnvironmentVariable(retryDelaySetting, EnvironmentVariableTarget.Process), out int parsedRetryDelay) ? parsedRetryDelay : 1000;
            int maxAttempts = int.TryParse(Environment.GetEnvironmentVariable(maxAttemptsSetting, EnvironmentVariableTarget.Process), out int parsedMaxAttempts) ? parsedMaxAttempts : 100;

            Dictionary <string, string> fieldMappings = JsonConvert.DeserializeObject <Dictionary <string, string> >(
                File.ReadAllText($"{executionContext.FunctionAppDirectory}\\field-mappings.json"));

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                var formUrl      = inRecord.Data["formUrl"] as string;
                var formSasToken = inRecord.Data["formSasToken"] as string;
                string formUri   = WebApiSkillHelpers.CombineSasTokenWithUri(formUrl, formSasToken);

                // Create the job
                string jobId = await GetJobId(formsRecognizerEndpointUrl, formUri, formsRecognizerApiKey);

                // Get the results
                for (int attempt = 0; attempt < maxAttempts; attempt++)
                {
                    (string status, JToken result) = await GetJobStatus(jobId, formsRecognizerApiKey);
                    if (status == "failed")
                    {
                        var errors = result.SelectToken("analyzeResult.errors") as JArray;
                        outRecord.Errors.AddRange(errors.Children().Select(error => new WebApiErrorWarningContract
                        {
                            Message = error.SelectToken("message").ToObject <string>()
                        }));
                        return(outRecord);
                    }
                    if (status == "succeeded")
                    {
                        //List<Page> pages = result.SelectToken("analyzeResult.pageResults").ToObject<List<Page>>();


                        List <Page> pages = result.SelectToken("analyzeResult.readResults").ToObject <List <Page> >();


                        foreach (KeyValuePair <string, string> kvp in fieldMappings)
                        {
                            string value = GetField(pages, kvp.Key);
                            if (!string.IsNullOrWhiteSpace(value))
                            {
                                outRecord.Data[kvp.Value] = value;
                            }
                        }
                        return(outRecord);
                    }
                    await Task.Delay(retryDelay);
                }
                outRecord.Errors.Add(new WebApiErrorWarningContract
                {
                    Message = $"The forms recognizer did not finish the job after {maxAttempts} attempts."
                });
                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log,
            ExecutionContext executionContext)
        {
            string skillName = executionContext.FunctionName;
            IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);

            if (requestRecords == null)
            {
                return(new BadRequestObjectResult($"{skillName} - Invalid request record array."));
            }

            // Get Endpoint and access key from App Settings
            string apiKey      = Environment.GetEnvironmentVariable(textAnalyticsApiKeySetting, EnvironmentVariableTarget.Process);
            string apiEndpoint = Environment.GetEnvironmentVariable(textAnalyticsApiEndpointSetting, EnvironmentVariableTarget.Process);

            if (apiKey == null || apiEndpoint == null)
            {
                return(new BadRequestObjectResult($"{skillName} - TextAnalyticsForHealth API key or Endpoint is missing. Make sure to set it in the Environment Variables."));
            }
            var client = new TextAnalyticsClient(new Uri(apiEndpoint), new AzureKeyCredential(apiKey));

            // Get a custom timeout from the header, if it exists. If not use the default timeout.
            int timeout;

            if (!int.TryParse(req.Headers["timeout"].ToString(), out timeout))
            {
                timeout = defaultTimeout;
            }
            timeout = Math.Clamp(timeout - timeoutBuffer, 1, maxTimeout - timeoutBuffer);
            var timeoutMiliseconds = timeout * 1000;
            var timeoutTask        = Task.Delay(timeoutMiliseconds);

            // Get a custom default language, if none is provided, use english
            string defaultLanguage = req.Headers.ContainsKey("defaultLanguageCode")? req.Headers["defaultLanguageCode"].ToString(): "en";

            WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                               async (inRecord, outRecord) => {
                if (timeoutTask.IsCompleted)
                {
                    // The time limit for all the skills has been met
                    outRecord.Errors.Add(new WebApiErrorWarningContract
                    {
                        Message = "TextAnalyticsForHealth Error: The Text Analysis Operation took too long to complete."
                    });
                    return(outRecord);
                }

                // Prepare analysis operation input
                if (!inRecord.Data.ContainsKey("text"))
                {
                    outRecord.Errors.Add(new WebApiErrorWarningContract
                    {
                        Message = "TextAnalyticsForHealth Error: The skill request did not contain 'text' in the input."
                    });
                    return(outRecord);
                }
                var document = inRecord.Data["text"] as string;
                var language = inRecord.Data.ContainsKey("languageCode") ? inRecord.Data["languageCode"] as string : defaultLanguage;

                var docInfo = new StringInfo(document);
                if (docInfo.LengthInTextElements >= maxCharLength)
                {
                    outRecord.Warnings.Add(new WebApiErrorWarningContract
                    {
                        Message = $"TextAnalyticsForHealth Warning: The submitted document was over {maxCharLength} elements. It has been truncated to fit this requirement."
                    });
                    document = docInfo.SubstringByTextElements(0, maxCharLength);
                }

                var options = new AnalyzeHealthcareEntitiesOptions {
                };
                List <string> batchInput = new List <string>()
                {
                    document
                };

                // start analysis process
                var timer = System.Diagnostics.Stopwatch.StartNew();
                AnalyzeHealthcareEntitiesOperation healthOperation = await client.StartAnalyzeHealthcareEntitiesAsync(batchInput, language, options);
                var healthOperationTask = healthOperation.WaitForCompletionAsync().AsTask();

                if (await Task.WhenAny(healthOperationTask, timeoutTask) == healthOperationTask)
                {
                    // Task Completed, now lets process the result.
                    outRecord.Data["status"] = healthOperation.Status.ToString();
                    if (healthOperation.Status != TextAnalyticsOperationStatus.Succeeded || !healthOperation.HasValue)
                    {
                        // The operation was not a success
                        outRecord.Errors.Add(new WebApiErrorWarningContract
                        {
                            Message = "TextAnalyticsForHealth Error: Health Operation returned a non-succeeded status."
                        });
                    }
                    else
                    {
                        // The operation was a success, so lets add the results to our output.
                        await ExtractEntityData(healthOperation.Value, outRecord);
                    }
                }
                else
                {
                    // Timeout
                    outRecord.Errors.Add(new WebApiErrorWarningContract
                    {
                        Message = "TextAnalyticsForHealth Error: The Text Analysis Operation took too long to complete."
                    });
                }

                // Record how long this task took to complete.
                timer.Stop();
                var timeToComplete = timer.Elapsed.TotalSeconds;
                log.LogInformation($"Time to complete request for document with ID {inRecord.RecordId}: {timeToComplete}");

                return(outRecord);
            });

            return(new OkObjectResult(response));
        }
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log, ExecutionContext executionContext)
        {
            log.LogInformation("Submit S2T Skill: C# HTTP trigger function processed a request.");
            log.LogInformation($"REQUEST: {new StreamReader(req.Body).ReadToEnd()}");
            req.Body.Position = 0;
            try
            {
                string destSasUrl;
                string skillName = executionContext.FunctionName;
                IEnumerable <WebApiRequestRecord> requestRecords = WebApiSkillHelpers.GetRequestRecords(req);
                if (requestRecords == null)
                {
                    return(new BadRequestObjectResult($"{skillName} - Invalid request record array."));
                }
                string storageConnectionString = Environment.GetEnvironmentVariable("StorageConnection");
                string region = Environment.GetEnvironmentVariable("region");

                CloudStorageAccount storageAccount;
                if (CloudStorageAccount.TryParse(storageConnectionString, out storageAccount))
                {
                    CloudBlobClient    cloudBlobClient    = storageAccount.CreateCloudBlobClient();
                    CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference("transcribed");
                    await cloudBlobContainer.CreateIfNotExistsAsync();

                    destSasUrl = GetContainerSasUri(cloudBlobContainer);
                }
                else
                {
                    // Otherwise, let the user know that they need to define the environment variable.
                    throw new Exception("Cannot access storage account");
                }


                WebApiSkillResponse response = await WebApiSkillHelpers.ProcessRequestRecordsAsync(skillName, requestRecords,
                                                                                                   async (inRecord, outRecord) =>
                {
                    Uri jobId;
                    var recUrl      = inRecord.Data["recUrl"] as string;
                    var recSasToken = inRecord.Data["recSasToken"] as string;

                    Transcription tc = new Transcription()
                    {
                        ContentUrls = new[] { new Uri(recUrl + recSasToken) },
                        DisplayName = "Transcription",
                        Locale      = "en-US",
                        model       = null,
                        properties  = new Properties
                        {
                            diarizationEnabled         = true,
                            wordLevelTimestampsEnabled = true,
                            PunctuationMode            = "DictatedAndAutomatic",
                            destinationContainerUrl    = destSasUrl
                        }
                    };

                    var client         = new HttpClient();
                    client.Timeout     = TimeSpan.FromMinutes(25);
                    client.BaseAddress = new UriBuilder(Uri.UriSchemeHttps, $"{region}.api.cognitive.microsoft.com", 443).Uri;
                    string path        = "speechtotext/v3.0/transcriptions";
                    client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", Environment.GetEnvironmentVariable("Ocp-Apim-Subscription-Key"));
                    string res             = Newtonsoft.Json.JsonConvert.SerializeObject(tc);
                    StringContent sc       = new StringContent(res);
                    sc.Headers.ContentType = JsonMediaTypeFormatter.DefaultMediaType;
                    using (var resp = await client.PostAsync(path, sc))
                    {
                        if (!resp.IsSuccessStatusCode)
                        {
                            throw new HttpRequestException("Failed to create  S2T transcription job");
                        }

                        IEnumerable <string> headerValues;
                        if (resp.Headers.TryGetValues(TranscriptionLocationHeaderKey, out headerValues))
                        {
                            if (headerValues.Any())
                            {
                                jobId = new Uri(headerValues.First());
                                outRecord.Data["jobId"] = jobId;
                            }
                        }
                    }



                    return(outRecord);
                });

                log.LogInformation(JsonConvert.SerializeObject(response));
                return(new OkObjectResult(response));
            }
            catch (Exception ex)
            {
                log.LogError(ex.Message);
                log.LogError(ex.StackTrace);
            }
            return(null);
        }