/// <summary> /// Update the first trail card with user details. /// </summary> /// <param name="scrumSummary">Scrum summary information to be shown on card.</param> /// <param name="scrum">Scrum details.</param> /// <param name="scrumMaster">Scrum master details.</param> /// <param name="membersActivityIdMap">Members id who are part of the scrum.</param> /// <param name="timeZone">Used to convert scrum start time as per specified time zone.</param> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param> /// <returns>A task of type bool where true represents summary card updated successfully while false indicates failure in updating the summary card.</returns> public async Task <bool> UpdateSummaryCardWithEndScrumAsync(ScrumSummary scrumSummary, Scrum scrum, ScrumMaster scrumMaster, Dictionary <string, string> membersActivityIdMap, string timeZone, ITurnContext turnContext, CancellationToken cancellationToken) { if (scrumSummary != null) { var activitySummary = MessageFactory.Attachment(ScrumCard.GetScrumStartCard(scrumSummary, membersActivityIdMap, scrumMaster?.ScrumMasterId, scrum?.ScrumStartActivityId, this.localizer, timeZone)); var teamsChannelInfo = await TeamsInfo.GetTeamChannelsAsync(turnContext, scrumMaster.TeamId, CancellationToken.None); var channelInfo = teamsChannelInfo.Where(channel => channel.Id.Equals(scrumMaster.ChannelId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (channelInfo != null) { activitySummary.Id = scrum.ScrumStartCardResponseId; activitySummary.Conversation = new ConversationAccount { Id = $"{scrumMaster.ChannelId};messageid={scrum.ScrumStartCardResponseId}", }; this.logger.LogInformation($"Trail card updated for: {scrum.ThreadConversationId} summaryCardActivityId: {scrum.ScrumStartCardResponseId}"); await turnContext?.UpdateActivityAsync(activitySummary, cancellationToken); } return(true); } else { this.logger.LogInformation($"No data obtained from storage to update summary card for summaryCardActivityId : {scrum?.ScrumStartCardResponseId}"); return(false); } }
/// <summary> /// Update the scrum summary card with updated details. /// </summary> /// <param name="scrumSummary">Scrum summary information to be shown on card.</param> /// <param name="summaryCardActivityId">Summary card activity id.</param> /// <param name="scrumTeamConfigId">Unique identifier for scrum team configuration details.</param> /// <param name="scrumStartActivityId">Scrum start card activity id</param> /// <param name="membersActivityIdMap">Members id who are part of the scrum.</param> /// <param name="timeZone">Used to convert scrum start time as per specified time zone.</param> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param> /// <returns>A task of type boolean where true represents summary card updated successfully while false indicates failure in updating the summary card.</returns> public async Task <bool> UpdateSummaryCardAsync(ScrumSummary scrumSummary, string summaryCardActivityId, string scrumTeamConfigId, string scrumStartActivityId, Dictionary <string, string> membersActivityIdMap, string timeZone, ITurnContext turnContext, CancellationToken cancellationToken) { var activitySummary = MessageFactory.Attachment(ScrumCard.GetScrumStartCard(scrumSummary, membersActivityIdMap, scrumTeamConfigId, scrumStartActivityId, this.localizer, timeZone)); activitySummary.Id = summaryCardActivityId; activitySummary.Conversation = turnContext?.Activity.Conversation; this.logger.LogInformation($"Trail card updated for: {turnContext.Activity.Conversation.Id} summaryCardActivityId: {summaryCardActivityId}"); await turnContext.UpdateActivityAsync(activitySummary, cancellationToken); return(true); }
/// <summary> /// Get scrum summary from table storage. /// </summary> /// <param name="scrumTeamConfigId">Scrum team configuration details describing scrum team name and channel id.</param> /// <param name="aadGroupId">Azure Active Directory group Id.</param> /// <param name="scrumCardResponseId">Scrum card response id.</param> /// <param name="membersActivityIdMap">Members id who are part of the scrum.</param> /// <returns>A task that represents the scrum summary data needs to be returned.</returns> public async Task <ScrumSummary> GetScrumSummaryAsync(string scrumTeamConfigId, string aadGroupId, string scrumCardResponseId = null, Dictionary <string, string> membersActivityIdMap = null) { var scrumSummary = new ScrumSummary(); var scrumConfiguration = await this.scrumConfigurationStorageProvider.GetScrumConfigurationDetailByScrumTeamConfigIdAsync(scrumTeamConfigId, aadGroupId); if (scrumConfiguration == null) { this.logger.LogInformation("scrum configuration details obtained from ScrumConfiguration table is null"); return(null); } if (string.IsNullOrEmpty(scrumCardResponseId)) { scrumSummary.ScrumName = string.IsNullOrEmpty(scrumConfiguration.ScrumTeamName) ? Strings.GeneralChannel : scrumConfiguration.ScrumTeamName; scrumSummary.ScrumStartTime = string.Format(CultureInfo.CurrentCulture, $"{{0:{ScrumStartDateTimeFormat}}}", DateTime.UtcNow); scrumSummary.TotalUserCount = scrumConfiguration.UserPrincipalNames.Split(',').Where(email => !string.IsNullOrEmpty(email)).Count(); scrumSummary.RespondedUserCount = 0; scrumSummary.BlockedUsersCount = 0; scrumSummary.ScrumRunningStatus = ScrumRunningStatus.Active; return(scrumSummary); } var scrum = (await this.scrumStorageProvider.GetScrumsBySummaryCardActivityIdAsync(scrumCardResponseId, scrumConfiguration.AadGroupId)).FirstOrDefault(); if (scrum == null) { this.logger.LogInformation("Scrum details obtained from Scrum table is null"); return(null); } var scrumStatus = (await this.scrumStatusStorageProvider.GetScrumStatusBySummaryCardIdAsync(scrumCardResponseId, scrumConfiguration.AadGroupId))?.ToList(); if (scrumStatus != null && scrumStatus.Count > 0) { scrumSummary.RespondedUserCount = scrumStatus.Count; scrumSummary.BlockedUsersCount = scrumStatus.Where(scrumDetails => !string.IsNullOrEmpty(scrumDetails.BlockerDescription) && !string.IsNullOrWhiteSpace(scrumDetails.BlockerDescription)).ToList().Count; } else { scrumSummary.RespondedUserCount = 0; scrumSummary.BlockedUsersCount = 0; } scrumSummary.ScrumName = string.IsNullOrEmpty(scrumConfiguration.ScrumTeamName) ? Strings.GeneralChannel : scrumConfiguration.ScrumTeamName; scrumSummary.ScrumStartTime = DateTimeOffset.Parse(scrum.CreatedOn, CultureInfo.InvariantCulture).ToString(ScrumStartDateTimeFormat, CultureInfo.InvariantCulture); scrumSummary.TotalUserCount = membersActivityIdMap == null ? 0 : membersActivityIdMap.Count; scrumSummary.ScrumRunningStatus = scrum.IsCompleted ? ScrumRunningStatus.Closed : ScrumRunningStatus.Active; return(scrumSummary); }
/// <summary> /// Get scrum start card when user clicks on update status button, end scrum button or scrum is started by background service. /// </summary> /// <param name="scrumSummary">Instance containing scrum related details.</param> /// <param name="membersActivityIdMap">Members id who are part of the scrum.</param> /// <param name="scrumMasterId">Unique identifier for scrum master details.</param> /// <param name="scrumStartActivityId">Scrum start card activity id. This will be used to refresh card if needed.</param> /// <param name="localizer">The current cultures' string localizer.</param> /// <param name="timeZone">Used to convert scrum start time as per specified time zone.</param> /// <returns>Scrum status update card attachment.</returns> public static Attachment GetScrumStartCard(ScrumSummary scrumSummary, Dictionary <string, string> membersActivityIdMap, string scrumMasterId, string scrumStartActivityId, IStringLocalizer <Strings> localizer, string timeZone) { string userSpecifiedDateTime = FormatDateStringToAdaptiveCardDateFormat(scrumSummary?.ScrumStartTime, timeZone); string scrumMembers = JsonConvert.SerializeObject(membersActivityIdMap); string isAnyUserResponded = scrumSummary.RespondedUserCount == 0 ? localizer.GetString("StartScrumCardWelcomeText") : string.Format(CultureInfo.CurrentCulture, localizer.GetString("StartScrumCardBlockedText"), scrumSummary.BlockedUsersCount); AdaptiveCard getScrumStartCard = new AdaptiveCard(Constants.AdaptiveCardVersion) { Body = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center, Width = AdaptiveColumnWidth.Auto, Items = new List <AdaptiveElement> { new AdaptiveTextBlock { HorizontalAlignment = AdaptiveHorizontalAlignment.Left, Text = scrumSummary.ScrumName, Weight = AdaptiveTextWeight.Bolder, Wrap = true, }, }, }, new AdaptiveColumn { VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center, Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { new AdaptiveTextBlock { HorizontalAlignment = AdaptiveHorizontalAlignment.Right, Wrap = true, Text = scrumSummary.ScrumRunningStatus == ScrumRunningStatus.Active ? localizer.GetString("ScrumRunningStatusActive") : localizer.GetString("ScrumRunningStatusClosed"), Color = scrumSummary.ScrumRunningStatus == ScrumRunningStatus.Active ? AdaptiveTextColor.Good : AdaptiveTextColor.Default, }, }, }, }, }, new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center, Width = AdaptiveColumnWidth.Auto, Items = new List <AdaptiveElement> { new AdaptiveTextBlock { HorizontalAlignment = AdaptiveHorizontalAlignment.Left, Wrap = true, Text = string.Format(CultureInfo.CurrentCulture, localizer.GetString("ScrumDetailsRespondedVsTotalUserStatus"), scrumSummary.RespondedUserCount, scrumSummary.TotalUserCount), }, }, }, new AdaptiveColumn { VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center, Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { new AdaptiveTextBlock { HorizontalAlignment = AdaptiveHorizontalAlignment.Right, Text = userSpecifiedDateTime, Wrap = true, }, }, }, }, }, new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { // Represents empty column required for alignment. }, }, }, }, new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { // Represents empty column required for alignment. }, }, }, }, new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Size = AdaptiveTextSize.Medium, Wrap = true, Text = isAnyUserResponded, HorizontalAlignment = AdaptiveHorizontalAlignment.Left, Color = scrumSummary.RespondedUserCount == 0 ? AdaptiveTextColor.Default : AdaptiveTextColor.Attention, Height = AdaptiveHeight.Stretch, Separator = true, }, }, Spacing = AdaptiveSpacing.Medium, }, }, }, new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { // Represents empty column required for alignment. }, }, }, }, new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { // Represents empty column required for alignment. }, }, }, }, }, }; var scrumDetailsAdaptiveSubmitAction = new AdaptiveSubmitAction() { Title = localizer.GetString("ScrumDetailsButtonText"), Data = new AdaptiveSubmitActionData { MsTeams = new CardAction { Type = Constants.TaskModuleFetchType, }, AdaptiveActionType = Constants.ScrumDetailsTaskModuleCommand, ScrumMasterId = scrumMasterId, ScrumStartActivityId = scrumStartActivityId, ScrumMembers = scrumMembers, }, }; if (scrumSummary.ScrumRunningStatus == ScrumRunningStatus.Active) { getScrumStartCard.Actions = new List <AdaptiveAction> { new AdaptiveSubmitAction() { Title = localizer.GetString("UpdateStatusButtonText"), Data = new AdaptiveSubmitActionData { MsTeams = new CardAction { Type = Constants.TaskModuleFetchType, }, AdaptiveActionType = Constants.UpdateStatusTaskModuleCommand, ScrumMasterId = scrumMasterId, ScrumStartActivityId = scrumStartActivityId, ScrumMembers = scrumMembers, }, }, scrumDetailsAdaptiveSubmitAction, new AdaptiveSubmitAction() { Title = localizer.GetString("EndScrumButtonText"), Data = new AdaptiveSubmitActionData { MsTeams = new CardAction { Type = ActionTypes.MessageBack, }, AdaptiveActionType = Constants.EndScrum, ScrumMasterId = scrumMasterId, ScrumStartActivityId = scrumStartActivityId, ScrumMembers = scrumMembers, }, }, }; } else { getScrumStartCard.Actions = new List <AdaptiveAction> { scrumDetailsAdaptiveSubmitAction, }; } var adaptiveCardAttachment = new Attachment() { ContentType = AdaptiveCard.ContentType, Content = getScrumStartCard, }; return(adaptiveCardAttachment); }
/// <summary> /// Get scrum details card for viewing all user status details. /// </summary> /// <param name="scrumStatuses">Scrum statuses details filled by members.</param> /// <param name="scrumSummary">Scrum related summary.</param> /// <param name="teamsChannels">Team channel information from which team roaster information to fetch.</param> /// <param name="applicationBasePath">Application base URL.</param> /// <param name="localizer">The current cultures' string localizer.</param> /// <param name="timeZone">Used to convert scrum start time as per specified time zone.</param> /// <returns>Returns scrum details card attachment.</returns> public static Attachment GetScrumDetailsCard(IEnumerable <ScrumStatus> scrumStatuses, ScrumSummary scrumSummary, IEnumerable <TeamsChannelAccount> teamsChannels, string applicationBasePath, IStringLocalizer <Strings> localizer, string timeZone) { string userSpecifiedDateTime = FormatDateStringToAdaptiveCardDateFormat(scrumSummary?.ScrumStartTime, timeZone); AdaptiveCard scrumDetailsCard = new AdaptiveCard(Constants.AdaptiveCardVersion) { Body = new List <AdaptiveElement> { new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center, Width = AdaptiveColumnWidth.Auto, Items = new List <AdaptiveElement> { new AdaptiveTextBlock { HorizontalAlignment = AdaptiveHorizontalAlignment.Left, Text = scrumSummary.ScrumName, Weight = AdaptiveTextWeight.Bolder, Wrap = true, }, }, }, new AdaptiveColumn { VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center, Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { new AdaptiveTextBlock { Color = scrumSummary.ScrumRunningStatus == ScrumRunningStatus.Active ? AdaptiveTextColor.Good : AdaptiveTextColor.Default, HorizontalAlignment = AdaptiveHorizontalAlignment.Right, Text = scrumSummary.ScrumRunningStatus == ScrumRunningStatus.Active ? localizer.GetString("ScrumRunningStatusActive") : localizer.GetString("ScrumRunningStatusClosed"), Wrap = true, }, }, }, }, }, new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center, Width = AdaptiveColumnWidth.Auto, Items = new List <AdaptiveElement> { new AdaptiveTextBlock { HorizontalAlignment = AdaptiveHorizontalAlignment.Left, Text = string.Format(CultureInfo.CurrentCulture, localizer.GetString("ScrumDetailsRespondedVsTotalUserStatus"), scrumSummary.RespondedUserCount, scrumSummary.TotalUserCount), Wrap = true, }, }, }, new AdaptiveColumn { VerticalContentAlignment = AdaptiveVerticalContentAlignment.Center, Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { new AdaptiveTextBlock { HorizontalAlignment = AdaptiveHorizontalAlignment.Right, Text = userSpecifiedDateTime, Wrap = true, }, }, }, }, }, new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { // Represents empty column required for alignment. }, }, }, }, new AdaptiveColumnSet { Columns = new List <AdaptiveColumn> { new AdaptiveColumn { Width = AdaptiveColumnWidth.Stretch, Items = new List <AdaptiveElement> { // Represents empty column required for alignment. }, }, }, }, }, }; List <AdaptiveElement> userScrumStatusList = GetAllUserScrumStatusCard(scrumStatuses, teamsChannels, applicationBasePath, localizer); scrumDetailsCard.Body.AddRange(userScrumStatusList); var scrumDetailsCardAttachment = new Attachment() { ContentType = AdaptiveCard.ContentType, Content = scrumDetailsCard, }; return(scrumDetailsCardAttachment); }