From f2cfb8b2875ee62c6c1b79b87d9b1d25c22234c7 Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg Date: Fri, 3 Dec 2021 14:19:55 +0100 Subject: [PATCH 01/22] Architecture --- API/Controllers/AlgorithmController.cs | 20 ++++++++++ Models/Project.cs | 2 + Services/Services/ActivityAlgorithmService.cs | 37 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 API/Controllers/AlgorithmController.cs create mode 100644 Services/Services/ActivityAlgorithmService.cs diff --git a/API/Controllers/AlgorithmController.cs b/API/Controllers/AlgorithmController.cs new file mode 100644 index 00000000..c200f45f --- /dev/null +++ b/API/Controllers/AlgorithmController.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Models.Defaults; +using System.Threading.Tasks; + +namespace API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class AlgorithmController : ControllerBase + { + [HttpPost("Activity")] + [Authorize(Policy = nameof(Defaults.Roles.BackendApplication))] + public async Task ActivityAlgorithm() + { + return Ok("Test"); + } + } +} diff --git a/Models/Project.cs b/Models/Project.cs index 87878628..2a6a3c4f 100644 --- a/Models/Project.cs +++ b/Models/Project.cs @@ -82,6 +82,8 @@ public Project() public List Images { get; set; } + public double ActivityScore { get; set; } + /// /// Checks if the user can access the project based on diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs new file mode 100644 index 00000000..957587fa --- /dev/null +++ b/Services/Services/ActivityAlgorithmService.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Models; + +namespace Services.Services +{ + public interface IActivityAlgorithmService + { + List CalculateAllProjects(); + double CalculateProjectActivityScore(Project project); + bool SetProjectActivityScore(Project project, double score); + + } + public class ActivityAlgorithmService : IActivityAlgorithmService + { + public ActivityAlgorithmService() + { + + } + + public List CalculateAllProjects() + { + throw new NotImplementedException(); + } + + public double CalculateProjectActivityScore(Project project) + { + throw new NotImplementedException(); + } + + public bool SetProjectActivityScore(Project project, double score) + { + throw new NotImplementedException(); + } + } +} From 9e60b3c421762f7a28c006805bc6c3f7057e2593 Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Mon, 6 Dec 2021 09:22:11 +0100 Subject: [PATCH 02/22] feat: algorithm example --- API/Controllers/AlgorithmController.cs | 9 ++- JobScheduler/ApiRequestHandler.cs | 18 +++--- Services/Services/ActivityAlgorithmService.cs | 61 ++++++++++++++++--- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/API/Controllers/AlgorithmController.cs b/API/Controllers/AlgorithmController.cs index c200f45f..3f653182 100644 --- a/API/Controllers/AlgorithmController.cs +++ b/API/Controllers/AlgorithmController.cs @@ -1,7 +1,11 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Models; using Models.Defaults; +using Repositories; +using Services.Services; +using System.Collections.Generic; using System.Threading.Tasks; namespace API.Controllers @@ -12,9 +16,10 @@ public class AlgorithmController : ControllerBase { [HttpPost("Activity")] [Authorize(Policy = nameof(Defaults.Roles.BackendApplication))] - public async Task ActivityAlgorithm() + public async Task ActivityAlgorithm(ActivityAlgorithmService activityAlgorithmService, ProjectRepository projectRepository) { - return Ok("Test"); + IEnumerable projects = activityAlgorithmService.CalculateAllProjects(await projectRepository.GetAll()); + return Ok(projects); } } } diff --git a/JobScheduler/ApiRequestHandler.cs b/JobScheduler/ApiRequestHandler.cs index 85cb3688..12ab0408 100644 --- a/JobScheduler/ApiRequestHandler.cs +++ b/JobScheduler/ApiRequestHandler.cs @@ -1,16 +1,16 @@ /* * Digital Excellence Copyright (C) 2020 Brend Smits -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. -* -* You can find a copy of the GNU Lesser General Public License +* +* You can find a copy of the GNU Lesser General Public License * along with this program, in the LICENSE.md file in the root project directory. * If not, see https://www.gnu.org/licenses/lgpl-3.0.txt */ diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index 957587fa..4e21ac82 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -1,37 +1,84 @@ +using Microsoft.VisualBasic.CompilerServices; using System; using System.Collections.Generic; using System.Text; using Models; +using Repositories; +using System.Linq; +using System.Threading.Tasks; namespace Services.Services { public interface IActivityAlgorithmService { - List CalculateAllProjects(); + IEnumerable CalculateAllProjects(IEnumerable projects); double CalculateProjectActivityScore(Project project); bool SetProjectActivityScore(Project project, double score); } public class ActivityAlgorithmService : IActivityAlgorithmService { - public ActivityAlgorithmService() - { + private readonly List dataPoints; + private readonly ProjectRepository projectRepo; + + public ActivityAlgorithmService(ProjectRepository projectRepo) + { + this.projectRepo = projectRepo; + //List all data points wat are required to calculate each projects activity score + dataPoints = new List() + { + new LikeDataPoint() + }; } - public List CalculateAllProjects() + public IEnumerable CalculateAllProjects(IEnumerable projects) { - throw new NotImplementedException(); + foreach(Project project in projects.ToList()) + { + double score = this.CalculateProjectActivityScore(project); + this.SetProjectActivityScore(project, score); + } + return projects; } public double CalculateProjectActivityScore(Project project) { - throw new NotImplementedException(); + double score = 0; + foreach(IActivityAlgorithmDataPoint dataPoint in this.dataPoints) + { + score += dataPoint.Calculate(project); + } + return score; + + //Is this better? + // return this._dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); } public bool SetProjectActivityScore(Project project, double score) { - throw new NotImplementedException(); + project.ActivityScore = score; + //TODO database implementation + //TODO Elastic search implementation + return true; } } + + internal interface IActivityAlgorithmDataPoint + { + + public double Calculate(Project project); + + } + + + internal class LikeDataPoint : IActivityAlgorithmDataPoint + { + + public double Calculate(Project project) + { + return project.Likes.Count; + } + + } } From d64c2b11eeedd89a8aa39729ab7b2899f427a543 Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg <73314842+BartvanEijkelenburg@users.noreply.github.com> Date: Mon, 6 Dec 2021 12:12:35 +0100 Subject: [PATCH 03/22] Added DataPoints --- API/Controllers/AlgorithmController.cs | 33 +- .../DependencyInjectionExtensions.cs | 2 + ...dedActivityScoreToProjectModel.Designer.cs | 797 ++++++++++++++++++ ...093905_AddedActivityScoreToProjectModel.cs | 23 + .../ApplicationDbContextModelSnapshot.cs | 3 + .../ActivityAlgorithmDataPointService.cs | 50 ++ Services/Services/ActivityAlgorithmService.cs | 59 +- 7 files changed, 929 insertions(+), 38 deletions(-) create mode 100644 Data/Migrations/20211206093905_AddedActivityScoreToProjectModel.Designer.cs create mode 100644 Data/Migrations/20211206093905_AddedActivityScoreToProjectModel.cs create mode 100644 Services/Services/ActivityAlgorithmDataPointService.cs diff --git a/API/Controllers/AlgorithmController.cs b/API/Controllers/AlgorithmController.cs index 3f653182..f429ac6b 100644 --- a/API/Controllers/AlgorithmController.cs +++ b/API/Controllers/AlgorithmController.cs @@ -6,20 +6,45 @@ using Repositories; using Services.Services; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace API.Controllers { + /// + /// This class is responsible for handling HTTP requests that are related + /// to DeX algorithms, for example activating the project ActivityAlgorythm + /// [Route("api/[controller]")] [ApiController] public class AlgorithmController : ControllerBase { + + private readonly IActivityAlgorithmService activityAlgorithmService; + private readonly IProjectRepository projectRepository; + + /// + /// Initializes a new instance of the class + /// + /// The category service which is used to communicate with the logic layer. + /// The project category service which is used to communicate with the logic layer. + public AlgorithmController(IActivityAlgorithmService activityAlgorithmService, IProjectRepository projectRepository) + { + this.activityAlgorithmService = activityAlgorithmService; + this.projectRepository = projectRepository; + } + + /// + /// This endpoint initiates the Activity Algorithm. + /// + /// HttpStatusCode [HttpPost("Activity")] - [Authorize(Policy = nameof(Defaults.Roles.BackendApplication))] - public async Task ActivityAlgorithm(ActivityAlgorithmService activityAlgorithmService, ProjectRepository projectRepository) + public async Task ActivityAlgorithm() { - IEnumerable projects = activityAlgorithmService.CalculateAllProjects(await projectRepository.GetAll()); - return Ok(projects); + return Ok(activityAlgorithmService.CalculateAllProjects( + await projectRepository.GetAllWithUsersCollaboratorsAndInstitutionsAsync()) + .OrderByDescending(p => p.ActivityScore).ToList()); + } } } diff --git a/API/Extensions/DependencyInjectionExtensions.cs b/API/Extensions/DependencyInjectionExtensions.cs index 902bf405..500e61a9 100644 --- a/API/Extensions/DependencyInjectionExtensions.cs +++ b/API/Extensions/DependencyInjectionExtensions.cs @@ -128,6 +128,8 @@ public static IServiceCollection AddServicesAndRepositories(this IServiceCollect services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddExternalDataSources(); return services; diff --git a/Data/Migrations/20211206093905_AddedActivityScoreToProjectModel.Designer.cs b/Data/Migrations/20211206093905_AddedActivityScoreToProjectModel.Designer.cs new file mode 100644 index 00000000..679e069a --- /dev/null +++ b/Data/Migrations/20211206093905_AddedActivityScoreToProjectModel.Designer.cs @@ -0,0 +1,797 @@ +// +using System; +using Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace _4_Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20211206093905_AddedActivityScoreToProjectModel")] + partial class AddedActivityScoreToProjectModel + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Models.CallToAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("OptionValue") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("CallToAction"); + }); + + modelBuilder.Entity("Models.CallToActionOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Type") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("CallToActionOption"); + }); + + modelBuilder.Entity("Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Category"); + }); + + modelBuilder.Entity("Models.Collaborator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("FullName") + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("Role") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("Collaborators"); + }); + + modelBuilder.Entity("Models.DataSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Guid") + .HasColumnType("nvarchar(max)"); + + b.Property("IconId") + .HasColumnType("int"); + + b.Property("IsVisible") + .HasColumnType("bit"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("IconId"); + + b.ToTable("DataSource"); + }); + + modelBuilder.Entity("Models.DataSourceWizardPage", b => + { + b.Property("DataSourceId") + .HasColumnType("int"); + + b.Property("WizardPageId") + .HasColumnType("int"); + + b.Property("AuthFlow") + .HasColumnType("bit"); + + b.Property("OrderIndex") + .HasColumnType("int"); + + b.HasKey("DataSourceId", "WizardPageId", "AuthFlow"); + + b.HasIndex("WizardPageId"); + + b.ToTable("DataSourceWizardPage"); + }); + + modelBuilder.Entity("Models.EmbeddedProject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Guid") + .HasColumnType("uniqueidentifier"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UserId"); + + b.ToTable("EmbeddedProject"); + }); + + modelBuilder.Entity("Models.File", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("UploadDateTime") + .HasColumnType("datetime2"); + + b.Property("UploaderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UploaderId"); + + b.ToTable("File"); + }); + + modelBuilder.Entity("Models.Highlight", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("ProjectId"); + + b.ToTable("Highlight"); + }); + + modelBuilder.Entity("Models.Institution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("IdentityId") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Institution"); + }); + + modelBuilder.Entity("Models.Project", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ActivityScore") + .HasColumnType("float"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutePrivate") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectIconId") + .HasColumnType("int"); + + b.Property("ShortDescription") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Updated") + .HasColumnType("datetime2"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectIconId"); + + b.HasIndex("UserId"); + + b.ToTable("Project"); + }); + + modelBuilder.Entity("Models.ProjectCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("ProjectId"); + + b.ToTable("ProjectCategory"); + }); + + modelBuilder.Entity("Models.ProjectInstitution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("InstitutionId") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId"); + + b.HasIndex("ProjectId"); + + b.ToTable("ProjectInstitution"); + }); + + modelBuilder.Entity("Models.ProjectLike", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("LikedProjectId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("LikedProjectId"); + + b.HasIndex("UserId"); + + b.ToTable("ProjectLike"); + }); + + modelBuilder.Entity("Models.ProjectTransferRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CurrentOwnerAcceptedRequest") + .HasColumnType("bit"); + + b.Property("PotentialNewOwnerAcceptedRequest") + .HasColumnType("bit"); + + b.Property("PotentialNewOwnerId") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TransferGuid") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("PotentialNewOwnerId"); + + b.HasIndex("ProjectId"); + + b.ToTable("ProjectTransferRequest"); + }); + + modelBuilder.Entity("Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Role"); + }); + + modelBuilder.Entity("Models.RoleScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Scope") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleScope"); + }); + + modelBuilder.Entity("Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccountCreationDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ExpectedGraduationDate") + .HasColumnType("datetime2"); + + b.Property("IdentityId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionId") + .HasColumnType("int"); + + b.Property("IsPublic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProfileUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId"); + + b.HasIndex("RoleId"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Models.UserProject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UserId"); + + b.ToTable("UserProject"); + }); + + modelBuilder.Entity("Models.UserTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserTask"); + }); + + modelBuilder.Entity("Models.UserUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("FollowedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FollowedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("UserUser"); + }); + + modelBuilder.Entity("Models.WizardPage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("WizardPage"); + }); + + modelBuilder.Entity("Models.CallToAction", b => + { + b.HasOne("Models.Project", null) + .WithMany("CallToActions") + .HasForeignKey("ProjectId"); + }); + + modelBuilder.Entity("Models.Collaborator", b => + { + b.HasOne("Models.Project", null) + .WithMany("Collaborators") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.DataSource", b => + { + b.HasOne("Models.File", "Icon") + .WithMany() + .HasForeignKey("IconId"); + }); + + modelBuilder.Entity("Models.DataSourceWizardPage", b => + { + b.HasOne("Models.DataSource", "DataSource") + .WithMany("DataSourceWizardPages") + .HasForeignKey("DataSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.WizardPage", "WizardPage") + .WithMany("DataSourceWizardPages") + .HasForeignKey("WizardPageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.EmbeddedProject", b => + { + b.HasOne("Models.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.File", b => + { + b.HasOne("Models.Project", null) + .WithMany("Images") + .HasForeignKey("ProjectId"); + + b.HasOne("Models.User", "Uploader") + .WithMany() + .HasForeignKey("UploaderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.Highlight", b => + { + b.HasOne("Models.File", "Image") + .WithMany() + .HasForeignKey("ImageId"); + + b.HasOne("Models.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.Project", b => + { + b.HasOne("Models.File", "ProjectIcon") + .WithMany() + .HasForeignKey("ProjectIconId"); + + b.HasOne("Models.User", "User") + .WithMany("Projects") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.ProjectCategory", b => + { + b.HasOne("Models.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.Project", "Project") + .WithMany("Categories") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.ProjectInstitution", b => + { + b.HasOne("Models.Institution", "Institution") + .WithMany() + .HasForeignKey("InstitutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.Project", "Project") + .WithMany("LinkedInstitutions") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.ProjectLike", b => + { + b.HasOne("Models.Project", "LikedProject") + .WithMany("Likes") + .HasForeignKey("LikedProjectId"); + + b.HasOne("Models.User", "ProjectLiker") + .WithMany("LikedProjectsByUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.ProjectTransferRequest", b => + { + b.HasOne("Models.User", "PotentialNewOwner") + .WithMany() + .HasForeignKey("PotentialNewOwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.RoleScope", b => + { + b.HasOne("Models.Role", null) + .WithMany("Scopes") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.User", b => + { + b.HasOne("Models.Institution", "Institution") + .WithMany() + .HasForeignKey("InstitutionId"); + + b.HasOne("Models.Role", "Role") + .WithMany() + .HasForeignKey("RoleId"); + }); + + modelBuilder.Entity("Models.UserProject", b => + { + b.HasOne("Models.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId"); + + b.HasOne("Models.User", "User") + .WithMany("UserProject") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.UserTask", b => + { + b.HasOne("Models.User", "User") + .WithMany("UserTasks") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Models.UserUser", b => + { + b.HasOne("Models.User", "FollowedUser") + .WithMany("FollowedUsers") + .HasForeignKey("FollowedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/20211206093905_AddedActivityScoreToProjectModel.cs b/Data/Migrations/20211206093905_AddedActivityScoreToProjectModel.cs new file mode 100644 index 00000000..c044857c --- /dev/null +++ b/Data/Migrations/20211206093905_AddedActivityScoreToProjectModel.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace _4_Data.Migrations +{ + public partial class AddedActivityScoreToProjectModel : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ActivityScore", + table: "Project", + nullable: false, + defaultValue: 0.0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ActivityScore", + table: "Project"); + } + } +} diff --git a/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/Data/Migrations/ApplicationDbContextModelSnapshot.cs index 7187b44e..eab3d632 100644 --- a/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -270,6 +270,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + b.Property("ActivityScore") + .HasColumnType("float"); + b.Property("Created") .HasColumnType("datetime2"); diff --git a/Services/Services/ActivityAlgorithmDataPointService.cs b/Services/Services/ActivityAlgorithmDataPointService.cs new file mode 100644 index 00000000..5955d405 --- /dev/null +++ b/Services/Services/ActivityAlgorithmDataPointService.cs @@ -0,0 +1,50 @@ +using Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Services.Services +{ + public abstract interface IActivityAlgorithmDataPointService + { + public IActivityAlgorithmDataPointService(int multiplier); + public double Calculate(Project project); + } + + public class LikeDataPoint : IActivityAlgorithmDataPointService + { + + public double Calculate(Project project) + { + if(project.Likes != null) + return project.Likes.Count; + return 0; + } + + } + + public class RecentCreationDate : IActivityAlgorithmDataPointService + { + public double Calculate(Project project) + { + if(project.Created != null) + { + if((DateTime.Now - project.Created).TotalDays < 30) + { + return 10; + } + + } + return 0; + } + } + + // TODO: Update like DataPoint + // TODO: Updated Time + // TODO: Average Like Date + // TODO: Institution + // TODO: Connectec Collaborators + // TODO: Sufficient MetaData + // TODO: CTA'S + // TODO: Research and implementation for GitHub +} diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index 4e21ac82..af08da9b 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -11,41 +11,45 @@ namespace Services.Services { public interface IActivityAlgorithmService { - IEnumerable CalculateAllProjects(IEnumerable projects); + List CalculateAllProjects(IEnumerable projects); double CalculateProjectActivityScore(Project project); - bool SetProjectActivityScore(Project project, double score); + void SetProjectActivityScore(Project project, double score); } public class ActivityAlgorithmService : IActivityAlgorithmService { - private readonly List dataPoints; - private readonly ProjectRepository projectRepo; - - public ActivityAlgorithmService(ProjectRepository projectRepo) - { - this.projectRepo = projectRepo; - //List all data points wat are required to calculate each projects activity score - dataPoints = new List() + private readonly List dataPoints + = new List() { - new LikeDataPoint() + new LikeDataPoint(), + new RecentCreationDate(), }; + private readonly IProjectRepository projectRepository; + private readonly IProjectService projectService; + public ActivityAlgorithmService(IProjectRepository projectRepository, IProjectService projectService) + { + this.projectRepository = projectRepository; + this.projectService = projectService; + //List all data points wat are required to calculate each projects activity score + } - public IEnumerable CalculateAllProjects(IEnumerable projects) + public List CalculateAllProjects(IEnumerable projects) { foreach(Project project in projects.ToList()) { - double score = this.CalculateProjectActivityScore(project); - this.SetProjectActivityScore(project, score); + double score = CalculateProjectActivityScore(project); + SetProjectActivityScore(project, score); } - return projects; + projectService.Save(); + return projects.ToList(); } public double CalculateProjectActivityScore(Project project) { double score = 0; - foreach(IActivityAlgorithmDataPoint dataPoint in this.dataPoints) + foreach(IActivityAlgorithmDataPointService dataPoint in dataPoints) { score += dataPoint.Calculate(project); } @@ -55,30 +59,17 @@ public double CalculateProjectActivityScore(Project project) // return this._dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); } - public bool SetProjectActivityScore(Project project, double score) + public void SetProjectActivityScore(Project project, double score) { project.ActivityScore = score; + projectService.Update(project); + + //TODO database implementation //TODO Elastic search implementation - return true; + return; } } - internal interface IActivityAlgorithmDataPoint - { - - public double Calculate(Project project); - } - - - internal class LikeDataPoint : IActivityAlgorithmDataPoint - { - - public double Calculate(Project project) - { - return project.Likes.Count; - } - - } } From 38ee09ba3b396a71aa0a4caa999a5be25bcf2d89 Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Mon, 6 Dec 2021 12:59:51 +0100 Subject: [PATCH 04/22] fix: update architecture --- .../ActivityAlgorithmDataPointService.cs | 50 ------------------- .../Services/ActivityAlgorithmDataPoints.cs | 47 +++++++++++++++++ Services/Services/ActivityAlgorithmService.cs | 31 +++--------- 3 files changed, 55 insertions(+), 73 deletions(-) delete mode 100644 Services/Services/ActivityAlgorithmDataPointService.cs create mode 100644 Services/Services/ActivityAlgorithmDataPoints.cs diff --git a/Services/Services/ActivityAlgorithmDataPointService.cs b/Services/Services/ActivityAlgorithmDataPointService.cs deleted file mode 100644 index 5955d405..00000000 --- a/Services/Services/ActivityAlgorithmDataPointService.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Services.Services -{ - public abstract interface IActivityAlgorithmDataPointService - { - public IActivityAlgorithmDataPointService(int multiplier); - public double Calculate(Project project); - } - - public class LikeDataPoint : IActivityAlgorithmDataPointService - { - - public double Calculate(Project project) - { - if(project.Likes != null) - return project.Likes.Count; - return 0; - } - - } - - public class RecentCreationDate : IActivityAlgorithmDataPointService - { - public double Calculate(Project project) - { - if(project.Created != null) - { - if((DateTime.Now - project.Created).TotalDays < 30) - { - return 10; - } - - } - return 0; - } - } - - // TODO: Update like DataPoint - // TODO: Updated Time - // TODO: Average Like Date - // TODO: Institution - // TODO: Connectec Collaborators - // TODO: Sufficient MetaData - // TODO: CTA'S - // TODO: Research and implementation for GitHub -} diff --git a/Services/Services/ActivityAlgorithmDataPoints.cs b/Services/Services/ActivityAlgorithmDataPoints.cs new file mode 100644 index 00000000..18f8ee36 --- /dev/null +++ b/Services/Services/ActivityAlgorithmDataPoints.cs @@ -0,0 +1,47 @@ +using Models; +using System; + +namespace Services.Services +{ + public abstract class AbstractDataPoint + { + protected readonly double Multiplier; + + public AbstractDataPoint(int multiplier = 1) + { + Multiplier = multiplier; + } + public abstract double Calculate(Project project); + + } + + public class LikeDataPoint : AbstractDataPoint + { + public LikeDataPoint(int multiplier = 1) : base(multiplier) { } + public override double Calculate(Project project) + { + if(project.Likes != null) + return project.Likes.Count * Multiplier; + return 0; + } + + + } + public class RecentCreatedDataPoint : AbstractDataPoint + { + public RecentCreatedDataPoint(int multiplier = 1) : base(multiplier) { } + public override double Calculate(Project project) + { + return (DateTime.Now - project.Created).TotalDays * Multiplier; + } + } + + // TODO: Update like DataPoint + // TODO: Updated Time + // TODO: Average Like Date + // TODO: Institution + // TODO: Connected Collaborators + // TODO: Sufficient MetaData + // TODO: CTA'S + // TODO: Research and implementation for GitHub +} diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index af08da9b..dbbc43a3 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -19,25 +19,22 @@ public interface IActivityAlgorithmService public class ActivityAlgorithmService : IActivityAlgorithmService { - private readonly List dataPoints - = new List() + private readonly List dataPoints + = new List() { - new LikeDataPoint(), - new RecentCreationDate(), + new LikeDataPoint(1), + new RecentCreatedDataPoint(1), }; - private readonly IProjectRepository projectRepository; + private readonly IProjectService projectService; - public ActivityAlgorithmService(IProjectRepository projectRepository, IProjectService projectService) + public ActivityAlgorithmService(IProjectService projectService) { - this.projectRepository = projectRepository; this.projectService = projectService; - //List all data points wat are required to calculate each projects activity score - } public List CalculateAllProjects(IEnumerable projects) { - foreach(Project project in projects.ToList()) + foreach(Project project in projects) { double score = CalculateProjectActivityScore(project); SetProjectActivityScore(project, score); @@ -48,26 +45,14 @@ public List CalculateAllProjects(IEnumerable projects) public double CalculateProjectActivityScore(Project project) { - double score = 0; - foreach(IActivityAlgorithmDataPointService dataPoint in dataPoints) - { - score += dataPoint.Calculate(project); - } - return score; - - //Is this better? - // return this._dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); + return this.dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); } public void SetProjectActivityScore(Project project, double score) { project.ActivityScore = score; projectService.Update(project); - - - //TODO database implementation //TODO Elastic search implementation - return; } } From 8f701e9bca6f7a8802b0e861bba62e9685b5d812 Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg <73314842+BartvanEijkelenburg@users.noreply.github.com> Date: Tue, 7 Dec 2021 14:18:06 +0100 Subject: [PATCH 05/22] added datapoints --- API/Controllers/AlgorithmController.cs | 1 - API/Controllers/ProjectController.cs | 4 +- .../Services/ActivityAlgorithmDataPoints.cs | 77 +++++++++++++++++-- Services/Services/ActivityAlgorithmService.cs | 7 +- Services/Services/ProjectService.cs | 3 + 5 files changed, 81 insertions(+), 11 deletions(-) diff --git a/API/Controllers/AlgorithmController.cs b/API/Controllers/AlgorithmController.cs index f429ac6b..bd8f1ed9 100644 --- a/API/Controllers/AlgorithmController.cs +++ b/API/Controllers/AlgorithmController.cs @@ -44,7 +44,6 @@ public async Task ActivityAlgorithm() return Ok(activityAlgorithmService.CalculateAllProjects( await projectRepository.GetAllWithUsersCollaboratorsAndInstitutionsAsync()) .OrderByDescending(p => p.ActivityScore).ToList()); - } } } diff --git a/API/Controllers/ProjectController.cs b/API/Controllers/ProjectController.cs index 8b8d9a8f..5f33dfde 100644 --- a/API/Controllers/ProjectController.cs +++ b/API/Controllers/ProjectController.cs @@ -180,7 +180,8 @@ public async Task GetAllProjects( projectFilterParamsResource.SortBy != "name" && projectFilterParamsResource.SortBy != "created" && projectFilterParamsResource.SortBy != "updated" && - projectFilterParamsResource.SortBy != "likes") + projectFilterParamsResource.SortBy != "likes" && + projectFilterParamsResource.SortBy != "activity") { problem.Detail = "Invalid sort value: Use \"name\", \"created\", \"updated\" or \"likes\"."; problem.Instance = "5CE2F569-C0D5-4179-9299-62916270A058"; @@ -238,7 +239,6 @@ public async Task GetAllProjects( Page = projectFilterParams.Page, TotalPages = await projectService.GetProjectsTotalPages(projectFilterParams) }; - return Ok(resultsResource); } diff --git a/Services/Services/ActivityAlgorithmDataPoints.cs b/Services/Services/ActivityAlgorithmDataPoints.cs index 18f8ee36..3293622e 100644 --- a/Services/Services/ActivityAlgorithmDataPoints.cs +++ b/Services/Services/ActivityAlgorithmDataPoints.cs @@ -1,5 +1,7 @@ using Models; using System; +using System.Collections.Generic; +using System.Linq; namespace Services.Services { @@ -7,7 +9,7 @@ public abstract class AbstractDataPoint { protected readonly double Multiplier; - public AbstractDataPoint(int multiplier = 1) + public AbstractDataPoint(double multiplier = 1) { Multiplier = multiplier; } @@ -17,11 +19,11 @@ public AbstractDataPoint(int multiplier = 1) public class LikeDataPoint : AbstractDataPoint { - public LikeDataPoint(int multiplier = 1) : base(multiplier) { } + public LikeDataPoint(double multiplier = 1) : base(multiplier) { } public override double Calculate(Project project) { if(project.Likes != null) - return project.Likes.Count * Multiplier; + return Math.Round(project.Likes.Count * Multiplier, 2); return 0; } @@ -29,16 +31,79 @@ public override double Calculate(Project project) } public class RecentCreatedDataPoint : AbstractDataPoint { - public RecentCreatedDataPoint(int multiplier = 1) : base(multiplier) { } + public RecentCreatedDataPoint(double multiplier = 1) : base(multiplier) { } public override double Calculate(Project project) { - return (DateTime.Now - project.Created).TotalDays * Multiplier; + double projectCreatedDays = (DateTime.Now - project.Created).TotalDays; + if(projectCreatedDays < 14) + { + return 5; + } + return Math.Round(projectCreatedDays * Multiplier, 2); } } + public class AverageLikeDateDataPoint : AbstractDataPoint + { + public AverageLikeDateDataPoint(double multiplier = 1) : base(multiplier) { } + + public override double Calculate(Project project) + { + List dates = new List(); + foreach(ProjectLike projectLike in project.Likes) + { + dates.Add(projectLike.Date); + } + if(dates.Count > 0) + { + DateTime averageDateTime = DateTime + .MinValue + .AddSeconds + (dates + .Sum(r => (r - DateTime.MinValue).TotalSeconds) + / dates.Count); + double totalDays = Math.Round((DateTime.Now - averageDateTime).TotalDays, 2); + if(totalDays < 14) + { + return 2; + } + return totalDays * Multiplier; + } + return 0; + } + } + + public class UpdatedTimeDataPoint : AbstractDataPoint + { + public UpdatedTimeDataPoint(double multiplier = 1) : base(multiplier) { } + + public override double Calculate(Project project) + { + return Math.Round((DateTime.Now - project.Updated).TotalDays * Multiplier, 2); + } + } + + public class InstitutionDataPoint : AbstractDataPoint + { + public InstitutionDataPoint(double multiplier = 1) : base(multiplier) { } + + public override double Calculate(Project project) + { + return Math.Round(project.LinkedInstitutions.Count * Multiplier, 2); + } + } + + public class ConnectedCollaboratorsDataPoint : AbstractDataPoint + { + public ConnectedCollaboratorsDataPoint(double multiplier = 1) : base(multiplier) { } + + public override double Calculate(Project project) + { + return Math.Round(project.Collaborators.Count * Multiplier, 2); + } + } // TODO: Update like DataPoint // TODO: Updated Time - // TODO: Average Like Date // TODO: Institution // TODO: Connected Collaborators // TODO: Sufficient MetaData diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index dbbc43a3..008ea7a6 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -23,7 +23,10 @@ private readonly List dataPoints = new List() { new LikeDataPoint(1), - new RecentCreatedDataPoint(1), + new RecentCreatedDataPoint(0.1), + new AverageLikeDateDataPoint(1), + new UpdatedTimeDataPoint(1), + new InstitutionDataPoint(1) }; private readonly IProjectService projectService; @@ -45,7 +48,7 @@ public List CalculateAllProjects(IEnumerable projects) public double CalculateProjectActivityScore(Project project) { - return this.dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); + return dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); } public void SetProjectActivityScore(Project project, double score) diff --git a/Services/Services/ProjectService.cs b/Services/Services/ProjectService.cs index 13378945..2286a119 100644 --- a/Services/Services/ProjectService.cs +++ b/Services/Services/ProjectService.cs @@ -170,6 +170,9 @@ public Task> GetAllWithUsersCollaboratorsAndInstitutionsAsync(Proj case "likes": orderBy = project => project.Likes.Count; break; + case "activity": + orderBy = project => project.ActivityScore; + break; default: orderBy = project => project.Updated; break; From 9e51697b657d84a329ff1e02ff051cdcb8ec76bd Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg <73314842+BartvanEijkelenburg@users.noreply.github.com> Date: Tue, 7 Dec 2021 15:19:21 +0100 Subject: [PATCH 06/22] Comments --- Services/Services/ActivityAlgorithmDataPoints.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Services/Services/ActivityAlgorithmDataPoints.cs b/Services/Services/ActivityAlgorithmDataPoints.cs index 3293622e..ad47272f 100644 --- a/Services/Services/ActivityAlgorithmDataPoints.cs +++ b/Services/Services/ActivityAlgorithmDataPoints.cs @@ -39,6 +39,7 @@ public override double Calculate(Project project) { return 5; } + // This does not make sense, older == more points? return Math.Round(projectCreatedDays * Multiplier, 2); } } @@ -67,6 +68,7 @@ public override double Calculate(Project project) { return 2; } + // Older equals more? return totalDays * Multiplier; } return 0; From 50bfc81adeb3077d1657142d5920f5d0b0cf3f29 Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Thu, 9 Dec 2021 13:13:45 +0100 Subject: [PATCH 07/22] feat: added first algorithm test --- API/appsettingsapi.Development.json | 2 +- IdentityServer/appsettings.Development.json | 2 +- Services.Tests/04_Services.Tests.csproj | 1 + Services.Tests/ActivityAlgorithmTest.cs | 46 +++++++++++++++++++ .../Helpers/ProjectGeneratorHelper.cs | 29 ++++++++++++ Services/Services/ActivityAlgorithmService.cs | 22 +++++---- 6 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 Services.Tests/ActivityAlgorithmTest.cs create mode 100644 Services.Tests/Helpers/ProjectGeneratorHelper.cs diff --git a/API/appsettingsapi.Development.json b/API/appsettingsapi.Development.json index 1c07898c..6dcfb292 100644 --- a/API/appsettingsapi.Development.json +++ b/API/appsettingsapi.Development.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "Server=(LocalDb)\\MSSQLLocalDB;Database=Dex;Trusted_Connection=True;MultipleActiveResultSets=true" + "DefaultConnection": "Server=localhost;Database=identity;User=sa;Password=Dexcelence!1" }, "App": { "Frontend": { diff --git a/IdentityServer/appsettings.Development.json b/IdentityServer/appsettings.Development.json index f4014680..45819857 100644 --- a/IdentityServer/appsettings.Development.json +++ b/IdentityServer/appsettings.Development.json @@ -1,7 +1,7 @@ { "ConnectionStrings": { "DefaultConnection": - "Server=(LocalDb)\\MSSQLLocalDB;Database=Dex;Trusted_Connection=True;MultipleActiveResultSets=true" + "Server=localhost;Database=identity;User=sa;Password=Dexcelence!1" }, "Logging": { "LogLevel": { diff --git a/Services.Tests/04_Services.Tests.csproj b/Services.Tests/04_Services.Tests.csproj index 4a380fef..341e0b2f 100644 --- a/Services.Tests/04_Services.Tests.csproj +++ b/Services.Tests/04_Services.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/Services.Tests/ActivityAlgorithmTest.cs b/Services.Tests/ActivityAlgorithmTest.cs new file mode 100644 index 00000000..6745230a --- /dev/null +++ b/Services.Tests/ActivityAlgorithmTest.cs @@ -0,0 +1,46 @@ +using Models; +using NUnit.Framework; +using Services.Services; +using Services.Tests.Helpers; +using Services.Tests.Base; +using System; +using System.Collections.Generic; + +namespace Services.Tests +{ + + public class ActivityAlgorithmTest + { + + private IActivityAlgorithmService _activityAlgorithmService; + private ProjectGeneratorHelper _projectGeneratorHelper; + + public ActivityAlgorithmTest() + { + _activityAlgorithmService = new ActivityAlgorithmService(); + _projectGeneratorHelper = new ProjectGeneratorHelper(); + } + + [Test] + public void ScoreProjectBasedOnLikes() + { + Project project = new Project(); + project.Likes = new List() + { + _projectGeneratorHelper.GetOldLike(), + _projectGeneratorHelper.GetOldLike(), + _projectGeneratorHelper.GetRecentLike() + }; + + double score = _activityAlgorithmService.CalculateProjectActivityScore(project, new List + { + new LikeDataPoint() + }); + + Assert.AreEqual((project.Likes.Count), score); + + } + + } + +} diff --git a/Services.Tests/Helpers/ProjectGeneratorHelper.cs b/Services.Tests/Helpers/ProjectGeneratorHelper.cs new file mode 100644 index 00000000..21ebff33 --- /dev/null +++ b/Services.Tests/Helpers/ProjectGeneratorHelper.cs @@ -0,0 +1,29 @@ +using Bogus; +using Models; + +namespace Services.Tests.Helpers +{ + + public class ProjectGeneratorHelper + { + + private Project _project = new Project(); + + + public ProjectLike GetOldLike() + { + Faker faker = new Faker(); + faker.RuleFor(l => l.Date, f => f.Date.Past()); + return faker.Generate(); + } + + public ProjectLike GetRecentLike() + { + Faker faker = new Faker(); + faker.RuleFor(l => l.Date, f => f.Date.Recent(2)); + return faker.Generate(); + } + + } + +} diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index 008ea7a6..bbfdf722 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -12,27 +12,27 @@ namespace Services.Services public interface IActivityAlgorithmService { List CalculateAllProjects(IEnumerable projects); - double CalculateProjectActivityScore(Project project); + double CalculateProjectActivityScore(Project project, List dataPoints); void SetProjectActivityScore(Project project, double score); } public class ActivityAlgorithmService : IActivityAlgorithmService { - private readonly List dataPoints + private readonly List _dataPoints = new List() { - new LikeDataPoint(1), + new LikeDataPoint(), new RecentCreatedDataPoint(0.1), - new AverageLikeDateDataPoint(1), - new UpdatedTimeDataPoint(1), - new InstitutionDataPoint(1) + // new AverageLikeDateDataPoint(), + // new UpdatedTimeDataPoint(), + // new InstitutionDataPoint() }; private readonly IProjectService projectService; - public ActivityAlgorithmService(IProjectService projectService) + public ActivityAlgorithmService() { - this.projectService = projectService; + // this.projectService = projectService; } public List CalculateAllProjects(IEnumerable projects) @@ -46,15 +46,17 @@ public List CalculateAllProjects(IEnumerable projects) return projects.ToList(); } - public double CalculateProjectActivityScore(Project project) + public double CalculateProjectActivityScore(Project project, List dataPoints = null) { + if(dataPoints == null) + dataPoints = _dataPoints; return dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); } public void SetProjectActivityScore(Project project, double score) { project.ActivityScore = score; - projectService.Update(project); + // projectService.Update(project); //TODO Elastic search implementation } } From b5dcdc3d647633b5afcaca99315f8ba727cb4395 Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Thu, 9 Dec 2021 13:16:11 +0100 Subject: [PATCH 08/22] Revert "feat: added first algorithm test" This reverts commit 50bfc81adeb3077d1657142d5920f5d0b0cf3f29. --- API/appsettingsapi.Development.json | 2 +- IdentityServer/appsettings.Development.json | 2 +- Services.Tests/04_Services.Tests.csproj | 1 - Services.Tests/ActivityAlgorithmTest.cs | 46 ------------------- .../Helpers/ProjectGeneratorHelper.cs | 29 ------------ Services/Services/ActivityAlgorithmService.cs | 22 ++++----- 6 files changed, 12 insertions(+), 90 deletions(-) delete mode 100644 Services.Tests/ActivityAlgorithmTest.cs delete mode 100644 Services.Tests/Helpers/ProjectGeneratorHelper.cs diff --git a/API/appsettingsapi.Development.json b/API/appsettingsapi.Development.json index 6dcfb292..1c07898c 100644 --- a/API/appsettingsapi.Development.json +++ b/API/appsettingsapi.Development.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "Server=localhost;Database=identity;User=sa;Password=Dexcelence!1" + "DefaultConnection": "Server=(LocalDb)\\MSSQLLocalDB;Database=Dex;Trusted_Connection=True;MultipleActiveResultSets=true" }, "App": { "Frontend": { diff --git a/IdentityServer/appsettings.Development.json b/IdentityServer/appsettings.Development.json index 45819857..f4014680 100644 --- a/IdentityServer/appsettings.Development.json +++ b/IdentityServer/appsettings.Development.json @@ -1,7 +1,7 @@ { "ConnectionStrings": { "DefaultConnection": - "Server=localhost;Database=identity;User=sa;Password=Dexcelence!1" + "Server=(LocalDb)\\MSSQLLocalDB;Database=Dex;Trusted_Connection=True;MultipleActiveResultSets=true" }, "Logging": { "LogLevel": { diff --git a/Services.Tests/04_Services.Tests.csproj b/Services.Tests/04_Services.Tests.csproj index 341e0b2f..4a380fef 100644 --- a/Services.Tests/04_Services.Tests.csproj +++ b/Services.Tests/04_Services.Tests.csproj @@ -9,7 +9,6 @@ - diff --git a/Services.Tests/ActivityAlgorithmTest.cs b/Services.Tests/ActivityAlgorithmTest.cs deleted file mode 100644 index 6745230a..00000000 --- a/Services.Tests/ActivityAlgorithmTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Models; -using NUnit.Framework; -using Services.Services; -using Services.Tests.Helpers; -using Services.Tests.Base; -using System; -using System.Collections.Generic; - -namespace Services.Tests -{ - - public class ActivityAlgorithmTest - { - - private IActivityAlgorithmService _activityAlgorithmService; - private ProjectGeneratorHelper _projectGeneratorHelper; - - public ActivityAlgorithmTest() - { - _activityAlgorithmService = new ActivityAlgorithmService(); - _projectGeneratorHelper = new ProjectGeneratorHelper(); - } - - [Test] - public void ScoreProjectBasedOnLikes() - { - Project project = new Project(); - project.Likes = new List() - { - _projectGeneratorHelper.GetOldLike(), - _projectGeneratorHelper.GetOldLike(), - _projectGeneratorHelper.GetRecentLike() - }; - - double score = _activityAlgorithmService.CalculateProjectActivityScore(project, new List - { - new LikeDataPoint() - }); - - Assert.AreEqual((project.Likes.Count), score); - - } - - } - -} diff --git a/Services.Tests/Helpers/ProjectGeneratorHelper.cs b/Services.Tests/Helpers/ProjectGeneratorHelper.cs deleted file mode 100644 index 21ebff33..00000000 --- a/Services.Tests/Helpers/ProjectGeneratorHelper.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Bogus; -using Models; - -namespace Services.Tests.Helpers -{ - - public class ProjectGeneratorHelper - { - - private Project _project = new Project(); - - - public ProjectLike GetOldLike() - { - Faker faker = new Faker(); - faker.RuleFor(l => l.Date, f => f.Date.Past()); - return faker.Generate(); - } - - public ProjectLike GetRecentLike() - { - Faker faker = new Faker(); - faker.RuleFor(l => l.Date, f => f.Date.Recent(2)); - return faker.Generate(); - } - - } - -} diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index bbfdf722..008ea7a6 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -12,27 +12,27 @@ namespace Services.Services public interface IActivityAlgorithmService { List CalculateAllProjects(IEnumerable projects); - double CalculateProjectActivityScore(Project project, List dataPoints); + double CalculateProjectActivityScore(Project project); void SetProjectActivityScore(Project project, double score); } public class ActivityAlgorithmService : IActivityAlgorithmService { - private readonly List _dataPoints + private readonly List dataPoints = new List() { - new LikeDataPoint(), + new LikeDataPoint(1), new RecentCreatedDataPoint(0.1), - // new AverageLikeDateDataPoint(), - // new UpdatedTimeDataPoint(), - // new InstitutionDataPoint() + new AverageLikeDateDataPoint(1), + new UpdatedTimeDataPoint(1), + new InstitutionDataPoint(1) }; private readonly IProjectService projectService; - public ActivityAlgorithmService() + public ActivityAlgorithmService(IProjectService projectService) { - // this.projectService = projectService; + this.projectService = projectService; } public List CalculateAllProjects(IEnumerable projects) @@ -46,17 +46,15 @@ public List CalculateAllProjects(IEnumerable projects) return projects.ToList(); } - public double CalculateProjectActivityScore(Project project, List dataPoints = null) + public double CalculateProjectActivityScore(Project project) { - if(dataPoints == null) - dataPoints = _dataPoints; return dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); } public void SetProjectActivityScore(Project project, double score) { project.ActivityScore = score; - // projectService.Update(project); + projectService.Update(project); //TODO Elastic search implementation } } From 22267f74696bd836c1e2d235b11cc91455d43442 Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Thu, 9 Dec 2021 13:22:08 +0100 Subject: [PATCH 09/22] feat: Added first algorithm test --- Services.Tests/04_Services.Tests.csproj | 37 +++++++-------- Services.Tests/ActivityAlgorithmTest.cs | 46 +++++++++++++++++++ .../Helpers/ProjectGeneratorHelper.cs | 29 ++++++++++++ Services/Services/ActivityAlgorithmService.cs | 22 +++++---- 4 files changed, 106 insertions(+), 28 deletions(-) create mode 100644 Services.Tests/ActivityAlgorithmTest.cs create mode 100644 Services.Tests/Helpers/ProjectGeneratorHelper.cs diff --git a/Services.Tests/04_Services.Tests.csproj b/Services.Tests/04_Services.Tests.csproj index 4a380fef..8f92ed25 100644 --- a/Services.Tests/04_Services.Tests.csproj +++ b/Services.Tests/04_Services.Tests.csproj @@ -1,26 +1,27 @@ - - netcoreapp3.1 + + netcoreapp3.1 - false + false - 1.8.0-beta - + 1.8.0-beta + - - - - - - - - + + + + + + + + + - - - - - + + + + + diff --git a/Services.Tests/ActivityAlgorithmTest.cs b/Services.Tests/ActivityAlgorithmTest.cs new file mode 100644 index 00000000..6745230a --- /dev/null +++ b/Services.Tests/ActivityAlgorithmTest.cs @@ -0,0 +1,46 @@ +using Models; +using NUnit.Framework; +using Services.Services; +using Services.Tests.Helpers; +using Services.Tests.Base; +using System; +using System.Collections.Generic; + +namespace Services.Tests +{ + + public class ActivityAlgorithmTest + { + + private IActivityAlgorithmService _activityAlgorithmService; + private ProjectGeneratorHelper _projectGeneratorHelper; + + public ActivityAlgorithmTest() + { + _activityAlgorithmService = new ActivityAlgorithmService(); + _projectGeneratorHelper = new ProjectGeneratorHelper(); + } + + [Test] + public void ScoreProjectBasedOnLikes() + { + Project project = new Project(); + project.Likes = new List() + { + _projectGeneratorHelper.GetOldLike(), + _projectGeneratorHelper.GetOldLike(), + _projectGeneratorHelper.GetRecentLike() + }; + + double score = _activityAlgorithmService.CalculateProjectActivityScore(project, new List + { + new LikeDataPoint() + }); + + Assert.AreEqual((project.Likes.Count), score); + + } + + } + +} diff --git a/Services.Tests/Helpers/ProjectGeneratorHelper.cs b/Services.Tests/Helpers/ProjectGeneratorHelper.cs new file mode 100644 index 00000000..21ebff33 --- /dev/null +++ b/Services.Tests/Helpers/ProjectGeneratorHelper.cs @@ -0,0 +1,29 @@ +using Bogus; +using Models; + +namespace Services.Tests.Helpers +{ + + public class ProjectGeneratorHelper + { + + private Project _project = new Project(); + + + public ProjectLike GetOldLike() + { + Faker faker = new Faker(); + faker.RuleFor(l => l.Date, f => f.Date.Past()); + return faker.Generate(); + } + + public ProjectLike GetRecentLike() + { + Faker faker = new Faker(); + faker.RuleFor(l => l.Date, f => f.Date.Recent(2)); + return faker.Generate(); + } + + } + +} diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index 008ea7a6..bbfdf722 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -12,27 +12,27 @@ namespace Services.Services public interface IActivityAlgorithmService { List CalculateAllProjects(IEnumerable projects); - double CalculateProjectActivityScore(Project project); + double CalculateProjectActivityScore(Project project, List dataPoints); void SetProjectActivityScore(Project project, double score); } public class ActivityAlgorithmService : IActivityAlgorithmService { - private readonly List dataPoints + private readonly List _dataPoints = new List() { - new LikeDataPoint(1), + new LikeDataPoint(), new RecentCreatedDataPoint(0.1), - new AverageLikeDateDataPoint(1), - new UpdatedTimeDataPoint(1), - new InstitutionDataPoint(1) + // new AverageLikeDateDataPoint(), + // new UpdatedTimeDataPoint(), + // new InstitutionDataPoint() }; private readonly IProjectService projectService; - public ActivityAlgorithmService(IProjectService projectService) + public ActivityAlgorithmService() { - this.projectService = projectService; + // this.projectService = projectService; } public List CalculateAllProjects(IEnumerable projects) @@ -46,15 +46,17 @@ public List CalculateAllProjects(IEnumerable projects) return projects.ToList(); } - public double CalculateProjectActivityScore(Project project) + public double CalculateProjectActivityScore(Project project, List dataPoints = null) { + if(dataPoints == null) + dataPoints = _dataPoints; return dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); } public void SetProjectActivityScore(Project project, double score) { project.ActivityScore = score; - projectService.Update(project); + // projectService.Update(project); //TODO Elastic search implementation } } From f62bd2bfee17ffe19d08314db0448fc26746d4ad Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg Date: Thu, 9 Dec 2021 16:05:17 +0100 Subject: [PATCH 10/22] Improved Algorythm --- .../Services/ActivityAlgorithmDataPoints.cs | 75 ++++++++++++++----- Services/Services/ActivityAlgorithmService.cs | 9 ++- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/Services/Services/ActivityAlgorithmDataPoints.cs b/Services/Services/ActivityAlgorithmDataPoints.cs index ad47272f..2c27d99d 100644 --- a/Services/Services/ActivityAlgorithmDataPoints.cs +++ b/Services/Services/ActivityAlgorithmDataPoints.cs @@ -1,4 +1,6 @@ +using Microsoft.Extensions.DependencyInjection; using Models; +using Services.Sources; using System; using System.Collections.Generic; using System.Linq; @@ -24,7 +26,7 @@ public override double Calculate(Project project) { if(project.Likes != null) return Math.Round(project.Likes.Count * Multiplier, 2); - return 0; + return 0.00; } @@ -37,10 +39,10 @@ public override double Calculate(Project project) double projectCreatedDays = (DateTime.Now - project.Created).TotalDays; if(projectCreatedDays < 14) { - return 5; + return 5.00; } // This does not make sense, older == more points? - return Math.Round(projectCreatedDays * Multiplier, 2); + return Math.Round(Multiplier / projectCreatedDays * 10, 2); } } @@ -63,15 +65,14 @@ public override double Calculate(Project project) (dates .Sum(r => (r - DateTime.MinValue).TotalSeconds) / dates.Count); - double totalDays = Math.Round((DateTime.Now - averageDateTime).TotalDays, 2); - if(totalDays < 14) + double averageLikeDate = Math.Round((DateTime.Now - averageDateTime).TotalDays, 2); + if(averageLikeDate < 14) { - return 2; + return 2.00; } - // Older equals more? - return totalDays * Multiplier; + return Math.Round(Multiplier / averageLikeDate, 2); } - return 0; + return 0.00; } } @@ -81,7 +82,12 @@ public UpdatedTimeDataPoint(double multiplier = 1) : base(multiplier) { } public override double Calculate(Project project) { - return Math.Round((DateTime.Now - project.Updated).TotalDays * Multiplier, 2); + double updatedDate = (DateTime.Now - project.Updated).TotalDays; + if(updatedDate < 14) + { + return 2.00; + } + return Math.Round(Multiplier / updatedDate, 2); } } @@ -101,14 +107,49 @@ public ConnectedCollaboratorsDataPoint(double multiplier = 1) : base(multiplier) public override double Calculate(Project project) { + if(project.Collaborators.Count > 5) return Math.Round(6 * Multiplier, 2); return Math.Round(project.Collaborators.Count * Multiplier, 2); } } - // TODO: Update like DataPoint - // TODO: Updated Time - // TODO: Institution - // TODO: Connected Collaborators - // TODO: Sufficient MetaData - // TODO: CTA'S - // TODO: Research and implementation for GitHub + + public class MetaDataDataPoint : AbstractDataPoint + { + public MetaDataDataPoint(double multiplier = 1) : base(multiplier) { } + public override double Calculate(Project project) + { + double score = 0; + if(project.Categories.Count >= 1) score += 1; + if(project.CallToActions.Count >= 1) score += project.CallToActions.Count; + if(project.Images.Count >= 1) score += 2; + if(project.Uri != null) score += 1; + return Math.Round(score * Multiplier, 2); + } + } + //ToDo: Research Repo System. + public class RepoScoreDataPoint : AbstractDataPoint + { + readonly IGitLabSource gitLabSource = new GitLabSource(new RestClientFactory()); + public RepoScoreDataPoint(double multiplier = 1) : base(multiplier) {} + public override double Calculate(Project project) + { + Uri sourceUri = new Uri(project.Uri); + + if(sourceUri.Host == "github.com") + { + return 2.00 * Multiplier; + } + if(sourceUri.Host == "gitlab.com") + { + Project gitlabProject = gitLabSource.GetProjectInformation(sourceUri); + if(gitlabProject != null) + { + return Math.Round(2.00 * Multiplier, 2); + } else + { + return 0.00; + } + } + return 2.00 * Multiplier; + } + } } diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index 008ea7a6..60608057 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -23,10 +23,13 @@ private readonly List dataPoints = new List() { new LikeDataPoint(1), - new RecentCreatedDataPoint(0.1), + new RecentCreatedDataPoint(1), new AverageLikeDateDataPoint(1), new UpdatedTimeDataPoint(1), - new InstitutionDataPoint(1) + new InstitutionDataPoint(1), + new ConnectedCollaboratorsDataPoint(1), + new MetaDataDataPoint(1), + new RepoScoreDataPoint(1), }; private readonly IProjectService projectService; @@ -48,7 +51,7 @@ public List CalculateAllProjects(IEnumerable projects) public double CalculateProjectActivityScore(Project project) { - return dataPoints.Sum(dataPoint => dataPoint.Calculate(project)); + return Math.Round(dataPoints.Sum(dataPoint => dataPoint.Calculate(project)), 2); } public void SetProjectActivityScore(Project project, double score) From 8ffc1bc5a22e954f3a8dd7d94223b02b9aef7757 Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg Date: Thu, 9 Dec 2021 16:22:59 +0100 Subject: [PATCH 11/22] Algorythm updates --- Services.Tests/ActivityAlgorithmTest.cs | 2 +- Services/Services/ActivityAlgorithmService.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Services.Tests/ActivityAlgorithmTest.cs b/Services.Tests/ActivityAlgorithmTest.cs index 6745230a..565230a8 100644 --- a/Services.Tests/ActivityAlgorithmTest.cs +++ b/Services.Tests/ActivityAlgorithmTest.cs @@ -1,4 +1,4 @@ -using Models; +using Models; using NUnit.Framework; using Services.Services; using Services.Tests.Helpers; diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index 6a9fbee9..dc6bb4ed 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -33,9 +33,9 @@ private readonly List _dataPoints }; private readonly IProjectService projectService; - public ActivityAlgorithmService() + public ActivityAlgorithmService(IProjectService projectService) { - // this.projectService = projectService; + this.projectService = projectService; } public List CalculateAllProjects(IEnumerable projects) From 618f92852eca98ef02a8c7b204be521fa3427c10 Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg <73314842+BartvanEijkelenburg@users.noreply.github.com> Date: Mon, 13 Dec 2021 12:35:14 +0100 Subject: [PATCH 12/22] Updated algorithm --- .../Services/ActivityAlgorithmDataPoints.cs | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/Services/Services/ActivityAlgorithmDataPoints.cs b/Services/Services/ActivityAlgorithmDataPoints.cs index 2c27d99d..ed4945d5 100644 --- a/Services/Services/ActivityAlgorithmDataPoints.cs +++ b/Services/Services/ActivityAlgorithmDataPoints.cs @@ -1,9 +1,12 @@ using Microsoft.Extensions.DependencyInjection; using Models; +using RestSharp; using Services.Sources; using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; namespace Services.Services { @@ -25,7 +28,7 @@ public LikeDataPoint(double multiplier = 1) : base(multiplier) { } public override double Calculate(Project project) { if(project.Likes != null) - return Math.Round(project.Likes.Count * Multiplier, 2); + return project.Likes.Count * Multiplier; return 0.00; } @@ -39,10 +42,9 @@ public override double Calculate(Project project) double projectCreatedDays = (DateTime.Now - project.Created).TotalDays; if(projectCreatedDays < 14) { - return 5.00; + return 3.00 * Multiplier; } - // This does not make sense, older == more points? - return Math.Round(Multiplier / projectCreatedDays * 10, 2); + return Multiplier / projectCreatedDays * 10; } } @@ -68,9 +70,9 @@ public override double Calculate(Project project) double averageLikeDate = Math.Round((DateTime.Now - averageDateTime).TotalDays, 2); if(averageLikeDate < 14) { - return 2.00; + return 2.00 * Multiplier; } - return Math.Round(Multiplier / averageLikeDate, 2); + return Multiplier / averageLikeDate; } return 0.00; } @@ -85,9 +87,9 @@ public override double Calculate(Project project) double updatedDate = (DateTime.Now - project.Updated).TotalDays; if(updatedDate < 14) { - return 2.00; + return 2.00 * Multiplier; } - return Math.Round(Multiplier / updatedDate, 2); + return Multiplier / updatedDate; } } @@ -97,7 +99,7 @@ public InstitutionDataPoint(double multiplier = 1) : base(multiplier) { } public override double Calculate(Project project) { - return Math.Round(project.LinkedInstitutions.Count * Multiplier, 2); + return project.LinkedInstitutions.Count * Multiplier; } } @@ -108,7 +110,7 @@ public ConnectedCollaboratorsDataPoint(double multiplier = 1) : base(multiplier) public override double Calculate(Project project) { if(project.Collaborators.Count > 5) return Math.Round(6 * Multiplier, 2); - return Math.Round(project.Collaborators.Count * Multiplier, 2); + return project.Collaborators.Count * Multiplier; } } @@ -122,34 +124,27 @@ public override double Calculate(Project project) if(project.CallToActions.Count >= 1) score += project.CallToActions.Count; if(project.Images.Count >= 1) score += 2; if(project.Uri != null) score += 1; - return Math.Round(score * Multiplier, 2); + return score * Multiplier; } } - //ToDo: Research Repo System. + public class RepoScoreDataPoint : AbstractDataPoint { - readonly IGitLabSource gitLabSource = new GitLabSource(new RestClientFactory()); - public RepoScoreDataPoint(double multiplier = 1) : base(multiplier) {} + public RepoScoreDataPoint(double multiplier = 1) : base(multiplier) { } public override double Calculate(Project project) { - Uri sourceUri = new Uri(project.Uri); - if(sourceUri.Host == "github.com") - { - return 2.00 * Multiplier; - } - if(sourceUri.Host == "gitlab.com") + if(string.IsNullOrWhiteSpace(project.Uri)) return 0; + Uri uri = new Uri(project.Uri); + IRestClientFactory restClientFactory = new RestClientFactory(); + IRestClient client = restClientFactory.Create(uri); + RestRequest request = new RestRequest(Method.GET); + IRestResponse response = client.Execute(request); + if((int)response.StatusCode < 400) { - Project gitlabProject = gitLabSource.GetProjectInformation(sourceUri); - if(gitlabProject != null) - { - return Math.Round(2.00 * Multiplier, 2); - } else - { - return 0.00; - } + return 2; } - return 2.00 * Multiplier; + return 0; } } } From 0d684c99b2d64f9a85c1a2f9d89a59a9d2b72bbe Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Mon, 13 Dec 2021 12:38:48 +0100 Subject: [PATCH 13/22] update testing for algorithm --- Repositories/ProjectRepository.cs | 11 +++ Services.Tests/ActivityAlgorithmTest.cs | 83 ++++++++++++++++ .../Helpers/ProjectGeneratorHelper.cs | 97 +++++++++++++++++++ .../Services/ActivityAlgorithmDataPoints.cs | 31 +++--- Services/Services/ActivityAlgorithmService.cs | 9 +- Services/Services/ProjectService.cs | 8 ++ 6 files changed, 214 insertions(+), 25 deletions(-) diff --git a/Repositories/ProjectRepository.cs b/Repositories/ProjectRepository.cs index 398cea09..65c31f98 100644 --- a/Repositories/ProjectRepository.cs +++ b/Repositories/ProjectRepository.cs @@ -150,6 +150,9 @@ Task> GetUserProjects(int userId, /// This method return a list of projects where the title, or part of the title matches the query. /// Task> FindProjectsWhereTitleStartsWithQuery(string query); + + void UpdateActivityScore(Project entity); + } /// @@ -401,6 +404,14 @@ public override void Update(Project entity) } + public void UpdateActivityScore(Project entity) + { + DbSet.Update(entity); + ESProjectDTO projectToSync = ProjectConverter.ProjectToESProjectDTO(entity); + taskPublisher.RegisterTask(Newtonsoft.Json.JsonConvert.SerializeObject(projectToSync), Subject.ELASTIC_CREATE_OR_UPDATE); + } + + private static void SetLikes(Project entity) { if(entity != null) diff --git a/Services.Tests/ActivityAlgorithmTest.cs b/Services.Tests/ActivityAlgorithmTest.cs index 565230a8..1e6a15a2 100644 --- a/Services.Tests/ActivityAlgorithmTest.cs +++ b/Services.Tests/ActivityAlgorithmTest.cs @@ -1,10 +1,13 @@ +using FluentAssertions; using Models; +using Moq; using NUnit.Framework; using Services.Services; using Services.Tests.Helpers; using Services.Tests.Base; using System; using System.Collections.Generic; +using System.Linq; namespace Services.Tests { @@ -41,6 +44,86 @@ public void ScoreProjectBasedOnLikes() } + + [Test] + public void ScoreProjectBasedOnLikeDate() + { + Project project = new Project(); + project.Likes = new List() + { + _projectGeneratorHelper.GetOldLike(), + _projectGeneratorHelper.GetOldLike(), + _projectGeneratorHelper.GetRecentLike() + }; + + double score = _activityAlgorithmService.CalculateProjectActivityScore(project, new List + { + new AverageLikeDateDataPoint() + }); + + Assert.AreEqual(1, score); + + } + + + + [Test] + public void ScoreProjectBasedOnConnectedCollaborators() + { + Project project = new Project(); + project.Collaborators = new List() + { + new Collaborator(), + new Collaborator(), + new Collaborator(), + new Collaborator(), + new Collaborator(), + }; + + double score = _activityAlgorithmService.CalculateProjectActivityScore(project, new List + { + new ConnectedCollaboratorsDataPoint() + }); + + Assert.AreEqual(5, score); + + } + + + [Test] + public void OrderProjectsWithAlgorithm() + { + Project firstProject = _projectGeneratorHelper.GetActiveProject(); + Project secondProject = _projectGeneratorHelper.GetInactiveProject(10); + Project thirdProject = _projectGeneratorHelper.GetInactiveProject(); + Project fourthProject = _projectGeneratorHelper.GetActiveProject(10); + IEnumerable projects = new List() + { + firstProject, + secondProject, + thirdProject, + fourthProject + }; + + List orderedProjects = _activityAlgorithmService.CalculateAllProjects(projects) + .OrderByDescending(p => p.ActivityScore) + .ToList(); + + orderedProjects[0] + .Should() + .Be(fourthProject); + orderedProjects[1] + .Should() + .Be(firstProject); + orderedProjects[2] + .Should() + .Be(secondProject); + orderedProjects[3] + .Should() + .Be(thirdProject); + + } + } } diff --git a/Services.Tests/Helpers/ProjectGeneratorHelper.cs b/Services.Tests/Helpers/ProjectGeneratorHelper.cs index 21ebff33..1c954c64 100644 --- a/Services.Tests/Helpers/ProjectGeneratorHelper.cs +++ b/Services.Tests/Helpers/ProjectGeneratorHelper.cs @@ -1,5 +1,8 @@ using Bogus; +using Bogus.DataSets; using Models; +using System; +using System.Collections.Generic; namespace Services.Tests.Helpers { @@ -24,6 +27,100 @@ public ProjectLike GetRecentLike() return faker.Generate(); } + + public Project GetInactiveProject(int multiplier = 1) + { + Faker faker = new Faker(); + + faker.RuleFor(p => p.Name, f => f.Company.CompanyName()); + faker.RuleFor(p => p.Description, f => f.Lorem.Paragraph(2)); + faker.RuleFor(p => p.Created, f => f.Date.Past(3 * multiplier)); + faker.RuleFor(p => p.Updated, f => f.Date.Past(2 * multiplier)); + + Project project = faker.Generate(); + + project.Categories = new List(); + project.CallToActions = new List(); + project.Images = new List(); + + project.Likes = new List() + { + this.GetOldLike(), + this.GetOldLike(), + this.GetOldLike(), + this.GetOldLike(), + this.GetOldLike() + }; + + for(int i = 0; i < multiplier; i++) + { + project.Likes.Add(GetRecentLike()); + } + + return project; + } + + + public Project GetActiveProject(int multiplier = 1) + { + Faker faker = new Faker(); + + faker.RuleFor(p => p.Name, f => f.Company.CompanyName()); + faker.RuleFor(p => p.Description, f => f.Lorem.Paragraph(2)); + faker.RuleFor(p => p.Created, f => f.Date.Recent(2 * multiplier)); + faker.RuleFor(p => p.Updated, f => f.Date.Past(2 * multiplier)); + faker.RuleFor(p => p.Uri, f => f.Internet.Url()); + + Project project = faker.Generate(); + + project.Categories = new List() + { + new ProjectCategory(), + new ProjectCategory() + }; + + project.Likes = new List() + { + this.GetOldLike(), + this.GetRecentLike(), + this.GetRecentLike(), + this.GetRecentLike(), + this.GetRecentLike(), + this.GetRecentLike(), + }; + + + project.Collaborators = new List() + { + new Collaborator(), + new Collaborator() + }; + + project.CallToActions = new List() + { + new CallToAction(), + new CallToAction(), + new CallToAction(), + new CallToAction(), + }; + + project.Images = new List() + { + new File(), + new File(), + new File() + }; + + for(int i = 0; i < multiplier; i++) + { + project.Likes.Add(GetRecentLike()); + project.Collaborators.Add(new Collaborator()); + } + + + return project; + } + } } diff --git a/Services/Services/ActivityAlgorithmDataPoints.cs b/Services/Services/ActivityAlgorithmDataPoints.cs index ed4945d5..f224ff9c 100644 --- a/Services/Services/ActivityAlgorithmDataPoints.cs +++ b/Services/Services/ActivityAlgorithmDataPoints.cs @@ -1,12 +1,8 @@ -using Microsoft.Extensions.DependencyInjection; using Models; using RestSharp; using Services.Sources; using System; -using System.Collections.Generic; using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; namespace Services.Services { @@ -54,27 +50,22 @@ public AverageLikeDateDataPoint(double multiplier = 1) : base(multiplier) { } public override double Calculate(Project project) { - List dates = new List(); - foreach(ProjectLike projectLike in project.Likes) - { - dates.Add(projectLike.Date); - } - if(dates.Count > 0) + if(project.Likes.Count > 0) { DateTime averageDateTime = DateTime .MinValue .AddSeconds - (dates - .Sum(r => (r - DateTime.MinValue).TotalSeconds) - / dates.Count); + (project.Likes + .Sum(r => (r.Date - DateTime.MinValue).TotalSeconds) + / project.Likes.Count); double averageLikeDate = Math.Round((DateTime.Now - averageDateTime).TotalDays, 2); if(averageLikeDate < 14) { - return 2.00 * Multiplier; + return 2 * Multiplier; } return Multiplier / averageLikeDate; } - return 0.00; + return 0; } } @@ -87,12 +78,11 @@ public override double Calculate(Project project) double updatedDate = (DateTime.Now - project.Updated).TotalDays; if(updatedDate < 14) { - return 2.00 * Multiplier; + return 2 * Multiplier; } return Multiplier / updatedDate; } } - public class InstitutionDataPoint : AbstractDataPoint { public InstitutionDataPoint(double multiplier = 1) : base(multiplier) { } @@ -121,13 +111,14 @@ public override double Calculate(Project project) { double score = 0; if(project.Categories.Count >= 1) score += 1; - if(project.CallToActions.Count >= 1) score += project.CallToActions.Count; + if(project.CallToActions.Count >= 1 && project.CallToActions.Count <= 4) score += project.CallToActions.Count; + if(project.CallToActions.Count >= 4 )score += 4; if(project.Images.Count >= 1) score += 2; if(project.Uri != null) score += 1; return score * Multiplier; } } - + //ToDo: Research Repo System. public class RepoScoreDataPoint : AbstractDataPoint { public RepoScoreDataPoint(double multiplier = 1) : base(multiplier) { } @@ -142,7 +133,7 @@ public override double Calculate(Project project) IRestResponse response = client.Execute(request); if((int)response.StatusCode < 400) { - return 2; + return 2 * Multiplier; } return 0; } diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index dc6bb4ed..a71a6d52 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -32,8 +32,8 @@ private readonly List _dataPoints new RepoScoreDataPoint(1), }; - private readonly IProjectService projectService; - public ActivityAlgorithmService(IProjectService projectService) + private readonly IProjectService? projectService; + public ActivityAlgorithmService(IProjectService? projectService = null) { this.projectService = projectService; } @@ -45,7 +45,7 @@ public List CalculateAllProjects(IEnumerable projects) double score = CalculateProjectActivityScore(project); SetProjectActivityScore(project, score); } - projectService.Save(); + projectService?.Save(); return projects.ToList(); } @@ -59,8 +59,7 @@ public double CalculateProjectActivityScore(Project project, List /// The string with which the title must begin /// The projects where the title matches the query Task> FindProjectsWhereTitleStartsWithQuery(string query); + + void UpdateActivityScore(Project entity); + } /// @@ -314,6 +317,11 @@ public async Task FindAsyncNotRedacted(int id) { return await Repository.FindAsyncNotRedacted(id); } + + public void UpdateActivityScore(Project entity) + { + Repository.UpdateActivityScore(entity); + } } } From 6fb197c7861a08151ca832b32e052eca0a935f86 Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Mon, 13 Dec 2021 12:50:08 +0100 Subject: [PATCH 14/22] feat: changes to controller authentication & authorization --- API/Controllers/AlgorithmController.cs | 28 ++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/API/Controllers/AlgorithmController.cs b/API/Controllers/AlgorithmController.cs index bd8f1ed9..443b9c9c 100644 --- a/API/Controllers/AlgorithmController.cs +++ b/API/Controllers/AlgorithmController.cs @@ -1,3 +1,4 @@ +using API.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -22,16 +23,18 @@ public class AlgorithmController : ControllerBase private readonly IActivityAlgorithmService activityAlgorithmService; private readonly IProjectRepository projectRepository; + private readonly IUserService userService; /// /// Initializes a new instance of the class /// /// The category service which is used to communicate with the logic layer. /// The project category service which is used to communicate with the logic layer. - public AlgorithmController(IActivityAlgorithmService activityAlgorithmService, IProjectRepository projectRepository) + public AlgorithmController(IActivityAlgorithmService activityAlgorithmService, IProjectRepository projectRepository, IUserService userService) { this.activityAlgorithmService = activityAlgorithmService; this.projectRepository = projectRepository; + this.userService = userService; } /// @@ -39,11 +42,28 @@ public AlgorithmController(IActivityAlgorithmService activityAlgorithmService, I /// /// HttpStatusCode [HttpPost("Activity")] + [Authorize] public async Task ActivityAlgorithm() { - return Ok(activityAlgorithmService.CalculateAllProjects( - await projectRepository.GetAllWithUsersCollaboratorsAndInstitutionsAsync()) - .OrderByDescending(p => p.ActivityScore).ToList()); + bool isAllowed = HttpContext.User.HasClaim("client_role", Defaults.Roles.BackendApplication); + + if(isAllowed == false) + { + User currentUser = await HttpContext.GetContextUser(userService) + .ConfigureAwait(false); + if(currentUser.Role.Name == Defaults.Roles.Administrator) + { + isAllowed = true; + } + } + if(isAllowed) + { + return Ok(activityAlgorithmService.CalculateAllProjects( + await projectRepository.GetAllWithUsersCollaboratorsAndInstitutionsAsync()) + .OrderByDescending(p => p.ActivityScore).ToList()); + } + return Forbid(); + } } } From 6361baf56b8ccfdc1c0efda29bc2b341248b8b8c Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Mon, 13 Dec 2021 13:00:02 +0100 Subject: [PATCH 15/22] feat: added AlgorithmWorker.cs to Job scheduler --- JobScheduler/AlgorithmWorker.cs | 92 +++++++++++++++++++++++++++++++ JobScheduler/ApiRequestHandler.cs | 12 ++++ JobScheduler/Program.cs | 19 ++++--- 3 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 JobScheduler/AlgorithmWorker.cs diff --git a/JobScheduler/AlgorithmWorker.cs b/JobScheduler/AlgorithmWorker.cs new file mode 100644 index 00000000..2b01c23e --- /dev/null +++ b/JobScheduler/AlgorithmWorker.cs @@ -0,0 +1,92 @@ +/* +* Digital Excellence Copyright (C) 2020 Brend Smits +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published +* by the Free Software Foundation version 3 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU Lesser General Public License for more details. +* +* You can find a copy of the GNU Lesser General Public License +* along with this program, in the LICENSE.md file in the root project directory. +* If not, see https://www.gnu.org/licenses/lgpl-3.0.txt +*/ + +using MessageBrokerPublisher.HelperClasses; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Models; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace JobScheduler +{ + + /// + /// This is the worker service which is responsible for running graduation related jobs. + /// + public class AlgorithmWorker : BackgroundService + { + + private readonly Config config; + private readonly ILogger logger; + private readonly IApiRequestHandler requestHandler; + + /// + /// This is the constructor of the worker service + /// + /// + /// + /// + public AlgorithmWorker(ILogger logger, + IApiRequestHandler apiRequestHandler, + Config config) + { + this.logger = logger; + requestHandler = apiRequestHandler; + this.config = config; + } + + /// + /// This is the asynchronous method which executes the job. + /// + /// + /// + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + // Wait till API and Identity are started + await Task.Delay(20000); + + while(!stoppingToken.IsCancellationRequested) + { + AlgorithmActivityJob(); + + // Time between job. + await Task.Delay(config.JobSchedulerConfig.TimeBetweenJobsInMs, stoppingToken); + } + } + + /// + /// Executes the request for the activity algorithm + /// + /// + private void AlgorithmActivityJob() + { + try + { + requestHandler.SetActivityAlgorithmScore(); + + } catch(Exception e) + { + logger.LogCritical(e.InnerException + " " + e.Message); + } + } + + } + +} diff --git a/JobScheduler/ApiRequestHandler.cs b/JobScheduler/ApiRequestHandler.cs index 12ab0408..743d8879 100644 --- a/JobScheduler/ApiRequestHandler.cs +++ b/JobScheduler/ApiRequestHandler.cs @@ -33,6 +33,7 @@ public interface IApiRequestHandler Task> GetExpectedGraduationUsersAsync(); void SetGraduationTaskStatusToMailed(int userTaskId); + void SetActivityAlgorithmScore(); } @@ -69,6 +70,17 @@ public async Task> GetExpectedGraduationUsersAsync() return JsonConvert.DeserializeObject>(await response.Content.ReadAsStringAsync()); } + + /// + /// This is the method which requests the API executes the activity algorithm + /// + /// + public async void SetActivityAlgorithmScore() + { + HttpResponseMessage response = + await client.GetAsync("api/Algorithm/Activity"); + } + /// /// This is the method which sets the user tasks to status 'mailed'. /// diff --git a/JobScheduler/Program.cs b/JobScheduler/Program.cs index b2016ef9..89097c88 100644 --- a/JobScheduler/Program.cs +++ b/JobScheduler/Program.cs @@ -1,16 +1,16 @@ /* * Digital Excellence Copyright (C) 2020 Brend Smits -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. -* -* You can find a copy of the GNU Lesser General Public License +* +* You can find a copy of the GNU Lesser General Public License * along with this program, in the LICENSE.md file in the root project directory. * If not, see https://www.gnu.org/licenses/lgpl-3.0.txt */ @@ -104,6 +104,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) services.AddScoped(); services.AddScoped(); services.AddHostedService(); + services.AddHostedService(); services.AddSingleton(config); }); } From 9be666d9299a1d961b7cd2abd3eb484eed4eb6b9 Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Mon, 13 Dec 2021 13:02:25 +0100 Subject: [PATCH 16/22] feat: added summaries to functions --- Repositories/ProjectRepository.cs | 8 ++++++++ Services/Services/ProjectService.cs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/Repositories/ProjectRepository.cs b/Repositories/ProjectRepository.cs index 65c31f98..54637f2f 100644 --- a/Repositories/ProjectRepository.cs +++ b/Repositories/ProjectRepository.cs @@ -151,6 +151,10 @@ Task> GetUserProjects(int userId, /// Task> FindProjectsWhereTitleStartsWithQuery(string query); + /// + /// This is a update method to only update the activity score + /// + /// void UpdateActivityScore(Project entity); } @@ -404,6 +408,10 @@ public override void Update(Project entity) } + /// + /// This is a update method to only update the activity score + /// + /// public void UpdateActivityScore(Project entity) { DbSet.Update(entity); diff --git a/Services/Services/ProjectService.cs b/Services/Services/ProjectService.cs index 5afbf87b..8c25137f 100644 --- a/Services/Services/ProjectService.cs +++ b/Services/Services/ProjectService.cs @@ -97,6 +97,10 @@ public interface IProjectService : IService /// The projects where the title matches the query Task> FindProjectsWhereTitleStartsWithQuery(string query); + /// + /// This is a update method to only update the activity score + /// + /// void UpdateActivityScore(Project entity); } @@ -318,6 +322,10 @@ public async Task FindAsyncNotRedacted(int id) return await Repository.FindAsyncNotRedacted(id); } + /// + /// This is a update method to only update the activity score + /// + /// public void UpdateActivityScore(Project entity) { Repository.UpdateActivityScore(entity); From e0a852ea466312a357e21fde1a08a0949db32bd8 Mon Sep 17 00:00:00 2001 From: Neal Geilen <44571222+NealGeilen@users.noreply.github.com> Date: Mon, 13 Dec 2021 14:30:16 +0100 Subject: [PATCH 17/22] fix: fix for average like date --- Services.Tests/ActivityAlgorithmTest.cs | 3 +-- Services/Services/ActivityAlgorithmDataPoints.cs | 13 ++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Services.Tests/ActivityAlgorithmTest.cs b/Services.Tests/ActivityAlgorithmTest.cs index 1e6a15a2..443cc894 100644 --- a/Services.Tests/ActivityAlgorithmTest.cs +++ b/Services.Tests/ActivityAlgorithmTest.cs @@ -51,7 +51,6 @@ public void ScoreProjectBasedOnLikeDate() Project project = new Project(); project.Likes = new List() { - _projectGeneratorHelper.GetOldLike(), _projectGeneratorHelper.GetOldLike(), _projectGeneratorHelper.GetRecentLike() }; @@ -61,7 +60,7 @@ public void ScoreProjectBasedOnLikeDate() new AverageLikeDateDataPoint() }); - Assert.AreEqual(1, score); + Assert.AreEqual(2, score); } diff --git a/Services/Services/ActivityAlgorithmDataPoints.cs b/Services/Services/ActivityAlgorithmDataPoints.cs index f224ff9c..4ea84b7d 100644 --- a/Services/Services/ActivityAlgorithmDataPoints.cs +++ b/Services/Services/ActivityAlgorithmDataPoints.cs @@ -52,18 +52,13 @@ public override double Calculate(Project project) { if(project.Likes.Count > 0) { - DateTime averageDateTime = DateTime - .MinValue - .AddSeconds - (project.Likes - .Sum(r => (r.Date - DateTime.MinValue).TotalSeconds) - / project.Likes.Count); - double averageLikeDate = Math.Round((DateTime.Now - averageDateTime).TotalDays, 2); - if(averageLikeDate < 14) + DateTime averageDateTime = DateTime.MinValue.AddSeconds(project.Likes.Sum(r => (r.Date - DateTime.MinValue).TotalSeconds) / project.Likes.Count); + int daysAfterLikeDate = (int)(DateTime.Now - averageDateTime).TotalDays; + if(daysAfterLikeDate > 28) { return 2 * Multiplier; } - return Multiplier / averageLikeDate; + return daysAfterLikeDate / Multiplier; } return 0; } From 886948daab91cebd1110bc115ac5ba3c814feb35 Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg <73314842+BartvanEijkelenburg@users.noreply.github.com> Date: Mon, 13 Dec 2021 19:07:14 +0100 Subject: [PATCH 18/22] Added endpoint to update the ActivityMultiplier --- API/Controllers/AlgorithmController.cs | 44 + .../DependencyInjectionExtensions.cs | 1 + .../ActivityAlgorithmInput.cs | 51 ++ Data/ApplicationDbContext.cs | 5 + ...dedActivityAlgorithmMultiplier.Designer.cs | 833 ++++++++++++++++++ ...165010_AddedActivityAlgorithmMultiplier.cs | 36 + .../ApplicationDbContextModelSnapshot.cs | 36 + Models/ActivityAlgorithmMultiplier.cs | 16 + Repositories/ActivityAlgorithmRepository.cs | 39 + Services/Services/ActivityAlgorithmService.cs | 35 +- 10 files changed, 1092 insertions(+), 4 deletions(-) create mode 100644 API/InputOutput/ActivityAlgorithm/ActivityAlgorithmInput.cs create mode 100644 Data/Migrations/20211213165010_AddedActivityAlgorithmMultiplier.Designer.cs create mode 100644 Data/Migrations/20211213165010_AddedActivityAlgorithmMultiplier.cs create mode 100644 Models/ActivityAlgorithmMultiplier.cs create mode 100644 Repositories/ActivityAlgorithmRepository.cs diff --git a/API/Controllers/AlgorithmController.cs b/API/Controllers/AlgorithmController.cs index 443b9c9c..069f961f 100644 --- a/API/Controllers/AlgorithmController.cs +++ b/API/Controllers/AlgorithmController.cs @@ -1,4 +1,5 @@ using API.Extensions; +using API.InputOutput.ActivityAlgorithm; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -65,5 +66,48 @@ await projectRepository.GetAllWithUsersCollaboratorsAndInstitutionsAsync()) return Forbid(); } + + /// + /// + /// + [HttpPut("UpdateActivityMultiplier")] + [Authorize] + public async Task UpdateActivityMutliplier([FromBody] ActivityAlgorithmInput activityAlgorithmInput) + { + bool isAllowed = HttpContext.User.HasClaim("client_role", Defaults.Roles.BackendApplication); + + if(isAllowed == false) + { + User currentUser = await HttpContext.GetContextUser(userService) + .ConfigureAwait(false); + if(currentUser.Role.Name == Defaults.Roles.Administrator) + { + isAllowed = true; + } + } + if(isAllowed && ModelState.IsValid) + { + ActivityAlgorithmMultiplier activityAlgorithmMultiplier = new ActivityAlgorithmMultiplier() + { + Id = 1, + AverageLikeDateMultiplier = activityAlgorithmInput.AverageLikeDateMultiplier, + ConnectedCollaboratorsMultiplier = activityAlgorithmInput.ConnectedCollaboratorsMultiplier, + RecentCreatedDataMultiplier = activityAlgorithmInput.RecentCreatedDataMultiplier, + InstitutionMultiplier = activityAlgorithmInput.InstitutionMultiplier, + LikeDataMultiplier = activityAlgorithmInput.LikeDataMultiplier, + MetaDataMultiplier = activityAlgorithmInput.MetaDataMultiplier, + RepoScoreMultiplier = activityAlgorithmInput.RepoScoreMultiplier, + UpdatedTimeMultiplier = activityAlgorithmInput.UpdatedTimeMultiplier, + }; + activityAlgorithmService.SetActivityAlgorithmMultiplier(activityAlgorithmMultiplier); + return Ok(); + } + if(ModelState.IsValid) + { + return BadRequest(); + } + return Forbid(); + } } } + diff --git a/API/Extensions/DependencyInjectionExtensions.cs b/API/Extensions/DependencyInjectionExtensions.cs index 500e61a9..672bd397 100644 --- a/API/Extensions/DependencyInjectionExtensions.cs +++ b/API/Extensions/DependencyInjectionExtensions.cs @@ -129,6 +129,7 @@ public static IServiceCollection AddServicesAndRepositories(this IServiceCollect services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddExternalDataSources(); diff --git a/API/InputOutput/ActivityAlgorithm/ActivityAlgorithmInput.cs b/API/InputOutput/ActivityAlgorithm/ActivityAlgorithmInput.cs new file mode 100644 index 00000000..07afdc79 --- /dev/null +++ b/API/InputOutput/ActivityAlgorithm/ActivityAlgorithmInput.cs @@ -0,0 +1,51 @@ +using System.ComponentModel.DataAnnotations; + +namespace API.InputOutput.ActivityAlgorithm +{ + /// + /// The class for the ActivityAlgorithmInput + /// + public class ActivityAlgorithmInput + { + /// + /// Multiplier for the LikeDataField + /// + [Required] + public int LikeDataMultiplier { get; set; } + /// + /// Multiplier for the RecentCreatedDataField + /// + [Required] + public int RecentCreatedDataMultiplier { get; set; } + /// + /// Multiplier for the AverageLikeDateField + /// + [Required] + public int AverageLikeDateMultiplier { get; set; } + /// + /// Multiplier for the UpdatedTimeField + /// + [Required] + public int UpdatedTimeMultiplier { get; set; } + /// + /// Multiplier for the InstitutionField + /// + [Required] + public int InstitutionMultiplier { get; set; } + /// + /// Multiplier for the ConnectedCollaboratorsField + /// + [Required] + public int ConnectedCollaboratorsMultiplier { get; set; } + /// + /// Multiplier for the MetaDataField + /// + [Required] + public int MetaDataMultiplier { get; set; } + /// + /// Multiplier for the RepoScoreField + /// + [Required] + public int RepoScoreMultiplier { get; set; } + } +} diff --git a/Data/ApplicationDbContext.cs b/Data/ApplicationDbContext.cs index 948046d5..a2c60c05 100644 --- a/Data/ApplicationDbContext.cs +++ b/Data/ApplicationDbContext.cs @@ -186,6 +186,11 @@ public ApplicationDbContext(DbContextOptions options) : ba /// public DbSet ProjectTransferRequest { get; set; } + /// + /// Gets or sets the ActivityAlgorithmMultiplier which represents the activity algorithm multipliers. + /// + public DbSet ActivityAlgorithmMultiplier { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); diff --git a/Data/Migrations/20211213165010_AddedActivityAlgorithmMultiplier.Designer.cs b/Data/Migrations/20211213165010_AddedActivityAlgorithmMultiplier.Designer.cs new file mode 100644 index 00000000..0edeb8d1 --- /dev/null +++ b/Data/Migrations/20211213165010_AddedActivityAlgorithmMultiplier.Designer.cs @@ -0,0 +1,833 @@ +// +using System; +using Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace _4_Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20211213165010_AddedActivityAlgorithmMultiplier")] + partial class AddedActivityAlgorithmMultiplier + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Models.ActivityAlgorithmMultiplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AverageLikeDateMultiplier") + .HasColumnType("int"); + + b.Property("ConnectedCollaboratorsMultiplier") + .HasColumnType("int"); + + b.Property("InstitutionMultiplier") + .HasColumnType("int"); + + b.Property("LikeDataMultiplier") + .HasColumnType("int"); + + b.Property("MetaDataMultiplier") + .HasColumnType("int"); + + b.Property("RecentCreatedDataMultiplier") + .HasColumnType("int"); + + b.Property("RepoScoreMultiplier") + .HasColumnType("int"); + + b.Property("UpdatedTimeMultiplier") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("ActivityAlgorithmMultiplier"); + }); + + modelBuilder.Entity("Models.CallToAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("OptionValue") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("CallToAction"); + }); + + modelBuilder.Entity("Models.CallToActionOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Type") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("CallToActionOption"); + }); + + modelBuilder.Entity("Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Category"); + }); + + modelBuilder.Entity("Models.Collaborator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("FullName") + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("Role") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.ToTable("Collaborators"); + }); + + modelBuilder.Entity("Models.DataSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Guid") + .HasColumnType("nvarchar(max)"); + + b.Property("IconId") + .HasColumnType("int"); + + b.Property("IsVisible") + .HasColumnType("bit"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("IconId"); + + b.ToTable("DataSource"); + }); + + modelBuilder.Entity("Models.DataSourceWizardPage", b => + { + b.Property("DataSourceId") + .HasColumnType("int"); + + b.Property("WizardPageId") + .HasColumnType("int"); + + b.Property("AuthFlow") + .HasColumnType("bit"); + + b.Property("OrderIndex") + .HasColumnType("int"); + + b.HasKey("DataSourceId", "WizardPageId", "AuthFlow"); + + b.HasIndex("WizardPageId"); + + b.ToTable("DataSourceWizardPage"); + }); + + modelBuilder.Entity("Models.EmbeddedProject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Guid") + .HasColumnType("uniqueidentifier"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UserId"); + + b.ToTable("EmbeddedProject"); + }); + + modelBuilder.Entity("Models.File", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("UploadDateTime") + .HasColumnType("datetime2"); + + b.Property("UploaderId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UploaderId"); + + b.ToTable("File"); + }); + + modelBuilder.Entity("Models.Highlight", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("ProjectId"); + + b.ToTable("Highlight"); + }); + + modelBuilder.Entity("Models.Institution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("IdentityId") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Institution"); + }); + + modelBuilder.Entity("Models.Project", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ActivityScore") + .HasColumnType("float"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutePrivate") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectIconId") + .HasColumnType("int"); + + b.Property("ShortDescription") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Updated") + .HasColumnType("datetime2"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectIconId"); + + b.HasIndex("UserId"); + + b.ToTable("Project"); + }); + + modelBuilder.Entity("Models.ProjectCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("ProjectId"); + + b.ToTable("ProjectCategory"); + }); + + modelBuilder.Entity("Models.ProjectInstitution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("InstitutionId") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId"); + + b.HasIndex("ProjectId"); + + b.ToTable("ProjectInstitution"); + }); + + modelBuilder.Entity("Models.ProjectLike", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("LikedProjectId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("LikedProjectId"); + + b.HasIndex("UserId"); + + b.ToTable("ProjectLike"); + }); + + modelBuilder.Entity("Models.ProjectTransferRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CurrentOwnerAcceptedRequest") + .HasColumnType("bit"); + + b.Property("PotentialNewOwnerAcceptedRequest") + .HasColumnType("bit"); + + b.Property("PotentialNewOwnerId") + .HasColumnType("int"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TransferGuid") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("PotentialNewOwnerId"); + + b.HasIndex("ProjectId"); + + b.ToTable("ProjectTransferRequest"); + }); + + modelBuilder.Entity("Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Role"); + }); + + modelBuilder.Entity("Models.RoleScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("Scope") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleScope"); + }); + + modelBuilder.Entity("Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccountCreationDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ExpectedGraduationDate") + .HasColumnType("datetime2"); + + b.Property("IdentityId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InstitutionId") + .HasColumnType("int"); + + b.Property("IsPublic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProfileUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId"); + + b.HasIndex("RoleId"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Models.UserProject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProjectId"); + + b.HasIndex("UserId"); + + b.ToTable("UserProject"); + }); + + modelBuilder.Entity("Models.UserTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserTask"); + }); + + modelBuilder.Entity("Models.UserUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("FollowedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("FollowedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("UserUser"); + }); + + modelBuilder.Entity("Models.WizardPage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("WizardPage"); + }); + + modelBuilder.Entity("Models.CallToAction", b => + { + b.HasOne("Models.Project", null) + .WithMany("CallToActions") + .HasForeignKey("ProjectId"); + }); + + modelBuilder.Entity("Models.Collaborator", b => + { + b.HasOne("Models.Project", null) + .WithMany("Collaborators") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.DataSource", b => + { + b.HasOne("Models.File", "Icon") + .WithMany() + .HasForeignKey("IconId"); + }); + + modelBuilder.Entity("Models.DataSourceWizardPage", b => + { + b.HasOne("Models.DataSource", "DataSource") + .WithMany("DataSourceWizardPages") + .HasForeignKey("DataSourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.WizardPage", "WizardPage") + .WithMany("DataSourceWizardPages") + .HasForeignKey("WizardPageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.EmbeddedProject", b => + { + b.HasOne("Models.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.File", b => + { + b.HasOne("Models.Project", null) + .WithMany("Images") + .HasForeignKey("ProjectId"); + + b.HasOne("Models.User", "Uploader") + .WithMany() + .HasForeignKey("UploaderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.Highlight", b => + { + b.HasOne("Models.File", "Image") + .WithMany() + .HasForeignKey("ImageId"); + + b.HasOne("Models.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.Project", b => + { + b.HasOne("Models.File", "ProjectIcon") + .WithMany() + .HasForeignKey("ProjectIconId"); + + b.HasOne("Models.User", "User") + .WithMany("Projects") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.ProjectCategory", b => + { + b.HasOne("Models.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.Project", "Project") + .WithMany("Categories") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.ProjectInstitution", b => + { + b.HasOne("Models.Institution", "Institution") + .WithMany() + .HasForeignKey("InstitutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.Project", "Project") + .WithMany("LinkedInstitutions") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.ProjectLike", b => + { + b.HasOne("Models.Project", "LikedProject") + .WithMany("Likes") + .HasForeignKey("LikedProjectId"); + + b.HasOne("Models.User", "ProjectLiker") + .WithMany("LikedProjectsByUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.ProjectTransferRequest", b => + { + b.HasOne("Models.User", "PotentialNewOwner") + .WithMany() + .HasForeignKey("PotentialNewOwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.RoleScope", b => + { + b.HasOne("Models.Role", null) + .WithMany("Scopes") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.User", b => + { + b.HasOne("Models.Institution", "Institution") + .WithMany() + .HasForeignKey("InstitutionId"); + + b.HasOne("Models.Role", "Role") + .WithMany() + .HasForeignKey("RoleId"); + }); + + modelBuilder.Entity("Models.UserProject", b => + { + b.HasOne("Models.Project", "Project") + .WithMany() + .HasForeignKey("ProjectId"); + + b.HasOne("Models.User", "User") + .WithMany("UserProject") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Models.UserTask", b => + { + b.HasOne("Models.User", "User") + .WithMany("UserTasks") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Models.UserUser", b => + { + b.HasOne("Models.User", "FollowedUser") + .WithMany("FollowedUsers") + .HasForeignKey("FollowedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/20211213165010_AddedActivityAlgorithmMultiplier.cs b/Data/Migrations/20211213165010_AddedActivityAlgorithmMultiplier.cs new file mode 100644 index 00000000..48eb889b --- /dev/null +++ b/Data/Migrations/20211213165010_AddedActivityAlgorithmMultiplier.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace _4_Data.Migrations +{ + public partial class AddedActivityAlgorithmMultiplier : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ActivityAlgorithmMultiplier", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + LikeDataMultiplier = table.Column(nullable: false), + RecentCreatedDataMultiplier = table.Column(nullable: false), + AverageLikeDateMultiplier = table.Column(nullable: false), + UpdatedTimeMultiplier = table.Column(nullable: false), + InstitutionMultiplier = table.Column(nullable: false), + ConnectedCollaboratorsMultiplier = table.Column(nullable: false), + MetaDataMultiplier = table.Column(nullable: false), + RepoScoreMultiplier = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ActivityAlgorithmMultiplier", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ActivityAlgorithmMultiplier"); + } + } +} diff --git a/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/Data/Migrations/ApplicationDbContextModelSnapshot.cs index eab3d632..4fbd4fd4 100644 --- a/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -19,6 +19,42 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + modelBuilder.Entity("Models.ActivityAlgorithmMultiplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AverageLikeDateMultiplier") + .HasColumnType("int"); + + b.Property("ConnectedCollaboratorsMultiplier") + .HasColumnType("int"); + + b.Property("InstitutionMultiplier") + .HasColumnType("int"); + + b.Property("LikeDataMultiplier") + .HasColumnType("int"); + + b.Property("MetaDataMultiplier") + .HasColumnType("int"); + + b.Property("RecentCreatedDataMultiplier") + .HasColumnType("int"); + + b.Property("RepoScoreMultiplier") + .HasColumnType("int"); + + b.Property("UpdatedTimeMultiplier") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("ActivityAlgorithmMultiplier"); + }); + modelBuilder.Entity("Models.CallToAction", b => { b.Property("Id") diff --git a/Models/ActivityAlgorithmMultiplier.cs b/Models/ActivityAlgorithmMultiplier.cs new file mode 100644 index 00000000..2895a33a --- /dev/null +++ b/Models/ActivityAlgorithmMultiplier.cs @@ -0,0 +1,16 @@ +namespace Models +{ + + public class ActivityAlgorithmMultiplier + { + public int Id { get; set; } + public int LikeDataMultiplier { get; set; } + public int RecentCreatedDataMultiplier { get; set; } + public int AverageLikeDateMultiplier { get; set; } + public int UpdatedTimeMultiplier { get; set; } + public int InstitutionMultiplier { get; set; } + public int ConnectedCollaboratorsMultiplier { get; set; } + public int MetaDataMultiplier { get; set; } + public int RepoScoreMultiplier { get; set; } + } +} diff --git a/Repositories/ActivityAlgorithmRepository.cs b/Repositories/ActivityAlgorithmRepository.cs new file mode 100644 index 00000000..c3fd13d3 --- /dev/null +++ b/Repositories/ActivityAlgorithmRepository.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore; +using Models; +using Repositories.Base; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Repositories +{ + public interface IActivityAlgorithmRepository + { + Task GetActivityAlgorithmMultiplierAsync(); + void UpdateActivityAlgorithmMultiplierAsync(ActivityAlgorithmMultiplier activityAlgorithmMultiplier); + } + public class ActivityAlgorithmRepository : IActivityAlgorithmRepository + { + private readonly DbContext dbContext; + /// + /// Initializes a new instance of the class. + /// + /// The database context. + public ActivityAlgorithmRepository(DbContext dbContext) + { + this.dbContext = dbContext; + } + + public async Task GetActivityAlgorithmMultiplierAsync() + { + return await dbContext.Set().AsNoTracking().FirstOrDefaultAsync(); + } + + public void UpdateActivityAlgorithmMultiplierAsync(ActivityAlgorithmMultiplier activityAlgorithmMultiplier) + { + dbContext.Set().Update(activityAlgorithmMultiplier); + dbContext.SaveChanges(); + } + } +} diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index a71a6d52..9c81d009 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -14,12 +14,14 @@ public interface IActivityAlgorithmService List CalculateAllProjects(IEnumerable projects); double CalculateProjectActivityScore(Project project, List dataPoints); void SetProjectActivityScore(Project project, double score); + ActivityAlgorithmMultiplier GetActivityAlgorithmMultiplier(); + void SetActivityAlgorithmMultiplier(ActivityAlgorithmMultiplier activityAlgorithmMultiplier); } public class ActivityAlgorithmService : IActivityAlgorithmService { - private readonly List _dataPoints + private List dataPoints = new List() { new LikeDataPoint(1), @@ -33,6 +35,24 @@ private readonly List _dataPoints }; private readonly IProjectService? projectService; + private readonly IActivityAlgorithmRepository activityAlgorithmRepository; + public ActivityAlgorithmService(IActivityAlgorithmRepository activityAlgorithmRepository, IProjectService projectService) + { + this.projectService = projectService; + this.activityAlgorithmRepository = activityAlgorithmRepository; + ActivityAlgorithmMultiplier multiplier = GetActivityAlgorithmMultiplier(); + dataPoints = new List() + { + new LikeDataPoint(multiplier.LikeDataMultiplier), + new RecentCreatedDataPoint(multiplier.RecentCreatedDataMultiplier), + new AverageLikeDateDataPoint(multiplier.AverageLikeDateMultiplier), + new UpdatedTimeDataPoint(multiplier.UpdatedTimeMultiplier), + new InstitutionDataPoint(multiplier.InstitutionMultiplier), + new ConnectedCollaboratorsDataPoint(multiplier.ConnectedCollaboratorsMultiplier), + new MetaDataDataPoint(multiplier.MetaDataMultiplier), + new RepoScoreDataPoint(multiplier.RepoScoreMultiplier), + }; + } public ActivityAlgorithmService(IProjectService? projectService = null) { this.projectService = projectService; @@ -52,7 +72,7 @@ public List CalculateAllProjects(IEnumerable projects) public double CalculateProjectActivityScore(Project project, List dataPoints = null) { if(dataPoints == null) - dataPoints = _dataPoints; + dataPoints = this.dataPoints; return Math.Round(dataPoints.Sum(dataPoint => dataPoint.Calculate(project)), 2); } @@ -61,7 +81,14 @@ public void SetProjectActivityScore(Project project, double score) project.ActivityScore = score; projectService?.UpdateActivityScore(project); } + public void SetActivityAlgorithmMultiplier(ActivityAlgorithmMultiplier activityAlgorithmMultiplier) + { + activityAlgorithmRepository.UpdateActivityAlgorithmMultiplierAsync(activityAlgorithmMultiplier); + } + public ActivityAlgorithmMultiplier GetActivityAlgorithmMultiplier() + { + ActivityAlgorithmMultiplier activityAlgorithmMultiplier = activityAlgorithmRepository.GetActivityAlgorithmMultiplierAsync().Result; + return activityAlgorithmMultiplier; + } } - - } From a0a5d7044168c9b517e8bbbd472d3172e11ff3ad Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg <73314842+BartvanEijkelenburg@users.noreply.github.com> Date: Tue, 14 Dec 2021 10:37:41 +0100 Subject: [PATCH 19/22] Improved Tests and algorithm. --- API/Controllers/AlgorithmController.cs | 4 +- API/Program.cs | 2 +- API/Startup.cs | 5 ++ Data/ApplicationDbContext.cs | 2 +- Data/Helpers/Seed.cs | 14 ++++++ ...Multiplier.cs => ProjectActivityConfig.cs} | 2 +- Repositories/ActivityAlgorithmRepository.cs | 30 ++++++++--- Services.Tests/ActivityAlgorithmTest.cs | 50 ++++++++++++------- Services/Services/ActivityAlgorithmService.cs | 35 ++++++------- 9 files changed, 97 insertions(+), 47 deletions(-) rename Models/{ActivityAlgorithmMultiplier.cs => ProjectActivityConfig.cs} (92%) diff --git a/API/Controllers/AlgorithmController.cs b/API/Controllers/AlgorithmController.cs index 069f961f..d15b5ff1 100644 --- a/API/Controllers/AlgorithmController.cs +++ b/API/Controllers/AlgorithmController.cs @@ -87,7 +87,7 @@ public async Task UpdateActivityMutliplier([FromBody] ActivityAlg } if(isAllowed && ModelState.IsValid) { - ActivityAlgorithmMultiplier activityAlgorithmMultiplier = new ActivityAlgorithmMultiplier() + ProjectActivityConfig projectActivityConfig = new ProjectActivityConfig() { Id = 1, AverageLikeDateMultiplier = activityAlgorithmInput.AverageLikeDateMultiplier, @@ -99,7 +99,7 @@ public async Task UpdateActivityMutliplier([FromBody] ActivityAlg RepoScoreMultiplier = activityAlgorithmInput.RepoScoreMultiplier, UpdatedTimeMultiplier = activityAlgorithmInput.UpdatedTimeMultiplier, }; - activityAlgorithmService.SetActivityAlgorithmMultiplier(activityAlgorithmMultiplier); + activityAlgorithmService.SetActivityAlgorithmMultiplier(projectActivityConfig); return Ok(); } if(ModelState.IsValid) diff --git a/API/Program.cs b/API/Program.cs index f4328977..63041394 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -39,7 +39,7 @@ public static class Program /// The arguments. /// The exit code of the program. public static int Main(string[] args) - { + { Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) diff --git a/API/Startup.cs b/API/Startup.cs index f0a78b03..72672a02 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -583,6 +583,11 @@ private static void UpdateDatabase(IApplicationBuilder app, IWebHostEnvironment context.SaveChanges(); } + if(!context.ActivityAlgorithmMultiplier.Any()) + { + context.ActivityAlgorithmMultiplier.AddRange(Seed.SeedActivityAlgorithmMultiplier()); + context.SaveChanges(); + } SeedHelper.SeedDataSourceWizardPages(context); } diff --git a/Data/ApplicationDbContext.cs b/Data/ApplicationDbContext.cs index a2c60c05..677f27e3 100644 --- a/Data/ApplicationDbContext.cs +++ b/Data/ApplicationDbContext.cs @@ -189,7 +189,7 @@ public ApplicationDbContext(DbContextOptions options) : ba /// /// Gets or sets the ActivityAlgorithmMultiplier which represents the activity algorithm multipliers. /// - public DbSet ActivityAlgorithmMultiplier { get; set; } + public DbSet ActivityAlgorithmMultiplier { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/Data/Helpers/Seed.cs b/Data/Helpers/Seed.cs index dd7b52e2..a4ee8098 100644 --- a/Data/Helpers/Seed.cs +++ b/Data/Helpers/Seed.cs @@ -544,6 +544,20 @@ public static List SeedDataSources() }; } + public static ProjectActivityConfig SeedActivityAlgorithmMultiplier() + { + return new ProjectActivityConfig() + { + AverageLikeDateMultiplier = 1, + ConnectedCollaboratorsMultiplier = 1, + RecentCreatedDataMultiplier = 1, + InstitutionMultiplier = 1, + LikeDataMultiplier = 1, + MetaDataMultiplier = 1, + RepoScoreMultiplier = 1, + UpdatedTimeMultiplier = 1 + }; + } public static User SeedAdminUser2(List roles) { Role adminRole = roles.Find(i => i.Name == nameof(Defaults.Roles.Administrator)); diff --git a/Models/ActivityAlgorithmMultiplier.cs b/Models/ProjectActivityConfig.cs similarity index 92% rename from Models/ActivityAlgorithmMultiplier.cs rename to Models/ProjectActivityConfig.cs index 2895a33a..b11ac568 100644 --- a/Models/ActivityAlgorithmMultiplier.cs +++ b/Models/ProjectActivityConfig.cs @@ -1,7 +1,7 @@ namespace Models { - public class ActivityAlgorithmMultiplier + public class ProjectActivityConfig { public int Id { get; set; } public int LikeDataMultiplier { get; set; } diff --git a/Repositories/ActivityAlgorithmRepository.cs b/Repositories/ActivityAlgorithmRepository.cs index c3fd13d3..6829095d 100644 --- a/Repositories/ActivityAlgorithmRepository.cs +++ b/Repositories/ActivityAlgorithmRepository.cs @@ -10,8 +10,8 @@ namespace Repositories { public interface IActivityAlgorithmRepository { - Task GetActivityAlgorithmMultiplierAsync(); - void UpdateActivityAlgorithmMultiplierAsync(ActivityAlgorithmMultiplier activityAlgorithmMultiplier); + Task GetActivityAlgorithmConfig(); + void UpdateActivityAlgorithmConfig(ProjectActivityConfig projectActivityConfig); } public class ActivityAlgorithmRepository : IActivityAlgorithmRepository { @@ -25,14 +25,32 @@ public ActivityAlgorithmRepository(DbContext dbContext) this.dbContext = dbContext; } - public async Task GetActivityAlgorithmMultiplierAsync() + public async Task GetActivityAlgorithmConfig() { - return await dbContext.Set().AsNoTracking().FirstOrDefaultAsync(); + ProjectActivityConfig projectActivityConfig = await dbContext.Set().AsNoTracking().FirstOrDefaultAsync(); + if(projectActivityConfig == null) + { + ProjectActivityConfig newProjectActivityConfig = new ProjectActivityConfig() + { + AverageLikeDateMultiplier = 1, + ConnectedCollaboratorsMultiplier = 1, + RecentCreatedDataMultiplier = 1, + InstitutionMultiplier = 1, + LikeDataMultiplier = 1, + MetaDataMultiplier = 1, + RepoScoreMultiplier = 1, + UpdatedTimeMultiplier = 1 + }; + dbContext.Set().Add(newProjectActivityConfig); + dbContext.SaveChanges(); + return newProjectActivityConfig; + } + return projectActivityConfig; } - public void UpdateActivityAlgorithmMultiplierAsync(ActivityAlgorithmMultiplier activityAlgorithmMultiplier) + public void UpdateActivityAlgorithmConfig(ProjectActivityConfig projectActivityConfig) { - dbContext.Set().Update(activityAlgorithmMultiplier); + dbContext.Set().Update(projectActivityConfig); dbContext.SaveChanges(); } } diff --git a/Services.Tests/ActivityAlgorithmTest.cs b/Services.Tests/ActivityAlgorithmTest.cs index 443cc894..f4003631 100644 --- a/Services.Tests/ActivityAlgorithmTest.cs +++ b/Services.Tests/ActivityAlgorithmTest.cs @@ -8,6 +8,8 @@ using System; using System.Collections.Generic; using System.Linq; +using Repositories; +using System.Threading.Tasks; namespace Services.Tests { @@ -15,13 +17,27 @@ namespace Services.Tests public class ActivityAlgorithmTest { - private IActivityAlgorithmService _activityAlgorithmService; - private ProjectGeneratorHelper _projectGeneratorHelper; + private readonly IActivityAlgorithmService activityAlgorithmService; + private readonly ProjectGeneratorHelper projectGeneratorHelper; public ActivityAlgorithmTest() { - _activityAlgorithmService = new ActivityAlgorithmService(); - _projectGeneratorHelper = new ProjectGeneratorHelper(); + Mock projectServiceMock = new Mock(); + Mock activityRepoMock = new Mock(); + activityRepoMock.Setup(m => m.GetActivityAlgorithmConfig()).Returns(Task.FromResult(new ProjectActivityConfig() + { + AverageLikeDateMultiplier = 1, + ConnectedCollaboratorsMultiplier = 1, + RecentCreatedDataMultiplier = 1, + InstitutionMultiplier = 1, + LikeDataMultiplier = 1, + MetaDataMultiplier = 1, + RepoScoreMultiplier = 1, + UpdatedTimeMultiplier = 1 + })); + projectServiceMock.Setup(m => m.UpdateActivityScore((new Mock()).Object)); + activityAlgorithmService = new ActivityAlgorithmService(activityRepoMock.Object, projectServiceMock.Object); + projectGeneratorHelper = new ProjectGeneratorHelper(); } [Test] @@ -30,12 +46,12 @@ public void ScoreProjectBasedOnLikes() Project project = new Project(); project.Likes = new List() { - _projectGeneratorHelper.GetOldLike(), - _projectGeneratorHelper.GetOldLike(), - _projectGeneratorHelper.GetRecentLike() + projectGeneratorHelper.GetOldLike(), + projectGeneratorHelper.GetOldLike(), + projectGeneratorHelper.GetRecentLike() }; - double score = _activityAlgorithmService.CalculateProjectActivityScore(project, new List + double score = activityAlgorithmService.CalculateProjectActivityScore(project, new List { new LikeDataPoint() }); @@ -51,11 +67,11 @@ public void ScoreProjectBasedOnLikeDate() Project project = new Project(); project.Likes = new List() { - _projectGeneratorHelper.GetOldLike(), - _projectGeneratorHelper.GetRecentLike() + projectGeneratorHelper.GetOldLike(), + projectGeneratorHelper.GetRecentLike() }; - double score = _activityAlgorithmService.CalculateProjectActivityScore(project, new List + double score = activityAlgorithmService.CalculateProjectActivityScore(project, new List { new AverageLikeDateDataPoint() }); @@ -79,7 +95,7 @@ public void ScoreProjectBasedOnConnectedCollaborators() new Collaborator(), }; - double score = _activityAlgorithmService.CalculateProjectActivityScore(project, new List + double score = activityAlgorithmService.CalculateProjectActivityScore(project, new List { new ConnectedCollaboratorsDataPoint() }); @@ -92,10 +108,10 @@ public void ScoreProjectBasedOnConnectedCollaborators() [Test] public void OrderProjectsWithAlgorithm() { - Project firstProject = _projectGeneratorHelper.GetActiveProject(); - Project secondProject = _projectGeneratorHelper.GetInactiveProject(10); - Project thirdProject = _projectGeneratorHelper.GetInactiveProject(); - Project fourthProject = _projectGeneratorHelper.GetActiveProject(10); + Project firstProject = projectGeneratorHelper.GetActiveProject(); + Project secondProject = projectGeneratorHelper.GetInactiveProject(10); + Project thirdProject = projectGeneratorHelper.GetInactiveProject(); + Project fourthProject = projectGeneratorHelper.GetActiveProject(10); IEnumerable projects = new List() { firstProject, @@ -104,7 +120,7 @@ public void OrderProjectsWithAlgorithm() fourthProject }; - List orderedProjects = _activityAlgorithmService.CalculateAllProjects(projects) + List orderedProjects = activityAlgorithmService.CalculateAllProjects(projects) .OrderByDescending(p => p.ActivityScore) .ToList(); diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index 9c81d009..13885f2f 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -14,8 +14,8 @@ public interface IActivityAlgorithmService List CalculateAllProjects(IEnumerable projects); double CalculateProjectActivityScore(Project project, List dataPoints); void SetProjectActivityScore(Project project, double score); - ActivityAlgorithmMultiplier GetActivityAlgorithmMultiplier(); - void SetActivityAlgorithmMultiplier(ActivityAlgorithmMultiplier activityAlgorithmMultiplier); + ProjectActivityConfig GetActivityAlgorithmMultiplier(); + void SetActivityAlgorithmMultiplier(ProjectActivityConfig projectActivityConfig); } public class ActivityAlgorithmService : IActivityAlgorithmService @@ -34,13 +34,18 @@ private List dataPoints new RepoScoreDataPoint(1), }; - private readonly IProjectService? projectService; + private readonly IProjectService projectService; private readonly IActivityAlgorithmRepository activityAlgorithmRepository; public ActivityAlgorithmService(IActivityAlgorithmRepository activityAlgorithmRepository, IProjectService projectService) { this.projectService = projectService; this.activityAlgorithmRepository = activityAlgorithmRepository; - ActivityAlgorithmMultiplier multiplier = GetActivityAlgorithmMultiplier(); + } + + + public List CalculateAllProjects(IEnumerable projects) + { + ProjectActivityConfig multiplier = GetActivityAlgorithmMultiplier(); dataPoints = new List() { new LikeDataPoint(multiplier.LikeDataMultiplier), @@ -52,20 +57,12 @@ public ActivityAlgorithmService(IActivityAlgorithmRepository activityAlgorithmRe new MetaDataDataPoint(multiplier.MetaDataMultiplier), new RepoScoreDataPoint(multiplier.RepoScoreMultiplier), }; - } - public ActivityAlgorithmService(IProjectService? projectService = null) - { - this.projectService = projectService; - } - - public List CalculateAllProjects(IEnumerable projects) - { foreach(Project project in projects) { double score = CalculateProjectActivityScore(project); SetProjectActivityScore(project, score); } - projectService?.Save(); + projectService.Save(); return projects.ToList(); } @@ -79,16 +76,16 @@ public double CalculateProjectActivityScore(Project project, List Date: Tue, 14 Dec 2021 10:42:55 +0100 Subject: [PATCH 20/22] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24a52315..acd5d146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Release v.2.0.0-beta - DATE HERE ### Added - Added JobScheduler unit tests - [#381](https://github.com/DigitalExcellence/dex-backend/pull/539) - +- Added a way to order inactive projects - [#522](https://github.com/DigitalExcellence/dex-backend/issues/522) ## Release v.1.9.0-beta - 25-11-2021 ### Added From d7755b476419c966c6e1747021e7b4209590522b Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg Date: Wed, 15 Dec 2021 11:41:38 +0100 Subject: [PATCH 21/22] Added comments --- API/Controllers/AlgorithmController.cs | 3 +- Repositories/ActivityAlgorithmRepository.cs | 24 +++++++------ Services/Services/ActivityAlgorithmService.cs | 35 +++++++++++++++++-- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/API/Controllers/AlgorithmController.cs b/API/Controllers/AlgorithmController.cs index d15b5ff1..83937d02 100644 --- a/API/Controllers/AlgorithmController.cs +++ b/API/Controllers/AlgorithmController.cs @@ -68,8 +68,9 @@ await projectRepository.GetAllWithUsersCollaboratorsAndInstitutionsAsync()) } /// - /// + /// This endpoint is used to update the activity multiplier /// + /// The input model of the activity algorithm multipliers [HttpPut("UpdateActivityMultiplier")] [Authorize] public async Task UpdateActivityMutliplier([FromBody] ActivityAlgorithmInput activityAlgorithmInput) diff --git a/Repositories/ActivityAlgorithmRepository.cs b/Repositories/ActivityAlgorithmRepository.cs index 6829095d..e3f12dba 100644 --- a/Repositories/ActivityAlgorithmRepository.cs +++ b/Repositories/ActivityAlgorithmRepository.cs @@ -8,26 +8,28 @@ namespace Repositories { - public interface IActivityAlgorithmRepository + public interface IActivityAlgorithmRepository : IRepository { Task GetActivityAlgorithmConfig(); void UpdateActivityAlgorithmConfig(ProjectActivityConfig projectActivityConfig); } - public class ActivityAlgorithmRepository : IActivityAlgorithmRepository + public class ActivityAlgorithmRepository : Repository, IActivityAlgorithmRepository { private readonly DbContext dbContext; /// /// Initializes a new instance of the class. /// /// The database context. - public ActivityAlgorithmRepository(DbContext dbContext) - { - this.dbContext = dbContext; - } + public ActivityAlgorithmRepository(DbContext dbContext) : base(dbContext) { } + /// + /// Gets the config for the activity algorithm + /// + /// The project activity config public async Task GetActivityAlgorithmConfig() { - ProjectActivityConfig projectActivityConfig = await dbContext.Set().AsNoTracking().FirstOrDefaultAsync(); + DbSet dbSet = GetDbSet(); + ProjectActivityConfig projectActivityConfig = await dbSet.AsNoTracking().FirstOrDefaultAsync(); if(projectActivityConfig == null) { ProjectActivityConfig newProjectActivityConfig = new ProjectActivityConfig() @@ -41,8 +43,8 @@ public async Task GetActivityAlgorithmConfig() RepoScoreMultiplier = 1, UpdatedTimeMultiplier = 1 }; - dbContext.Set().Add(newProjectActivityConfig); - dbContext.SaveChanges(); + dbSet.Add(newProjectActivityConfig); + base.Save(); return newProjectActivityConfig; } return projectActivityConfig; @@ -50,8 +52,8 @@ public async Task GetActivityAlgorithmConfig() public void UpdateActivityAlgorithmConfig(ProjectActivityConfig projectActivityConfig) { - dbContext.Set().Update(projectActivityConfig); - dbContext.SaveChanges(); + GetDbSet().Update(projectActivityConfig); + base.Save(); } } } diff --git a/Services/Services/ActivityAlgorithmService.cs b/Services/Services/ActivityAlgorithmService.cs index 13885f2f..52e86082 100644 --- a/Services/Services/ActivityAlgorithmService.cs +++ b/Services/Services/ActivityAlgorithmService.cs @@ -6,6 +6,7 @@ using Repositories; using System.Linq; using System.Threading.Tasks; +using Services.Base; namespace Services.Services { @@ -36,13 +37,23 @@ private List dataPoints private readonly IProjectService projectService; private readonly IActivityAlgorithmRepository activityAlgorithmRepository; + + /// + /// Constructor for the activity algorithm service + /// + /// Initializes the activity algorithm repository + /// Initializes the project service public ActivityAlgorithmService(IActivityAlgorithmRepository activityAlgorithmRepository, IProjectService projectService) { this.projectService = projectService; this.activityAlgorithmRepository = activityAlgorithmRepository; } - + /// + /// Calculate all the projects + /// + /// Update list of projects. + /// A list of projects public List CalculateAllProjects(IEnumerable projects) { ProjectActivityConfig multiplier = GetActivityAlgorithmMultiplier(); @@ -65,23 +76,41 @@ public List CalculateAllProjects(IEnumerable projects) projectService.Save(); return projects.ToList(); } - + /// + /// Calculate the score per project + /// + /// The project that has to be calculated + /// The datapoints whether it should look at. + /// the project score public double CalculateProjectActivityScore(Project project, List dataPoints = null) { if(dataPoints == null) dataPoints = this.dataPoints; return Math.Round(dataPoints.Sum(dataPoint => dataPoint.Calculate(project)), 2); } - + /// + /// Sets the project score + /// + /// The project which score needs to be set. + /// The score of the project public void SetProjectActivityScore(Project project, double score) { project.ActivityScore = score; projectService.UpdateActivityScore(project); } + /// + /// Updates the project activity config. + /// + /// The new config public void SetActivityAlgorithmMultiplier(ProjectActivityConfig projectActivityConfig) { activityAlgorithmRepository.UpdateActivityAlgorithmConfig(projectActivityConfig); + } + /// + /// Gets the activity algorithm multiplier + /// + /// Project activity configuration. public ProjectActivityConfig GetActivityAlgorithmMultiplier() { ProjectActivityConfig projectActivityConfig = activityAlgorithmRepository.GetActivityAlgorithmConfig().Result; From 7fc1c90a3e690b6b50a026f90debaadd37bce77f Mon Sep 17 00:00:00 2001 From: Bart van Eijkelenburg Date: Wed, 15 Dec 2021 13:34:52 +0100 Subject: [PATCH 22/22] Fixed warnings.... --- API/Controllers/AlgorithmController.cs | 1 + Repositories/ActivityAlgorithmRepository.cs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/API/Controllers/AlgorithmController.cs b/API/Controllers/AlgorithmController.cs index 83937d02..9dd8ba95 100644 --- a/API/Controllers/AlgorithmController.cs +++ b/API/Controllers/AlgorithmController.cs @@ -31,6 +31,7 @@ public class AlgorithmController : ControllerBase /// /// The category service which is used to communicate with the logic layer. /// The project category service which is used to communicate with the logic layer. + /// The userService to get the current user. public AlgorithmController(IActivityAlgorithmService activityAlgorithmService, IProjectRepository projectRepository, IUserService userService) { this.activityAlgorithmService = activityAlgorithmService; diff --git a/Repositories/ActivityAlgorithmRepository.cs b/Repositories/ActivityAlgorithmRepository.cs index e3f12dba..a84a8be0 100644 --- a/Repositories/ActivityAlgorithmRepository.cs +++ b/Repositories/ActivityAlgorithmRepository.cs @@ -15,7 +15,6 @@ public interface IActivityAlgorithmRepository : IRepository, IActivityAlgorithmRepository { - private readonly DbContext dbContext; /// /// Initializes a new instance of the class. ///