diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/Permissions/AIPermissionDefinitionProvider.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/Permissions/AIPermissionDefinitionProvider.cs index 6f292a277..051af6723 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/Permissions/AIPermissionDefinitionProvider.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/Permissions/AIPermissionDefinitionProvider.cs @@ -25,32 +25,32 @@ public override void Define(IPermissionDefinitionContext context) AIPermissions.Analysis.AnalysisDefault, L("Permission:AI.Analysis")); - analysisParent.AddChild( + var viewApplicationAnalysis = analysisParent.AddChild( AIPermissions.Analysis.ViewApplicationAnalysis, L("Permission:AI.Analysis.ViewApplicationAnalysis")) .RequireFeatures("Unity.AI.ApplicationAnalysis"); - analysisParent.AddChild( - AIPermissions.Analysis.ViewAttachmentSummary, - L("Permission:AI.Analysis.ViewAttachmentSummary")) - .RequireFeatures("Unity.AI.AttachmentSummaries"); - - analysisParent.AddChild( - AIPermissions.Analysis.ViewScoringResult, - L("Permission:AI.Analysis.ViewScoringResult")) - .RequireFeatures("Unity.AI.Scoring"); - - analysisParent.AddChild( + viewApplicationAnalysis.AddChild( AIPermissions.Analysis.GenerateApplicationAnalysis, L("Permission:AI.Analysis.GenerateApplicationAnalysis")) .RequireFeatures("Unity.AI.ApplicationAnalysis"); - analysisParent.AddChild( + var viewAttachmentSummary = analysisParent.AddChild( + AIPermissions.Analysis.ViewAttachmentSummary, + L("Permission:AI.Analysis.ViewAttachmentSummary")) + .RequireFeatures("Unity.AI.AttachmentSummaries"); + + viewAttachmentSummary.AddChild( AIPermissions.Analysis.GenerateAttachmentSummaries, L("Permission:AI.Analysis.GenerateAttachmentSummaries")) .RequireFeatures("Unity.AI.AttachmentSummaries"); - analysisParent.AddChild( + var viewScoringResult = analysisParent.AddChild( + AIPermissions.Analysis.ViewScoringResult, + L("Permission:AI.Analysis.ViewScoringResult")) + .RequireFeatures("Unity.AI.Scoring"); + + viewScoringResult.AddChild( AIPermissions.Analysis.GenerateScoring, L("Permission:AI.Analysis.GenerateScoring")) .RequireFeatures("Unity.AI.Scoring"); diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/Attachments/AttachmentSummaryAppService.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/Attachments/AttachmentSummaryAppService.cs index 511669df4..4f57fd483 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/Attachments/AttachmentSummaryAppService.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/Attachments/AttachmentSummaryAppService.cs @@ -10,7 +10,7 @@ namespace Unity.GrantManager.Attachments; -[Authorize(AIPermissions.Analysis.ViewAttachmentSummary)] +[Authorize(AIPermissions.Analysis.GenerateAttachmentSummaries)] [ExposeServices(typeof(AttachmentSummaryAppService), typeof(IAttachmentSummaryAppService))] public class AttachmentSummaryAppService( IAttachmentSummaryService attachmentSummaryService, diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/GrantApplications/ApplicationAnalysisAppService.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/GrantApplications/ApplicationAnalysisAppService.cs index 84a88ff8e..fad67fc53 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/GrantApplications/ApplicationAnalysisAppService.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/GrantApplications/ApplicationAnalysisAppService.cs @@ -9,7 +9,7 @@ namespace Unity.GrantManager.GrantApplications; -[Authorize(AIPermissions.Analysis.ViewApplicationAnalysis)] +[Authorize(AIPermissions.Analysis.GenerateApplicationAnalysis)] public class ApplicationAnalysisAppService( IApplicationAnalysisService applicationAnalysisService, IFeatureChecker featureChecker) diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/GrantApplications/ApplicationScoringAppService.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/GrantApplications/ApplicationScoringAppService.cs index 811295d4d..329a1ee2c 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/GrantApplications/ApplicationScoringAppService.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/GrantApplications/ApplicationScoringAppService.cs @@ -9,7 +9,7 @@ namespace Unity.GrantManager.GrantApplications; -[Authorize(AIPermissions.Analysis.ViewScoringResult)] +[Authorize(AIPermissions.Analysis.GenerateScoring)] public class ApplicationScoringAppService( IApplicationScoringService applicationScoringService, IFeatureChecker featureChecker) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Handlers/QueueApplicationAIPipelineOnProcessHandler.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Handlers/QueueApplicationAIPipelineOnProcessHandler.cs index f8261b66f..6bb6e3702 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Handlers/QueueApplicationAIPipelineOnProcessHandler.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Handlers/QueueApplicationAIPipelineOnProcessHandler.cs @@ -1,14 +1,21 @@ using Microsoft.Extensions.Logging; using System; using System.Threading.Tasks; +using Unity.AI.Settings; +using Unity.GrantManager.Applications; using Unity.GrantManager.GrantApplications.Automation.BackgroundJobs; using Unity.GrantManager.Intakes.Events; using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; +using Volo.Abp.Settings; + namespace Unity.GrantManager.GrantApplications.Automation.Handlers; + public class QueueApplicationAIPipelineOnProcessHandler( IBackgroundJobManager backgroundJobManager, + ISettingProvider settingProvider, + IApplicationFormRepository applicationFormRepository, ILogger logger) : ILocalEventHandler, ITransientDependency { public async Task HandleEventAsync(ApplicationProcessEvent eventData) @@ -18,6 +25,21 @@ public async Task HandleEventAsync(ApplicationProcessEvent eventData) logger.LogWarning("Event data or application is null in QueueApplicationAIPipelineOnProcessHandler."); return; } + + var automaticGenerationEnabled = await settingProvider.GetAsync(AISettings.AutomaticGenerationEnabled, defaultValue: false); + if (!automaticGenerationEnabled) + { + logger.LogDebug("Automatic AI generation is disabled at tenant level, skipping intake pipeline for application {ApplicationId}.", eventData.Application.Id); + return; + } + + var applicationForm = await applicationFormRepository.GetAsync(eventData.Application.ApplicationFormId); + if (!applicationForm.AutomaticallyGenerateAIAnalysis) + { + logger.LogDebug("Automatic AI analysis is disabled at form level for application {ApplicationId}, skipping intake pipeline.", eventData.Application.Id); + return; + } + try { await backgroundJobManager.EnqueueAsync(new RunApplicationAIPipelineJobArgs diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml index f364808bf..0c9773fa8 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml @@ -2,7 +2,9 @@ @* #pragma warning disable S1128 *@ @using Microsoft.Extensions.Localization @using Unity.AI.Permissions +@using Unity.AI.Settings @using Unity.Flex.Web.Views.Shared.Components.WorksheetInstanceWidget +@using Unity.GrantManager.Applications @using Unity.GrantManager.Flex @using Unity.GrantManager.Localization @using Unity.GrantManager.Web.Pages.GrantApplications @@ -14,6 +16,7 @@ @using Volo.Abp.Authorization.Permissions @using Volo.Abp.Features @using Volo.Abp.MultiTenancy +@using Volo.Abp.Settings @* #pragma warning restore S1128 *@ @@ -23,18 +26,29 @@ @inject IFeatureChecker FeatureChecker @inject IPermissionChecker PermissionChecker @inject ICurrentTenant CurrentTenant +@inject ISettingProvider SettingProvider +@inject IApplicationFormRepository ApplicationFormRepository @{ PageLayout.Content.Title = L["Grants"].Value; var notificationsFeatureEnabled = await FeatureChecker.IsEnabledAsync("Unity.Notifications"); var readEmailGranted = await PermissionChecker.IsGrantedAsync("Notifications.Email"); - var aiAttachmentSummariesEnabled = await FeatureChecker.IsEnabledAsync("Unity.AI.AttachmentSummaries") - && await PermissionChecker.IsGrantedAsync(AIPermissions.Analysis.ViewAttachmentSummary); + var flexFeatureEnabled = await FeatureChecker.IsEnabledAsync("Unity.Flex"); + + // View guards — control tab visibility and loading of existing AI results var aiApplicationAnalysisEnabled = await FeatureChecker.IsEnabledAsync("Unity.AI.ApplicationAnalysis") && await PermissionChecker.IsGrantedAsync(AIPermissions.Analysis.ViewApplicationAnalysis); - var aiScoringEnabled = await FeatureChecker.IsEnabledAsync("Unity.AI.Scoring") - && await PermissionChecker.IsGrantedAsync(AIPermissions.Analysis.ViewScoringResult); - var flexFeatureEnabled = await FeatureChecker.IsEnabledAsync("Unity.Flex"); + + // Shared manual-generation guards + var tenantManualEnabled = await SettingProvider.GetAsync(AISettings.ManualGenerationEnabled, defaultValue: false); + var applicationForm = await ApplicationFormRepository.GetAsync(Model.ApplicationFormId); + var formManualEnabled = applicationForm.ManuallyInitiateAIAnalysis; + + // Generate guard — controls visibility of the Generate button in the AI Analysis tab + var aiApplicationAnalysisGenerateEnabled = await FeatureChecker.IsEnabledAsync("Unity.AI.ApplicationAnalysis") + && tenantManualEnabled + && formManualEnabled + && await PermissionChecker.IsGrantedAsync(AIPermissions.Analysis.GenerateApplicationAnalysis); } @section styles { @@ -72,7 +86,6 @@ - @functions { @@ -153,7 +166,7 @@
- @await Component.InvokeAsync("ReviewList") + @await Component.InvokeAsync("ReviewList", new { applicationFormId = Model.ApplicationFormId })
@@ -265,15 +278,15 @@ - - @if (aiApplicationAnalysisEnabled) - { - - } + + @if (aiApplicationAnalysisEnabled) + { + + } @if (Model.IsDevPromptControlsEnabled) {