using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Dbas; using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; using System; using System.Collections.Generic; using System.Linq; namespace Saxonite.Net.Standard.Base.Consoles { //======================================================================= // The index data is intended to provide a common, performance effective route // to identifying the activities that are relevant to a User or other context // that defines at least one region/sport combination and an effective date (usually today). //======================================================================= public abstract class SaxActivityConsole : SaxDisposableStateItem { //------------------------------------------------------------------- // Definitions. /// /// Parameters required to query the index for activities to identify relevant activities. /// public class IndexContext { // ToDo Include Localiser. /// /// Index Context - high level activity filter. /// User Account allows User specific information about the activities to be included. /// public IndexContext(SysId userAccountId) { UserAccountId = userAccountId; } public SysId UserAccountId { get; } /// Activities must match one of the sport/region combinations specified. public List SportRegionIdsIncluded { get; } = new List(); /// /// Owner Users - eg. journalist for articles. /// Allows the users' activities to be queried without relying on matching sport/region combinations. /// public List UserAccountIdsOwner { get; } = new List(); /// /// Owner Organisations - for articles, events and opportunities. /// Allows the organisations' own activities to be queried without relying on matching sport/region combinations. /// public List OrganisationIdsOwner { get; } = new List(); // Todo: Default effective period for Activities. public DateTime EffectiveFrom { get; set; } = DateUtil.IncrementDate(DateTime.Today, -365); public DateTime EffectiveTo { get; set; } = DateUtil.IncrementDate(DateTime.Today, 30); public DateTime NotExpiredOn { get; set; } = DateTime.Today; public bool IsValid() { // Context must contain at least a sport, a region and a valid effective period. if (!SportRegionIdsIncluded.Exists(item => SysId.IsValid(item.SportId) && SysId.IsValid(item.RegionId)) && !UserAccountIdsOwner.HasValidIds() && !OrganisationIdsOwner.HasValidIds()) return false; if (!DateUtil.IsValid(EffectiveFrom) || EffectiveTo < EffectiveFrom) return false; return true; } public DbQuery.Paging Paging { get; } = new DbQuery.Paging(); } /// /// Activities identified by querying index with context. /// public class IndexResults { public class ActivityData { public ActivityData(ItemId itemId, DateTime effectiveOn) { ItemId = itemId; EffectiveOn = effectiveOn; } public ItemId ItemId { get; } public DateTime EffectiveOn { get; } // Organisation. public SysId OrganisationId { get; set; } public String Organisation { get; set; } public bool OrganisationHasLogo { get; set; } // Author. public SysId UserAccountIdAuthor { get; set; } public String Author { get; set; } // Activity details. public String Caption { get; protected set; } public String Blurb { get; protected set; } // Thumbnail. public SysId ItemImageIdThumbnail { get; set; } // Sport. public SysId SportId { get; protected set; } public String SportName { get; protected set; } // Region. public SysId RegionId { get; protected set; } public String RegionName { get; protected set; } // User Activity. public DateTime LastListedAt { get; protected set; } public DateTime LastViewedAt { get; protected set; } public Sax.ActivityReaction Reaction { get; protected set; } public DateTime LastReactedAt { get; protected set; } } public List Items { get; } = new List(); } //------------------------------------------------------------------- // Data members. private SaxInternals internals = null; private readonly List validActivityItemTypes = new List() { Sax.DataItemType.Article, Sax.DataItemType.OrganisationEvent, Sax.DataItemType.OrganisationOpportunity, }; //------------------------------------------------------------------- // Protected construction. protected SaxActivityConsole(SaxSessionContext sessionContext) { SessionContext = sessionContext; } protected override void Dispose(bool disposing) { if (disposing && internals != null) internals.Dispose(); internals = null; base.Dispose(disposing); } //------------------------------------------------------------------- // Properties. protected SaxSessionContext SessionContext { get; } protected SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } } protected ISacsDatabase CoreDatabase { get { return Internals.CoreDatabase; } } protected List ValidActivityItemTypes { get { return validActivityItemTypes.ToList(); } } //------------------------------------------------------------------- // Query Activity Index Entries for User Context. //------------------------------------------------------------------- public abstract IndexResults QueryIndex(IndexContext context); //------------------------------------------------------------------- // Store Activity Index for Specific activities. //------------------------------------------------------------------- /// /// Store the Activity Index for an Article. /// public bool StoreActivityIndex(SaxArticle article, DbUpdateInfo dbUpdateInfo) { ClearErrorState(); // Determine whether article should be included in the Activity Index. bool includeInActivityIndex = true; // Only published articles have an activity index. if (!DateUtil.IsValid(article.PublishedAt)) includeInActivityIndex = false; // Only active articles have an activity index. if (!article.IsStatusActive) includeInActivityIndex = false; // NB There will be other rules as well, eg. whether expired. if (includeInActivityIndex) { // Activity Index properties. IndexData indexData = new IndexData(article.GetItemId()); indexData.UserAccountIdOwner = article.UserAccountIdAuthor; indexData.OrganisationIdOwner = article.OrganisationId; indexData.SportIds.Add(article.SportIdPrimary); indexData.RegionIds.Add(article.RegionIdPrimary); indexData.EffectiveOn = DateUtil.OnInvalid(article.PublishedAt, article.CreateStamp.On).Date; return StoreActivityIndexData(indexData, dbUpdateInfo); } return ClearActivityIndex(article.GetItemId(), dbUpdateInfo); } public bool ClearActivityIndex(SaxArticle article, DbUpdateInfo dbUpdateInfo) { return ClearActivityIndex(article.GetItemId(), dbUpdateInfo); } //------------------------------------------------------------------- /// /// Store the Activity Index for a Organisation Event. /// public bool StoreActivityIndex(SaxOrganisationEvent organisationEvent, DbUpdateInfo dbUpdateInfo) { ClearErrorState(); // Only active opportunities have an activity index. if (!organisationEvent.IsStatusActive) ClearActivityIndex(organisationEvent.GetItemId(), dbUpdateInfo); // NB There will be other rules as well, eg. whether published. // Activity Index properties. IndexData indexData = new IndexData(organisationEvent.GetItemId()); indexData.OrganisationIdOwner = organisationEvent.OrganisationId; indexData.SportIds.Add(organisationEvent.SportIdPrimary); indexData.RegionIds.Add(organisationEvent.RegionIdCountry); indexData.EffectiveOn = organisationEvent.EventAt.Date; indexData.ExpiresOn = DateUtil.OnInvalid(organisationEvent.ExpiresAt, DateUtil.IncrementDate(DateUtil.OnInvalid(organisationEvent.EventTo, organisationEvent.EventAt), 2)).Date; return StoreActivityIndexData(indexData, dbUpdateInfo); } public bool ClearActivityIndex(SaxOrganisationEvent organisationEvent, DbUpdateInfo dbUpdateInfo) { return ClearActivityIndex(organisationEvent.GetItemId(), dbUpdateInfo); } //------------------------------------------------------------------- /// /// Store the Activity Index for a Organisation Opportunity. /// public bool StoreActivityIndex(SaxOrganisationOpportunity opportunity, DbUpdateInfo dbUpdateInfo) { ClearErrorState(); // Only active opportunities have an activity index. if (!opportunity.IsStatusActive) ClearActivityIndex(opportunity.GetItemId(), dbUpdateInfo); // NB There will be other rules as well, eg. whether published. // Activity Index properties. IndexData indexData = new IndexData(opportunity.GetItemId()); indexData.OrganisationIdOwner = opportunity.OrganisationId; indexData.SportIds.Add(opportunity.SportIdPrimary); indexData.RegionIds.Add(opportunity.RegionIdPrimary); indexData.EffectiveOn = DateUtil.OnInvalid(opportunity.PublishedAt, opportunity.CreateStamp.On).Date; indexData.ExpiresOn = DateUtil.OnInvalid(opportunity.ExpiresAt, DateUtil.IncrementDate(indexData.EffectiveOn, 28)).Date; return StoreActivityIndexData(indexData, dbUpdateInfo); } public bool ClearActivityIndex(SaxOrganisationOpportunity opportunity, DbUpdateInfo dbUpdateInfo) { return ClearActivityIndex(opportunity.GetItemId(), dbUpdateInfo); } //------------------------------------------------------------------- // Store Activity Index. //------------------------------------------------------------------- /// Data values that make up the index for an activity. protected class IndexData { public IndexData(ItemId itemId) { ItemId = itemId; } public ItemId ItemId { get; } public SysId UserAccountIdOwner { get; set; } public SysId OrganisationIdOwner { get; set; } public List SportIds { get; } = new List(); public List RegionIds { get; } = new List(); public DateTime EffectiveOn { get; set; } public DateTime ExpiresOn { get; set; } public bool IsValid() { if (!ItemId.IsValid(ItemId)) return false; if (!SportIds.HasValidIds()) return false; if (!RegionIds.HasValidIds()) return false; if (!DateUtil.IsValid(EffectiveOn)) return false; return true; } } /// Single entry in the index for an activity. protected class IndexEntryData { public IndexEntryData(ItemId itemId, SysId userAccountIdOwner, SysId organisationIdOwner, SysId sportId, SysId regionId, DateTime effectiveOn, DateTime expiresOn) { ItemId = itemId; UserAccountIdOwner = userAccountIdOwner; OrganisationIdOwner = organisationIdOwner; SportId = sportId; RegionId = regionId; EffectiveOn = effectiveOn; ExpiresOn = expiresOn; } public ItemId ItemId { get; } public SysId UserAccountIdOwner { get; } public SysId OrganisationIdOwner { get; } public SysId SportId { get; } public SysId RegionId { get; } public DateTime EffectiveOn { get; } public DateTime? ExpiresOn { get; } public bool IsValid() { if (!ItemId.IsValid(ItemId)) return false; if (!SysId.IsValid(SportId)) return false; if (!SysId.IsValid(RegionId)) return false; if (!DateUtil.IsValid(EffectiveOn)) return false; return true; } /// Compare index entry Ids - Item, Sport & Region. public bool MatchesIds(SaxActivityIndex activityIndex) { if (!ItemId.Equals(activityIndex.ItemId)) return false; if (!SportId.Equals(activityIndex.SportId)) return false; if (!RegionId.Equals(activityIndex.RegionId)) return false; return true; } /// Compare index entry Ids - Item, Sport & Region - and effective date. public bool Matches(SaxActivityIndex activityIndex) { if (!MatchesIds(activityIndex)) return false; if (!EffectiveOn.Equals(activityIndex.EffectiveOn)) return false; return true; } } //------------------------------------------------------------------- // Activity Index data. protected abstract bool StoreActivityIndexData(IndexData indexData, DbUpdateInfo dbUpdateInfo); protected abstract bool ClearActivityIndex(ItemId itemId, DbUpdateInfo dbUpdateInfo); //------------------------------------------------------------------- // Activity Index Utilities. protected List GetEntriesForIndex(IndexData indexData) { ClearErrorState(); List indexEntries = new List(); if (ItemId.IsValid(indexData.ItemId) && DateUtil.IsValid(indexData.EffectiveOn)) { foreach (SysId sportId in indexData.SportIds.FindAll(item => SysId.IsValid(item))) { foreach (SysId regionId in indexData.RegionIds.FindAll(item => SysId.IsValid(item))) { IndexEntryData indexEntry = new IndexEntryData(indexData.ItemId, indexData.UserAccountIdOwner, indexData.OrganisationIdOwner, sportId, regionId, indexData.EffectiveOn, indexData.ExpiresOn); if (indexEntry.IsValid()) indexEntries.Add(indexEntry); } } } return indexEntries; } //------------------------------------------------------------------- // User Activities //------------------------------------------------------------------- public abstract bool RecordUserActivitiesListed(SysId userAccountId, List activityItemIds, DbUpdateInfo dbUpdateInfo); public abstract bool RecordUserActivityViewed(SysId userAccountId, ItemId activityItemId, DbUpdateInfo dbUpdateInfo); public abstract bool RecordUserActivityReaction(SysId userAccountId, ItemId activityItemId, Sax.ActivityReaction activityReaction, DbUpdateInfo dbUpdateInfo); //------------------------------------------------------------------- } //======================================================================= }