diff --git a/CodeModel.Tests/Integration/CodeModelFactoryTests.cs b/CodeModel.Tests/Integration/CodeModelFactoryTests.cs index 4a137319..891776c2 100644 --- a/CodeModel.Tests/Integration/CodeModelFactoryTests.cs +++ b/CodeModel.Tests/Integration/CodeModelFactoryTests.cs @@ -74,6 +74,17 @@ public void Create_code_model_with_links(string folderLocation) difference.ShouldBeEmpty(); } + [Fact] + public void Create_code_model_with_links_with_partial_classes() + { + CodeModelFactory factory = new CodeModelFactory(true); + var project = factory.CreateProjectWithCodeFileLinks("C:/sdataset-partial"); + + var projectClassesAndMembersCount = project.Classes.Count + project.Classes.Sum(c => c.Members.Count); + + project.CodeLinks.Count.ShouldBe(projectClassesAndMembersCount); + } + [Fact] public void Create_code_model_with_links_on_large_repo() { @@ -93,5 +104,27 @@ public void Create_code_model_with_links_on_large_repo() locationLink.StartLoC.ShouldBe(15); locationLink.EndLoC.ShouldBe(16); } + + [Theory] + //[InlineData("C:\\temp\\challenge-sandbox\\start\\01. Structural Cohesion")] + //[InlineData("C:\\temp\\challenge-sandbox\\end\\01. structural")] + //[InlineData("C:\\temp\\challenge-sandbox\\start\\03. Coupling\\Employees")] + //[InlineData("C:\\temp\\challenge-sandbox\\end\\03. coupling employee")] + //[InlineData("C:\\temp\\challenge-sandbox\\start\\03. Coupling\\Rental")] + //[InlineData("C:\\temp\\challenge-sandbox\\end\\03. coupling rental")] + //[InlineData("C:\\temp\\challenge-sandbox\\start\\04. SRP\\Achievements")] + //[InlineData("C:\\temp\\challenge-sandbox\\end\\04. srp achievements")] + [InlineData("C:\\temp\\challenge-sandbox\\start\\04. SRP\\Books")] + [InlineData("C:\\temp\\challenge-sandbox\\end\\04. srp books")] + public void Create_code_model(string folderLocation) + { + CodeModelFactory factory = new CodeModelFactory(); + var project = factory.CreateProjectWithCodeFileLinks(folderLocation); + + var classes = project.Classes; + var members = project.Classes.SelectMany(c => c.Members); + + var firstClassMetrics = classes.First().Metrics; + } } } diff --git a/CodeModel/CaDETModel/CodeItems/CaDETClass.cs b/CodeModel/CaDETModel/CodeItems/CaDETClass.cs index b2911f3c..43f9fcc2 100644 --- a/CodeModel/CaDETModel/CodeItems/CaDETClass.cs +++ b/CodeModel/CaDETModel/CodeItems/CaDETClass.cs @@ -27,6 +27,7 @@ public string ContainerName public List Members { get; internal set; } public List Fields { get; internal set; } public Dictionary Metrics { get; set; } + public List Subclasses { get; set; } public CaDETClass(string name) { diff --git a/CodeModel/CaDETModel/CodeItems/CaDETModifier.cs b/CodeModel/CaDETModel/CodeItems/CaDETModifier.cs index 0496ea47..69ea6136 100644 --- a/CodeModel/CaDETModel/CodeItems/CaDETModifier.cs +++ b/CodeModel/CaDETModel/CodeItems/CaDETModifier.cs @@ -1,5 +1,4 @@ -using CodeModel.CodeParsers.CSharp.Exceptions; -using System.ComponentModel; +using System.ComponentModel; namespace CodeModel.CaDETModel.CodeItems { @@ -27,7 +26,7 @@ public CaDETModifier(string modifier) "const" => CaDETModifierValue.Const, "async" => CaDETModifierValue.Async, "volatile" => CaDETModifierValue.Volatile, - "partial" => throw new PartialIsNotSupportedException(), + "partial" => CaDETModifierValue.Partial, _ => throw new InvalidEnumArgumentException(modifier) }; } diff --git a/CodeModel/CodeModel.csproj b/CodeModel/CodeModel.csproj index 0f29c52a..f783ef42 100644 --- a/CodeModel/CodeModel.csproj +++ b/CodeModel/CodeModel.csproj @@ -13,6 +13,7 @@ https://github.com/Clean-CaDET/platform/tree/master/CodeModel GPL-3.0-or-later true + 1.0.3 diff --git a/CodeModel/CodeModelCache.cs b/CodeModel/CodeModelCache.cs index 0a8ff77a..c36a6e59 100644 --- a/CodeModel/CodeModelCache.cs +++ b/CodeModel/CodeModelCache.cs @@ -17,7 +17,7 @@ public CaDETProject GetOrCreate(string repoAndCommitUrl, string repoFolder, Lang { if (_cache.TryGetValue(repoAndCommitUrl, out CaDETProject cacheEntry)) return cacheEntry; - cacheEntry = new CodeModelFactory(language).CreateProjectWithCodeFileLinks(repoFolder); + cacheEntry = new CodeModelFactory(false, language).CreateProjectWithCodeFileLinks(repoFolder); var cacheEntryOptions = new MemoryCacheEntryOptions { diff --git a/CodeModel/CodeModelFactory.cs b/CodeModel/CodeModelFactory.cs index 3f19f246..325394b9 100644 --- a/CodeModel/CodeModelFactory.cs +++ b/CodeModel/CodeModelFactory.cs @@ -12,16 +12,18 @@ namespace CodeModel public class CodeModelFactory { private readonly LanguageEnum _language; + private readonly bool _includePartial; - public CodeModelFactory(LanguageEnum language = LanguageEnum.CSharp) + public CodeModelFactory(bool includePartial = false, LanguageEnum language = LanguageEnum.CSharp) { + _includePartial = includePartial; _language = language; } public CaDETProject CreateProject(IEnumerable multipleClassSourceCode) { ICodeParser codeParser = SimpleParserFactory.CreateParser(_language); - return codeParser.Parse(multipleClassSourceCode); + return _includePartial ? codeParser.ParseWithPartial(multipleClassSourceCode) : codeParser.Parse(multipleClassSourceCode); } public CaDETProject CreateProject(string sourceCodeLocation, List ignoredFolders) diff --git a/CodeModel/CodeParsers/CSharp/CSharpCodeParser.cs b/CodeModel/CodeParsers/CSharp/CSharpCodeParser.cs index c5247bcd..e4aaa8cf 100644 --- a/CodeModel/CodeParsers/CSharp/CSharpCodeParser.cs +++ b/CodeModel/CodeParsers/CSharp/CSharpCodeParser.cs @@ -27,12 +27,20 @@ public CSharpCodeParser() public CaDETProject Parse(IEnumerable sourceCode) { var syntaxErrors = LoadSyntaxTrees(sourceCode); - var parsedClasses = ParseClasses(); + var parsedClasses = ParseClasses(false); ValidateUniqueFullNameForNonPartial(parsedClasses); parsedClasses = ConnectCaDETGraph(parsedClasses); return new CaDETProject(LanguageEnum.CSharp, CalculateMetrics(parsedClasses), syntaxErrors); } + public CaDETProject ParseWithPartial(IEnumerable sourceCode) + { + var syntaxErrors = LoadSyntaxTrees(sourceCode); + var parsedClasses = ParseClasses(true); + parsedClasses = ConnectCaDETGraph(parsedClasses); + return new CaDETProject(LanguageEnum.CSharp, CalculateMetrics(parsedClasses), syntaxErrors); + } + private List LoadSyntaxTrees(IEnumerable sourceCode) { var syntaxErrors = new List(); @@ -47,7 +55,7 @@ private List LoadSyntaxTrees(IEnumerable sourceCode) return syntaxErrors; } - private List ParseClasses() + private List ParseClasses(bool includePartial) { List builtClasses = new List(); foreach (var ast in _compilation.SyntaxTrees) @@ -59,7 +67,7 @@ private List ParseClasses() { try { - ValidateNoPartialModifier(node); + if(!includePartial) ValidateNoPartialModifier(node); ValidateNoStructParent(node); builtClasses.Add(ParseClass(semanticModel, node)); } @@ -196,6 +204,7 @@ private List ConnectCaDETGraph(List classes) foreach (var c in classes) { c.Parent = LinkParent(classes, c.Parent); + c.Subclasses = LinkSubclasses(classes, c); var outerClass = LinkOuterClass(classes, c.ContainerName); if (outerClass != null) { @@ -218,6 +227,19 @@ private static CaDETClass LinkParent(List classes, CaDETClass parent if (parent.Name.Equals("object")) return null; return classes.FirstOrDefault(c => c.FullName.Equals(parent.Name)); } + + private List LinkSubclasses(List classes, CaDETClass c) + { + var subclasses = new List(); + foreach(var subclass in classes) + { + if (subclass.Parent == null) continue; + if (subclass.Parent.FullName != null && subclass.Parent.FullName.Equals(c.FullName)) subclasses.Add(subclass); + else if (subclass.Parent.Name != null && subclass.Parent.Name.Equals(c.FullName)) subclasses.Add(subclass); + } + return subclasses; + } + private static CaDETClass LinkOuterClass(List classes, string containerName) { return classes.FirstOrDefault(c => c.FullName.Equals(containerName)); diff --git a/CodeModel/CodeParsers/ICodeParser.cs b/CodeModel/CodeParsers/ICodeParser.cs index 092b2a43..d2d35702 100644 --- a/CodeModel/CodeParsers/ICodeParser.cs +++ b/CodeModel/CodeParsers/ICodeParser.cs @@ -6,5 +6,6 @@ namespace CodeModel.CodeParsers public interface ICodeParser { CaDETProject Parse(IEnumerable sourceCode); + CaDETProject ParseWithPartial(IEnumerable sourceCode); } } \ No newline at end of file diff --git a/DataSetExplorer/Core/AnnotationConsistency/AnnotationConsistencyService.cs b/DataSetExplorer/Core/AnnotationConsistency/AnnotationConsistencyService.cs index 53bcfcaa..77a1d663 100644 --- a/DataSetExplorer/Core/AnnotationConsistency/AnnotationConsistencyService.cs +++ b/DataSetExplorer/Core/AnnotationConsistency/AnnotationConsistencyService.cs @@ -16,7 +16,7 @@ public AnnotationConsistencyService(FullDataSetFactory fullDataSetFactory) _fullDataSetFactory = fullDataSetFactory; } - public Result CheckMetricsSignificanceBetweenAnnotatorsForSeverity(int severity, IDictionary projects, List annotators) + public Result CheckMetricsSignificanceBetweenAnnotatorsForSeverity(string severity, IDictionary projects, List annotators) { var instancesGroupedBySmells = _fullDataSetFactory.GetAnnotatedInstancesGroupedBySmells(projects, annotators, annotatorId: null); IMetricsSignificanceTester tester = new AnovaTest(); @@ -42,7 +42,7 @@ public Result CheckMetricsSignificanceInAnnotationsForAnnotator(int anno return Result.Ok(); } - public Result CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(int severity, IDictionary projects, List annotators) + public Result CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(string severity, IDictionary projects, List annotators) { var instancesGroupedBySmells = _fullDataSetFactory.GetAnnotatedInstancesGroupedBySmells(projects, annotators, annotatorId: null); IAnnotatorsConsistencyTester tester = new ManovaTest(); @@ -67,7 +67,7 @@ public Result> CheckAnnotationConsistencyForAnnotator return tester.TestConsistencyOfSingleAnnotator(annotatorId, instancesGroupedBySmells); } - public Result> CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(int projectId, int severity) + public Result> CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(int projectId, string severity) { var instancesGroupedBySmells = _fullDataSetFactory.GetAnnotatedInstancesGroupedBySmells(projectId, annotatorId: null); IAnnotatorsConsistencyTester tester = new ManovaTest(); @@ -81,7 +81,7 @@ public Result>> CheckMetricsSignif return tester.TestForSingleAnnotator(annotatorId, instancesGroupedBySmells); } - public Result>> CheckMetricsSignificanceBetweenAnnotatorsForSeverity(int projectId, int severity) + public Result>> CheckMetricsSignificanceBetweenAnnotatorsForSeverity(int projectId, string severity) { var instancesGroupedBySmells = _fullDataSetFactory.GetAnnotatedInstancesGroupedBySmells(projectId, annotatorId: null); IMetricsSignificanceTester tester = new AnovaTest(); diff --git a/DataSetExplorer/Core/AnnotationConsistency/AnovaTest.cs b/DataSetExplorer/Core/AnnotationConsistency/AnovaTest.cs index 89138393..0c964b64 100644 --- a/DataSetExplorer/Core/AnnotationConsistency/AnovaTest.cs +++ b/DataSetExplorer/Core/AnnotationConsistency/AnovaTest.cs @@ -11,8 +11,8 @@ namespace DataSetExplorer.Core.AnnotationConsistency { public class AnovaTest : IMetricsSignificanceTester { - private readonly string _anovaScriptFile = "./AnnotationConsistency/scripts/anova_test.py"; - private readonly string _pythonPath = "./AnnotationConsistency/venv/Scripts/python.exe"; + private readonly string _anovaScriptFile = "./Core/AnnotationConsistency/scripts/anova_test.py"; + private readonly string _pythonPath = "./Core/AnnotationConsistency/venv/Scripts/python.exe"; private string _annotatedInstancesFile; private string _dependentVariable; private string _independentVariable; @@ -22,15 +22,18 @@ public class AnovaTest : IMetricsSignificanceTester public Result>> TestForSingleAnnotator(int annotatorId, List instancesGroupedBySmells) { - return Test(annotatorId, instancesGroupedBySmells, TestCodeSmellForAnnotator); - } - - public Result>> TestBetweenAnnotators(int severity, List instancesGroupedBySmells) - { - return Test(severity, instancesGroupedBySmells, TestCodeSmellBetweenAnnotators); + var results = new Dictionary>(); + foreach (var codeSmellGroup in instancesGroupedBySmells) + { + var codeSmell = codeSmellGroup.CodeSmell.Name; + var metrics = codeSmellGroup.Instances.First().MetricFeatures.Keys.ToList(); + var instances = codeSmellGroup.Instances; + results[codeSmell] = TestCodeSmellForAnnotator(annotatorId, instances, codeSmell, metrics); + } + return Result.Ok(results); } - private Result>> Test(int id, List instancesGroupedBySmells, TestCodeSmellDelegate testCodeSmell) + public Result>> TestBetweenAnnotators(string severity, List instancesGroupedBySmells) { var results = new Dictionary>(); foreach (var codeSmellGroup in instancesGroupedBySmells) @@ -38,7 +41,7 @@ private Result>> Test(int id, List var codeSmell = codeSmellGroup.CodeSmell.Name; var metrics = codeSmellGroup.Instances.First().MetricFeatures.Keys.ToList(); var instances = codeSmellGroup.Instances; - results[codeSmell] = testCodeSmell(id, instances, codeSmell, metrics); + results[codeSmell] = TestCodeSmellBetweenAnnotators(severity, instances, codeSmell, metrics); } return Result.Ok(results); } @@ -55,7 +58,7 @@ private Dictionary TestCodeSmellForAnnotator(int annotatorId, Li return results; } - private Dictionary TestCodeSmellBetweenAnnotators(int severity, List instances, string codeSmell, List metrics) + private Dictionary TestCodeSmellBetweenAnnotators(string severity, List instances, string codeSmell, List metrics) { var results = new Dictionary(); string exportedAnnotationsFile = ExportAnnotationsForSeverity(severity, instances, codeSmell); @@ -75,7 +78,7 @@ private string ExportAnnotationsForAnnotator(int annotatorId, List ins return exportedAnnotationsFile; } - private string ExportAnnotationsForSeverity(int severity, List instances, string codeSmell) + private string ExportAnnotationsForSeverity(string severity, List instances, string codeSmell) { var exportedAnnotationsFile = "MetricsSignificance_" + codeSmell + "_Severity_" + severity; var exporter = new AnnotationConsistencyByMetricsExporter(_annotatedInstancesFolderPath); diff --git a/DataSetExplorer/Core/AnnotationConsistency/IAnnotationConsistencyService.cs b/DataSetExplorer/Core/AnnotationConsistency/IAnnotationConsistencyService.cs index b0fe08f9..36447dc7 100644 --- a/DataSetExplorer/Core/AnnotationConsistency/IAnnotationConsistencyService.cs +++ b/DataSetExplorer/Core/AnnotationConsistency/IAnnotationConsistencyService.cs @@ -6,14 +6,13 @@ namespace DataSetExplorer.Core.AnnotationConsistency { public interface IAnnotationConsistencyService { - Result CheckMetricsSignificanceBetweenAnnotatorsForSeverity(int severity, IDictionary projects, List annotators); + Result CheckMetricsSignificanceBetweenAnnotatorsForSeverity(string severity, IDictionary projects, List annotators); Result CheckMetricsSignificanceInAnnotationsForAnnotator(int annotatorId, IDictionary projects, List annotators); - Result CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(int severity, IDictionary projects, List annotators); + Result CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(string severity, IDictionary projects, List annotators); Result CheckAnnotationConsistencyForAnnotator(int annotatorId, IDictionary projects, List annotators); - Result> CheckAnnotationConsistencyForAnnotator(int projectId, int annotatorId); - Result> CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(int projectId, int severity); + Result> CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(int projectId, string severity); Result>> CheckMetricsSignificanceInAnnotationsForAnnotator(int projectId, int annotatorId); - Result>> CheckMetricsSignificanceBetweenAnnotatorsForSeverity(int projectId, int severity); + Result>> CheckMetricsSignificanceBetweenAnnotatorsForSeverity(int projectId, string severity); } } diff --git a/DataSetExplorer/Core/AnnotationConsistency/IAnnotatorsConsistencyTester.cs b/DataSetExplorer/Core/AnnotationConsistency/IAnnotatorsConsistencyTester.cs index 06a654dd..fa5f2227 100644 --- a/DataSetExplorer/Core/AnnotationConsistency/IAnnotatorsConsistencyTester.cs +++ b/DataSetExplorer/Core/AnnotationConsistency/IAnnotatorsConsistencyTester.cs @@ -6,7 +6,7 @@ namespace DataSetExplorer.Core.AnnotationConsistency { internal interface IAnnotatorsConsistencyTester { - public Result> TestConsistencyBetweenAnnotators(int severity, List instancesGroupedBySmells); + public Result> TestConsistencyBetweenAnnotators(string severity, List instancesGroupedBySmells); public Result> TestConsistencyOfSingleAnnotator(int annotatorId, List instancesGroupedBySmells); } diff --git a/DataSetExplorer/Core/AnnotationConsistency/IMetricsSignificanceTester.cs b/DataSetExplorer/Core/AnnotationConsistency/IMetricsSignificanceTester.cs index 2efd463a..5522cbfb 100644 --- a/DataSetExplorer/Core/AnnotationConsistency/IMetricsSignificanceTester.cs +++ b/DataSetExplorer/Core/AnnotationConsistency/IMetricsSignificanceTester.cs @@ -8,6 +8,6 @@ internal interface IMetricsSignificanceTester { public Result>> TestForSingleAnnotator(int annotatorId, List instancesGroupedBySmells); - public Result>> TestBetweenAnnotators(int severity, List instancesGroupedBySmells); + public Result>> TestBetweenAnnotators(string severity, List instancesGroupedBySmells); } } \ No newline at end of file diff --git a/DataSetExplorer/Core/AnnotationConsistency/ManovaTest.cs b/DataSetExplorer/Core/AnnotationConsistency/ManovaTest.cs index 190021f8..7f441973 100644 --- a/DataSetExplorer/Core/AnnotationConsistency/ManovaTest.cs +++ b/DataSetExplorer/Core/AnnotationConsistency/ManovaTest.cs @@ -11,8 +11,8 @@ namespace DataSetExplorer.Core.AnnotationConsistency { public class ManovaTest : IAnnotatorsConsistencyTester { - private readonly string _manovaScriptFile = "./AnnotationConsistency/scripts/manova_test.py"; - private readonly string _pythonPath = "./AnnotationConsistency/venv/Scripts/python.exe"; + private readonly string _manovaScriptFile = "./Core/AnnotationConsistency/scripts/manova_test.py"; + private readonly string _pythonPath = "./Core/AnnotationConsistency/venv/Scripts/python.exe"; private string _annotatedInstancesFile; private string _dependentVariable; private string _independentVariable; @@ -20,18 +20,22 @@ public class ManovaTest : IAnnotatorsConsistencyTester private delegate void PrepareTestDelegate(int id, List instances, string codeSmell, List metrics); - public Result> TestConsistencyBetweenAnnotators(int severity, List instancesGroupedBySmells) + public Result> TestConsistencyBetweenAnnotators(string severity, List instancesGroupedBySmells) { - return Test(severity, instancesGroupedBySmells, PrepareDataForBetweenAnnotators); - } + var results = new Dictionary(); + foreach (var codeSmellGroup in instancesGroupedBySmells) + { + var codeSmell = codeSmellGroup.CodeSmell.Name; + var metrics = codeSmellGroup.Instances.First().MetricFeatures.Keys.ToList(); + var instances = codeSmellGroup.Instances; - public Result> TestConsistencyOfSingleAnnotator(int annotatorId, List instancesGroupedBySmells) - { - return Test(annotatorId, instancesGroupedBySmells, PrepareDataForSingleAnnotator); + PrepareDataForBetweenAnnotators(severity, instances, codeSmell, metrics); + results[codeSmell] = StartProcess(); + } + return Result.Ok(results); } - private Result> Test(int id, List instancesGroupedBySmells, - PrepareTestDelegate prepareTest) + public Result> TestConsistencyOfSingleAnnotator(int annotatorId, List instancesGroupedBySmells) { var results = new Dictionary(); foreach (var codeSmellGroup in instancesGroupedBySmells) @@ -40,13 +44,13 @@ private Result> Test(int id, List instances, string codeSmell, List metrics) + private void PrepareDataForBetweenAnnotators(string severity, List instances, string codeSmell, List metrics) { string exportedAnnotatorsFile = ExportAnnotatorsForSeverity(severity, instances, codeSmell); SetupTestArguments(exportedAnnotatorsFile, metrics, "Annotator"); @@ -57,7 +61,7 @@ private void PrepareDataForSingleAnnotator(int annotatorId, List insta SetupTestArguments(exportedAnnotationsFile, metrics, "Annotation"); } - private string ExportAnnotatorsForSeverity(int severity, List instances, string codeSmell) + private string ExportAnnotatorsForSeverity(string severity, List instances, string codeSmell) { var exportedAnnotatorsFile = "SanityCheck_" + codeSmell + "_Severity_" + severity; var exporter = new AnnotationConsistencyByMetricsExporter(_annotatedInstancesFolderPath); diff --git a/DataSetExplorer/Core/AnnotationSchema/AnnotationSchemaService.cs b/DataSetExplorer/Core/AnnotationSchema/AnnotationSchemaService.cs new file mode 100644 index 00000000..330ee9b8 --- /dev/null +++ b/DataSetExplorer/Core/AnnotationSchema/AnnotationSchemaService.cs @@ -0,0 +1,187 @@ +using System.Collections.Generic; +using System.Linq; +using DataSetExplorer.Core.Annotations; +using DataSetExplorer.Core.AnnotationSchema.Model; +using DataSetExplorer.Core.AnnotationSchema.Repository; +using DataSetExplorer.Core.DataSets; +using FluentResults; + +namespace DataSetExplorer.Core.AnnotationSchema +{ + public class AnnotationSchemaService : IAnnotationSchemaService + { + private readonly IAnnotationSchemaRepository _annotationSchemaRepository; + private readonly IAnnotationService _annotationService; + private readonly IInstanceService _instanceService; + + public AnnotationSchemaService(IAnnotationSchemaRepository annotationSchemaRepository, + IAnnotationService annotationService, IInstanceService instanceService) + { + _annotationSchemaRepository = annotationSchemaRepository; + _annotationService = annotationService; + _instanceService = instanceService; + } + + public Result GetCodeSmellDefinition(int id) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(id); + if (codeSmellDefinition == default) return Result.Fail($"Code smell definition with id: {id} does not exist."); + return Result.Ok(codeSmellDefinition); + } + + public Result GetCodeSmellDefinitionByName(string name) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinitionByName(name); + if (codeSmellDefinition == default) return Result.Fail($"Code smell definition with name: {name} does not exist."); + return Result.Ok(codeSmellDefinition); + } + + public Result> GetAllCodeSmellDefinitions() + { + return Result.Ok(_annotationSchemaRepository.GetAllCodeSmellDefinitions()); + } + + public Result CreateCodeSmellDefinition(CodeSmellDefinition codeSmellDefinition) + { + _annotationSchemaRepository.SaveCodeSmellDefinition(codeSmellDefinition); + return Result.Ok(codeSmellDefinition); + } + + public Result UpdateCodeSmellDefinition(int id, CodeSmellDefinition codeSmellDefinition) + { + var existingCodeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(id); + if (existingCodeSmellDefinition == default) return Result.Fail($"CodeSmellDefinition with id: {id} does not exist."); + + var codeSmells = _annotationService.GetCodeSmellsByDefinition(existingCodeSmellDefinition).Value; + foreach (var codeSmell in codeSmells) + { + if (codeSmell.Name.Equals(existingCodeSmellDefinition.Name)) + { + codeSmell.Name = codeSmellDefinition.Name; + _annotationService.UpdateCodeSmell(codeSmell); + } + } + + existingCodeSmellDefinition.Update(codeSmellDefinition); + _annotationSchemaRepository.SaveCodeSmellDefinition(existingCodeSmellDefinition); + + return Result.Ok(codeSmellDefinition); + } + + public Result DeleteCodeSmellDefinition(int id) + { + var codeSmellDefinition = _annotationSchemaRepository.DeleteCodeSmellDefinition(id); + _instanceService.DeleteCandidateInstancesForSmell(codeSmellDefinition); + return Result.Ok(codeSmellDefinition); + } + + public Result> GetHeuristicsForCodeSmell(int id) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(id); + if (codeSmellDefinition == default) return Result.Fail($"Code smell definition with id: {id} does not exist."); + return Result.Ok((IEnumerable)codeSmellDefinition.Heuristics); + } + + public Result> GetHeuristicsForEachCodeSmell() + { + IDictionary result = new Dictionary(); + var codeSmellDefinitions = _annotationSchemaRepository.GetAllCodeSmellDefinitions(); + foreach (var codeSmellDefinition in codeSmellDefinitions) + { + var heuristics = GetHeuristicsForCodeSmell(codeSmellDefinition.Id); + result[codeSmellDefinition.Name] = heuristics.Value.ToArray(); + } + return Result.Ok(result); + } + + public Result AddHeuristicToCodeSmell(int id, HeuristicDefinition heuristic) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(id); + if (codeSmellDefinition == default) return Result.Fail($"Code smell definition with id: {id} does not exist."); + + codeSmellDefinition.Heuristics.Add(heuristic); + _annotationSchemaRepository.SaveCodeSmellDefinition(codeSmellDefinition); + return Result.Ok(heuristic); + } + + public Result UpdateHeuristicInCodeSmell(int id, HeuristicDefinition heuristic) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(id); + var oldHeuristic = codeSmellDefinition.Heuristics.Find(h => h.Id == heuristic.Id); + codeSmellDefinition.Heuristics.Remove(oldHeuristic); + codeSmellDefinition.Heuristics.Add(heuristic); + _annotationSchemaRepository.SaveCodeSmellDefinition(codeSmellDefinition); + _annotationService.UpdateAnnotationsAfterHeuristicUpdate(codeSmellDefinition, heuristic, oldHeuristic); + return Result.Ok(heuristic); + } + + public Result DeleteHeuristicFromCodeSmell(int smellId, int heuristicId) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(smellId); + if (codeSmellDefinition == default) return Result.Fail($"Code smell definition with id: {smellId} does not exist."); + + var heuristic = codeSmellDefinition.Heuristics.Find(h => h.Id == heuristicId); + codeSmellDefinition.Heuristics.RemoveAt(codeSmellDefinition.Heuristics.FindIndex(h => h.Id == heuristicId)); + _annotationSchemaRepository.SaveCodeSmellDefinition(codeSmellDefinition); + + _annotationService.UpdateAnnotationsAfterHeuristicDeletion(codeSmellDefinition, heuristic); + + return Result.Ok(codeSmellDefinition); + } + + public Result> GetSeveritiesForCodeSmell(int id) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(id); + if (codeSmellDefinition == default) return Result.Fail($"Code smell definition with id: {id} does not exist."); + return Result.Ok((IEnumerable)codeSmellDefinition.Severities); + } + + public Result> GetSeveritiesForEachCodeSmell() + { + IDictionary result = new Dictionary(); + var codeSmellDefinitions = _annotationSchemaRepository.GetAllCodeSmellDefinitions(); + foreach (var codeSmellDefinition in codeSmellDefinitions) + { + var severities = GetSeveritiesForCodeSmell(codeSmellDefinition.Id); + result[codeSmellDefinition.Name] = severities.Value.ToArray(); + } + return Result.Ok(result); + } + + public Result AddSeverityToCodeSmell(int id, SeverityDefinition severity) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(id); + if (codeSmellDefinition == default) return Result.Fail($"Code smell definition with id: {id} does not exist."); + + codeSmellDefinition.Severities.Add(severity); + _annotationSchemaRepository.SaveCodeSmellDefinition(codeSmellDefinition); + return Result.Ok(severity); + } + + public Result UpdateSeverityInCodeSmell(int id, SeverityDefinition severity) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(id); + var oldSeverity = codeSmellDefinition.Severities.Find(s => s.Id == severity.Id); + codeSmellDefinition.Severities.RemoveAt(codeSmellDefinition.Severities.FindIndex(s => s.Id == severity.Id)); + codeSmellDefinition.Severities.Add(severity); + _annotationSchemaRepository.SaveCodeSmellDefinition(codeSmellDefinition); + + _annotationService.UpdateAnnotationsAfterSeverityUpdate(codeSmellDefinition, severity, oldSeverity); + return Result.Ok(severity); + } + + public Result DeleteSeverityFromCodeSmell(int smellId, int severityId) + { + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinition(smellId); + if (codeSmellDefinition == default) return Result.Fail($"Code smell definition with id: {smellId} does not exist."); + + var severity = codeSmellDefinition.Severities.Find(s => s.Id == severityId); + codeSmellDefinition.Severities.RemoveAt(codeSmellDefinition.Severities.FindIndex(s => s.Id == severityId)); + _annotationSchemaRepository.SaveCodeSmellDefinition(codeSmellDefinition); + + _annotationService.UpdateAnnotationsAfterSeverityDeletion(codeSmellDefinition, severity); + + return Result.Ok(codeSmellDefinition); + } + } +} diff --git a/DataSetExplorer/Core/AnnotationSchema/IAnnotationSchemaService.cs b/DataSetExplorer/Core/AnnotationSchema/IAnnotationSchemaService.cs new file mode 100644 index 00000000..71481fce --- /dev/null +++ b/DataSetExplorer/Core/AnnotationSchema/IAnnotationSchemaService.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using DataSetExplorer.Core.AnnotationSchema.Model; +using FluentResults; + +namespace DataSetExplorer.Core.AnnotationSchema +{ + public interface IAnnotationSchemaService + { + Result GetCodeSmellDefinition(int id); + Result GetCodeSmellDefinitionByName(string name); + Result> GetAllCodeSmellDefinitions(); + Result CreateCodeSmellDefinition(CodeSmellDefinition codeSmellDefinition); + Result UpdateCodeSmellDefinition(int id, CodeSmellDefinition codeSmellDefinition); + Result DeleteCodeSmellDefinition(int id); + Result> GetHeuristicsForCodeSmell(int id); + Result> GetHeuristicsForEachCodeSmell(); + Result AddHeuristicToCodeSmell(int id, HeuristicDefinition heuristic); + Result UpdateHeuristicInCodeSmell(int id, HeuristicDefinition heuristic); + Result DeleteHeuristicFromCodeSmell(int smellId, int heuristicId); + Result> GetSeveritiesForCodeSmell(int id); + Result> GetSeveritiesForEachCodeSmell(); + Result AddSeverityToCodeSmell(int id, SeverityDefinition severity); + Result UpdateSeverityInCodeSmell(int id, SeverityDefinition severity); + Result DeleteSeverityFromCodeSmell(int smellId, int severityId); + } +} diff --git a/DataSetExplorer/Core/AnnotationSchema/Model/CodeSmellDefinition.cs b/DataSetExplorer/Core/AnnotationSchema/Model/CodeSmellDefinition.cs new file mode 100644 index 00000000..953d8cac --- /dev/null +++ b/DataSetExplorer/Core/AnnotationSchema/Model/CodeSmellDefinition.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using DataSetExplorer.Core.DataSets.Model; + +namespace DataSetExplorer.Core.AnnotationSchema.Model +{ + public class CodeSmellDefinition + { + public int Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public SnippetType SnippetType { get; set; } + public List Severities { get; set; } + public List Heuristics { get; set; } + + public CodeSmellDefinition(string name, string description, SnippetType snippetType) + { + Name = name; + Description = description; + SnippetType = snippetType; + Heuristics = new List(); + Severities = new List(); + } + + private CodeSmellDefinition() + { + } + + public void Update(CodeSmellDefinition other) + { + Name = other.Name; + Description = other.Description; + SnippetType = other.SnippetType; + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public override bool Equals(object other) + { + return other is CodeSmellDefinition smell && Name.Equals(smell.Name); + } + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/AnnotationSchema/Model/HeuristicDefinition.cs b/DataSetExplorer/Core/AnnotationSchema/Model/HeuristicDefinition.cs new file mode 100644 index 00000000..cc52bb7e --- /dev/null +++ b/DataSetExplorer/Core/AnnotationSchema/Model/HeuristicDefinition.cs @@ -0,0 +1,25 @@ +namespace DataSetExplorer.Core.AnnotationSchema.Model +{ + public class HeuristicDefinition + { + public int Id { get; private set; } + public string Name { get; set; } + public string Description { get; set; } + + public HeuristicDefinition(string name, string description) + { + Name = name; + Description = description; + } + + private HeuristicDefinition() + { + } + + public void Update(HeuristicDefinition other) + { + Name = other.Name; + Description = other.Description; + } + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/AnnotationSchema/Model/SeverityDefinition.cs b/DataSetExplorer/Core/AnnotationSchema/Model/SeverityDefinition.cs new file mode 100644 index 00000000..1f322c19 --- /dev/null +++ b/DataSetExplorer/Core/AnnotationSchema/Model/SeverityDefinition.cs @@ -0,0 +1,25 @@ +namespace DataSetExplorer.Core.AnnotationSchema.Model +{ + public class SeverityDefinition + { + public int Id { get; private set; } + public string Value { get; set; } + public string Description { get; set; } + + public SeverityDefinition(string value, string description) + { + Value = value; + Description = description; + } + + private SeverityDefinition() + { + } + + public void Update(SeverityDefinition other) + { + Value = other.Value; + Description = other.Description; + } + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/AnnotationSchema/Repository/AnnotationSchemaDatabaseRepository.cs b/DataSetExplorer/Core/AnnotationSchema/Repository/AnnotationSchemaDatabaseRepository.cs new file mode 100644 index 00000000..f9ec1cf6 --- /dev/null +++ b/DataSetExplorer/Core/AnnotationSchema/Repository/AnnotationSchemaDatabaseRepository.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using DataSetExplorer.Core.AnnotationSchema.Model; +using DataSetExplorer.Infrastructure.Database; +using Microsoft.EntityFrameworkCore; + +namespace DataSetExplorer.Core.AnnotationSchema.Repository +{ + public class AnnotationSchemaDatabaseRepository : IAnnotationSchemaRepository + { + private readonly DataSetExplorerContext _dbContext; + + public AnnotationSchemaDatabaseRepository(DataSetExplorerContext dbContext) + { + _dbContext = dbContext; + } + + public CodeSmellDefinition GetCodeSmellDefinition(int id) + { + return _dbContext.CodeSmellDefinitions + .Include(c => c.Heuristics) + .Include(c => c.Severities) + .FirstOrDefault(c => c.Id == id); + } + + public CodeSmellDefinition GetCodeSmellDefinitionByName(string name) + { + return _dbContext.CodeSmellDefinitions + .Include(c => c.Heuristics) + .Include(c => c.Severities) + .FirstOrDefault(c => c.Name.Equals(name)); + } + + public IEnumerable GetAllCodeSmellDefinitions() + { + return _dbContext.CodeSmellDefinitions.ToList(); + } + + public void SaveCodeSmellDefinition(CodeSmellDefinition codeSmellDefinition) + { + _dbContext.Update(codeSmellDefinition); + _dbContext.SaveChanges(); + } + + public CodeSmellDefinition DeleteCodeSmellDefinition(int id) + { + var deletedCodeSmell = _dbContext.CodeSmellDefinitions.Remove(_dbContext.CodeSmellDefinitions.Find(id)).Entity; + _dbContext.SaveChanges(); + return deletedCodeSmell; + } + } +} diff --git a/DataSetExplorer/Core/AnnotationSchema/Repository/IAnnotationSchemaRepository.cs b/DataSetExplorer/Core/AnnotationSchema/Repository/IAnnotationSchemaRepository.cs new file mode 100644 index 00000000..32d0568b --- /dev/null +++ b/DataSetExplorer/Core/AnnotationSchema/Repository/IAnnotationSchemaRepository.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using DataSetExplorer.Core.AnnotationSchema.Model; + +namespace DataSetExplorer.Core.AnnotationSchema.Repository +{ + public interface IAnnotationSchemaRepository + { + CodeSmellDefinition GetCodeSmellDefinition(int id); + CodeSmellDefinition GetCodeSmellDefinitionByName(string name); + IEnumerable GetAllCodeSmellDefinitions(); + void SaveCodeSmellDefinition(CodeSmellDefinition codeSmellDefinition); + CodeSmellDefinition DeleteCodeSmellDefinition(int id); + } +} diff --git a/DataSetExplorer/Core/Annotations/AnnotationService.cs b/DataSetExplorer/Core/Annotations/AnnotationService.cs index fbcfcd05..9dbcc69c 100644 --- a/DataSetExplorer/Core/Annotations/AnnotationService.cs +++ b/DataSetExplorer/Core/Annotations/AnnotationService.cs @@ -1,7 +1,9 @@ -using AutoMapper; -using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.AnnotationSchema.Model; +using DataSetExplorer.Core.DataSets; using DataSetExplorer.Core.DataSets.Repository; using FluentResults; +using System.Collections.Generic; namespace DataSetExplorer.Core.Annotations { @@ -9,11 +11,14 @@ public class AnnotationService : IAnnotationService { private readonly IInstanceRepository _instanceRepository; private readonly IAnnotationRepository _annotationRepository; + private readonly IInstanceService _instanceService; - public AnnotationService(IMapper mapper, IInstanceRepository instanceRepository, IAnnotationRepository annotationRepository) + public AnnotationService(IInstanceRepository instanceRepository, IAnnotationRepository annotationRepository, + IInstanceService instanceService) { _instanceRepository = instanceRepository; _annotationRepository = annotationRepository; + _instanceService = instanceService; } public Result AddAnnotation(Annotation annotation, int instanceId, int annotatorId) @@ -41,5 +46,92 @@ public Result UpdateAnnotation(Annotation changed, int annotationId, _annotationRepository.Update(annotation); return Result.Ok(annotation); } + + public Result UpdateAnnotationsAfterHeuristicDeletion(CodeSmellDefinition codeSmellDefinition, HeuristicDefinition heuristic) + { + foreach (var instance in _instanceService.GetInstancesForSmell(codeSmellDefinition.Name).Value) + { + RemoveDeletedHeuristicFromAnnotations(instance.Annotations, heuristic); + } + return Result.Ok(); + } + + private void RemoveDeletedHeuristicFromAnnotations(ISet annotations, HeuristicDefinition heuristic) + { + foreach (var annotation in annotations) + { + var appliedHeuristic = annotation.ApplicableHeuristics.Find(h => h.Description.Equals(heuristic.Name)); + if (appliedHeuristic == null) continue; + _annotationRepository.DeleteHeuristic(appliedHeuristic.Id); + } + } + + public Result UpdateAnnotationsAfterSeverityDeletion(CodeSmellDefinition codeSmellDefinition, SeverityDefinition severity) + { + foreach (var instance in _instanceService.GetInstancesForSmell(codeSmellDefinition.Name).Value) + { + DeleteAnnotationsWithDeletedSeverity(instance.Annotations, severity); + } + return Result.Ok(); + } + + private void DeleteAnnotationsWithDeletedSeverity(ISet annotations, SeverityDefinition severity) + { + foreach (var annotation in annotations) + { + if (annotation.Severity.Equals(severity.Value)) _annotationRepository.Delete(annotation.Id); + } + } + + public Result UpdateAnnotationsAfterSeverityUpdate(CodeSmellDefinition codeSmellDefinition, SeverityDefinition severity, SeverityDefinition oldSeverity) + { + foreach (var instance in _instanceService.GetInstancesForSmell(codeSmellDefinition.Name).Value) + { + if (instance.Annotations.Count > 0) UpdateAnnotationsWithUpdatedSeverity(instance.Annotations, severity, oldSeverity); + } + return Result.Ok(); + } + + private void UpdateAnnotationsWithUpdatedSeverity(ISet annotations, SeverityDefinition severity, SeverityDefinition oldSeverity) + { + foreach (var annotation in annotations) + { + if (annotation.Severity.Equals(oldSeverity.Value)) + { + annotation.Severity = severity.Value; + _annotationRepository.Update(annotation); + } + } + } + + public Result UpdateAnnotationsAfterHeuristicUpdate(CodeSmellDefinition codeSmellDefinition, HeuristicDefinition heuristic, HeuristicDefinition oldHeuristic) + { + foreach (var instance in _instanceService.GetInstancesForSmell(codeSmellDefinition.Name).Value) + { + UpdateAnnotationsWithUpdatedHeuristic(instance.Annotations, heuristic, oldHeuristic); + } + return Result.Ok(); + } + + private void UpdateAnnotationsWithUpdatedHeuristic(ISet annotations, HeuristicDefinition heuristic, HeuristicDefinition oldHeuristic) + { + foreach (var annotation in annotations) + { + var appliedHeuristic = annotation.ApplicableHeuristics.Find(h => h.Description.Equals(oldHeuristic.Name)); + if (appliedHeuristic == null) continue; + appliedHeuristic.Description = heuristic.Name; + _annotationRepository.UpdateAppliedHeuristic(appliedHeuristic); + } + } + + public Result> GetCodeSmellsByDefinition(CodeSmellDefinition codeSmellDefinition) + { + return Result.Ok(_annotationRepository.GetCodeSmellsByDefinition(codeSmellDefinition)); + } + + public Result UpdateCodeSmell(CodeSmell codeSmell) + { + return Result.Ok(_annotationRepository.UpdateCodeSmell(codeSmell)); + } } } diff --git a/DataSetExplorer/Core/Annotations/DataSetAnalysisService.cs b/DataSetExplorer/Core/Annotations/DataSetAnalysisService.cs index b49c2723..42f0de35 100644 --- a/DataSetExplorer/Core/Annotations/DataSetAnalysisService.cs +++ b/DataSetExplorer/Core/Annotations/DataSetAnalysisService.cs @@ -13,9 +13,11 @@ namespace DataSetExplorer.Core.Annotations public class DataSetAnalysisService : IDataSetAnalysisService { private readonly IProjectRepository _projectRepository; - public DataSetAnalysisService(IProjectRepository projectRepository) + private readonly IAnnotationRepository _annotationRepository; + public DataSetAnalysisService(IProjectRepository projectRepository, IAnnotationRepository annotationRepository) { _projectRepository = projectRepository; + _annotationRepository = annotationRepository; } public Result FindInstancesWithAllDisagreeingAnnotations(IDictionary projects) @@ -66,13 +68,13 @@ public Result> FindInstancesRequiringAdditionalAnn private DataSetProject LoadDataSetProject(string folder, string projectName) { - var importer = new ExcelImporter(folder); + var importer = new ExcelImporter(folder, _annotationRepository); return importer.Import(projectName); } private List LoadAnnotatedInstances(string datasetPath) { - var importer = new ExcelImporter(datasetPath); + var importer = new ExcelImporter(datasetPath, _annotationRepository); return importer.ImportAnnotatedInstancesFromDataSet(datasetPath); } @@ -82,7 +84,7 @@ public Result ExportMembersFromAnnotatedClasses(IDictionary>(); + var classesGroupedBySeverity = new Dictionary>(); var annotatedInstances = LoadAnnotatedInstances(datasetPath); foreach (var projectUrl in projects.Keys) @@ -101,7 +103,7 @@ public Result ExportMembersFromAnnotatedClasses(IDictionary> classesGroupedBySeverity, List annotatedInstances, CaDETProject cadetProject) + private static void GroupInstancesBySeverity(Dictionary> classesGroupedBySeverity, List annotatedInstances, CaDETProject cadetProject) { foreach (var instance in annotatedInstances) { diff --git a/DataSetExplorer/Core/Annotations/IAnnotationService.cs b/DataSetExplorer/Core/Annotations/IAnnotationService.cs index 0cf6b5bd..3c450ab6 100644 --- a/DataSetExplorer/Core/Annotations/IAnnotationService.cs +++ b/DataSetExplorer/Core/Annotations/IAnnotationService.cs @@ -1,5 +1,7 @@ using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.AnnotationSchema.Model; using FluentResults; +using System.Collections.Generic; namespace DataSetExplorer.Core.Annotations { @@ -7,5 +9,11 @@ public interface IAnnotationService { Result AddAnnotation(Annotation annotation, int instanceId, int annotatorId); Result UpdateAnnotation(Annotation changed, int annotationId, int annotatorId); + Result UpdateAnnotationsAfterHeuristicDeletion(CodeSmellDefinition codeSmellDefinition, HeuristicDefinition heuristic); + Result UpdateAnnotationsAfterSeverityDeletion(CodeSmellDefinition codeSmellDefinition, SeverityDefinition severity); + Result UpdateAnnotationsAfterSeverityUpdate(CodeSmellDefinition codeSmellDefinition, SeverityDefinition severity, SeverityDefinition oldSeverity); + Result UpdateAnnotationsAfterHeuristicUpdate(CodeSmellDefinition codeSmellDefinition, HeuristicDefinition heuristic, HeuristicDefinition oldHeuristic); + Result> GetCodeSmellsByDefinition(CodeSmellDefinition codeSmellDefinition); + Result UpdateCodeSmell(CodeSmell codeSmell); } } diff --git a/DataSetExplorer/Core/Annotations/Model/Annotation.cs b/DataSetExplorer/Core/Annotations/Model/Annotation.cs index f9564cb6..ea9fe42d 100644 --- a/DataSetExplorer/Core/Annotations/Model/Annotation.cs +++ b/DataSetExplorer/Core/Annotations/Model/Annotation.cs @@ -7,24 +7,23 @@ public class Annotation { public int Id { get; private set; } public CodeSmell InstanceSmell { get; private set; } - public int Severity { get; private set; } + public string Severity { get; set; } public List ApplicableHeuristics { get; private set; } public Annotator Annotator { get; set; } public string Note { get; private set; } - public Annotation(string instanceSmell, int severity, Annotator annotator, List applicableHeuristics, String note) : + public Annotation(string instanceSmell, string severity, Annotator annotator, List applicableHeuristics, String note) : this(new CodeSmell(instanceSmell), severity, annotator, applicableHeuristics, note) { } - public Annotation(CodeSmell instanceSmell, int severity, Annotator annotator, List applicableHeuristics, String note) + public Annotation(CodeSmell instanceSmell, string severity, Annotator annotator, List applicableHeuristics, String note) { InstanceSmell = instanceSmell; Severity = severity; Annotator = annotator; ApplicableHeuristics = applicableHeuristics; Note = note; - Validate(); } private Annotation() @@ -42,9 +41,7 @@ public void Update(Annotation other) private void Validate() { - if (Severity < 0 || Severity > 3) throw new ArgumentException("Accepted severity ranges from 0 to 3, but was " + Severity); if (Annotator.Id == 0) throw new ArgumentException("Annotator ID is required."); - if (Severity > 0 && ApplicableHeuristics.Count < 1) throw new ArgumentException("Annotations made by " + Annotator.Id + " with severity " + Severity + " must have at least one applicable heuristic."); } public override int GetHashCode() => (Annotator.Id, InstanceSmell: InstanceSmell.Name).GetHashCode(); diff --git a/DataSetExplorer/Core/Annotations/Model/Annotator.cs b/DataSetExplorer/Core/Annotations/Model/Annotator.cs index 5608b060..65847a17 100644 --- a/DataSetExplorer/Core/Annotations/Model/Annotator.cs +++ b/DataSetExplorer/Core/Annotations/Model/Annotator.cs @@ -4,18 +4,32 @@ namespace DataSetExplorer.Core.Annotations.Model { public class Annotator { - public int Id { get; private set; } + public int Id { get; set; } + public string Name { get; private set; } + public string Email { get; private set; } public int YearsOfExperience { get; private set; } public int Ranking { get; private set; } + public Annotator() {} + public Annotator(int id) { Id = id; } - public Annotator(int id, int yearsOfExperience, int ranking) + public Annotator(int id, string name, string email, int yearsOfExperience, int ranking) + { + Id = id; + Name = name; + Email = email; + YearsOfExperience = yearsOfExperience; + Ranking = ranking; + } + + public Annotator(int id, string name, int yearsOfExperience, int ranking) { Id = id; + Name = name; YearsOfExperience = yearsOfExperience; Ranking = ranking; } @@ -29,13 +43,15 @@ public bool Equals(Annotator other) { return other != null && Id == other.Id && + Name == other.Name && + Email == other.Email && YearsOfExperience == other.YearsOfExperience && Ranking == other.Ranking; } public override int GetHashCode() { - return HashCode.Combine(Id, YearsOfExperience, Ranking); + return HashCode.Combine(Id, Name, Email, YearsOfExperience, Ranking); } } } \ No newline at end of file diff --git a/DataSetExplorer/Core/Annotations/Model/CodeSmell.cs b/DataSetExplorer/Core/Annotations/Model/CodeSmell.cs index d6c47f09..1846d9ee 100644 --- a/DataSetExplorer/Core/Annotations/Model/CodeSmell.cs +++ b/DataSetExplorer/Core/Annotations/Model/CodeSmell.cs @@ -1,51 +1,27 @@ -using System; -using System.Collections.Generic; -using DataSetExplorer.Core.DataSets.Model; +using DataSetExplorer.Core.DataSets.Model; namespace DataSetExplorer.Core.Annotations.Model { public class CodeSmell { - public CodeSmell(string smell) + public CodeSmell(string smell, SnippetType snippetType) { Name = smell; - Validate(); + SnippetType = snippetType; } - private CodeSmell() + public CodeSmell(string smell) { + Name = smell; } - public int Id { get; private set; } - public string Name { get; private set; } - - public void Validate() + private CodeSmell() { - switch (Name) - { - case "Large_Class": - case "Long_Method": - case "Feature_Envy": - case "Data_Class": - case "Refused_Bequest": - return; - default: - throw new InvalidOperationException("Unsupported code smell type."); - } } - public List RelevantSnippetTypes() - { - return Name switch - { - "Large_Class" => new List {SnippetType.Class}, - "Long_Method" => new List {SnippetType.Function}, - "Feature_Envy" => new List { SnippetType.Function }, - "Data_Class" => new List { SnippetType.Class }, - "Refused_Bequest" => new List { SnippetType.Class }, - _ => null - }; - } + public int Id { get; private set; } + public string Name { get; set; } + public SnippetType SnippetType { get; private set; } public override int GetHashCode() { diff --git a/DataSetExplorer/Core/Annotations/Model/SmellHeuristic.cs b/DataSetExplorer/Core/Annotations/Model/SmellHeuristic.cs index 5f034d3d..f629b5cb 100644 --- a/DataSetExplorer/Core/Annotations/Model/SmellHeuristic.cs +++ b/DataSetExplorer/Core/Annotations/Model/SmellHeuristic.cs @@ -3,7 +3,7 @@ public class SmellHeuristic { public int Id { get; private set; } - public string Description { get; private set; } + public string Description { get; set; } public bool IsApplicable { get; private set; } public string ReasonForApplicability { get; private set; } diff --git a/DataSetExplorer/Core/Auth/AuthService.cs b/DataSetExplorer/Core/Auth/AuthService.cs new file mode 100644 index 00000000..83c91d0a --- /dev/null +++ b/DataSetExplorer/Core/Auth/AuthService.cs @@ -0,0 +1,37 @@ +using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.Auth.Repository; +using FluentResults; +using System.Collections.Generic; + +namespace DataSetExplorer.Core.Auth +{ + public class AuthService : IAuthService + { + private readonly IAuthRepository _authRepository; + + public AuthService(IAuthRepository authRepository) + { + _authRepository = authRepository; + } + + public Result Save(Annotator annotator) + { + return Result.Ok(_authRepository.Save(annotator)); + } + + public Result GetAnnotatorByEmail(string email) + { + return Result.Ok(_authRepository.GetAnnotatorByEmail(email)); + } + + public Result GetAnnotatorById(int id) + { + return Result.Ok(_authRepository.GetAnnotatorById(id)); + } + + public Result> GetAll() + { + return Result.Ok(_authRepository.GetAll()); + } + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/Auth/IAuthService.cs b/DataSetExplorer/Core/Auth/IAuthService.cs new file mode 100644 index 00000000..f49adbdf --- /dev/null +++ b/DataSetExplorer/Core/Auth/IAuthService.cs @@ -0,0 +1,14 @@ +using DataSetExplorer.Core.Annotations.Model; +using FluentResults; +using System.Collections.Generic; + +namespace DataSetExplorer.Core.Auth +{ + public interface IAuthService + { + Result Save(Annotator annotatorDTO); + Result GetAnnotatorByEmail(string email); + Result GetAnnotatorById(int id); + Result> GetAll(); + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/Auth/Repository/AuthRepository.cs b/DataSetExplorer/Core/Auth/Repository/AuthRepository.cs new file mode 100644 index 00000000..5888c176 --- /dev/null +++ b/DataSetExplorer/Core/Auth/Repository/AuthRepository.cs @@ -0,0 +1,49 @@ +using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Infrastructure.Database; +using Microsoft.EntityFrameworkCore; +using System.Collections.Generic; +using System.Linq; + +namespace DataSetExplorer.Core.Auth.Repository +{ + public class AuthRepository : IAuthRepository + { + private readonly DataSetExplorerContext _dbContext; + + public AuthRepository(DataSetExplorerContext dbContext) + { + _dbContext = dbContext; + } + + public Annotator Save(Annotator annotator) + { + _dbContext.Update(annotator); + try + { + _dbContext.SaveChanges(); + return annotator; + } + catch (DbUpdateException e) + { + return null; + } + } + + public Annotator GetAnnotatorByEmail(string email) + { + return _dbContext.Annotators + .FirstOrDefault(a => a.Email == email); + } + + public Annotator GetAnnotatorById(int id) + { + return _dbContext.Annotators + .FirstOrDefault(a => a.Id == id); + } + + public List GetAll() + { + return _dbContext.Annotators.ToList(); + } + } +} diff --git a/DataSetExplorer/Core/Auth/Repository/IAuthRepository.cs b/DataSetExplorer/Core/Auth/Repository/IAuthRepository.cs new file mode 100644 index 00000000..5c449654 --- /dev/null +++ b/DataSetExplorer/Core/Auth/Repository/IAuthRepository.cs @@ -0,0 +1,13 @@ +using DataSetExplorer.Core.Annotations.Model; +using System.Collections.Generic; + +namespace DataSetExplorer.Core.Auth.Repository +{ + public interface IAuthRepository + { + Annotator Save(Annotator annotator); + Annotator GetAnnotatorByEmail(string email); + Annotator GetAnnotatorById(int id); + List GetAll(); + } +} diff --git a/DataSetExplorer/Core/CleanCodeAnalysis/CleanCodeAnalysisService.cs b/DataSetExplorer/Core/CleanCodeAnalysis/CleanCodeAnalysisService.cs new file mode 100644 index 00000000..044312cf --- /dev/null +++ b/DataSetExplorer/Core/CleanCodeAnalysis/CleanCodeAnalysisService.cs @@ -0,0 +1,291 @@ +using CodeModel.CaDETModel.CodeItems; +using DataSetExplorer.Core.CleanCodeAnalysis.Model; +using DataSetExplorer.Core.DataSets; +using DataSetExplorer.Core.DataSets.Model; +using DataSetExplorer.UI.Controllers.Dataset.DTOs; +using FluentResults; +using OfficeOpenXml; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; + +namespace DataSetExplorer.Core.CleanCodeAnalysis +{ + public class CleanCodeAnalysisService : ICleanCodeAnalysisService + { + private readonly string _cleanNamesAnalysisTemplatePath = "./Core/DataSetSerializer/Template/Clean_Names_Analysis_Template.xlsx"; + private readonly string _cleanFunctionsAnalysisTemplatePath = "./Core/DataSetSerializer/Template/Clean_Functions_Analysis_Template.xlsx"; + private readonly string _cleanClassesAnalysisTemplatePath = "./Core/DataSetSerializer/Template/Clean_Classes_Analysis_Template.xlsx"; + private string _exportPath; + private ExcelPackage _excelFile; + private ExcelWorksheet _sheet; + private readonly IInstanceService _instanceService; + + public CleanCodeAnalysisService(IInstanceService instanceService) + { + _instanceService = instanceService; + } + + public Result ExportDatasetAnalysis(int datasetId, CleanCodeAnalysisDTO analysisOptions) + { + _exportPath = analysisOptions.ExportPath; + foreach (var option in analysisOptions.CleanCodeOptions) + { + if (option.Equals("Clean names")) + ExportCleanNamesAnalysis(_instanceService.GetInstancesWithIdentifiersByDatasetId(datasetId).Value); + if (option.Equals("Clean functions")) + ExportCleanFunctionsAnalysis(_instanceService.GetInstancesByDatasetId(datasetId).Value); + if (option.Equals("Clean classes")) + ExportCleanClassesAnalysis(_instanceService.GetInstancesByDatasetId(datasetId).Value); + } + return Result.Ok(analysisOptions.ExportPath); + } + + public Result ExportProjectAnalysis(int projectId, CleanCodeAnalysisDTO analysisOptions) + { + _exportPath = analysisOptions.ExportPath; + foreach (var option in analysisOptions.CleanCodeOptions) + { + if (option.Equals("Clean names")) + ExportCleanNamesAnalysis(_instanceService.GetInstancesWithIdentifiersByProjectId(projectId).Value); + if (option.Equals("Clean functions")) + ExportCleanFunctionsAnalysis(_instanceService.GetInstancesByProjectId(projectId).Value); + if (option.Equals("Clean classes")) + ExportCleanClassesAnalysis(_instanceService.GetInstancesByProjectId(projectId).Value); + } + return Result.Ok(analysisOptions.ExportPath); + } + + private void ExportCleanNamesAnalysis(Dictionary> projectsAndInstances) + { + if (projectsAndInstances == default) return; + + foreach (var projectAndInstances in projectsAndInstances) + { + ExcelPackage.LicenseContext = LicenseContext.NonCommercial; + _excelFile = new ExcelPackage(new FileInfo(_cleanNamesAnalysisTemplatePath)); + _sheet = _excelFile.Workbook.Worksheets[0]; + + RemoveFunctions(projectAndInstances.Value); + var instancesAndIdentifiers = GetInstancesAndIdentifiers(projectAndInstances.Value); + PopulateCleanNamesTemplate(projectAndInstances.Value, instancesAndIdentifiers); + Serialize(projectAndInstances.Key + "_CleanNames"); + } + } + + private Dictionary>> GetInstancesAndIdentifiers(List instances) + { + Dictionary>> instanceIdentifiers = new Dictionary>>(); + foreach (var instance in instances) + { + Dictionary> identifierTypes = new Dictionary>(); + instance.Identifiers.ForEach(ident => AddTypeForIdentifier(ident, identifierTypes)); + instanceIdentifiers.Add(instance.Id, identifierTypes); + } + return instanceIdentifiers; + } + + private void AddTypeForIdentifier(Identifier identifier, Dictionary> identifierTypes) + { + if (identifierTypes.ContainsKey(identifier.Name)) identifierTypes[identifier.Name].Add(identifier.Type); + else + { + identifierTypes.Add(identifier.Name, new List()); + identifierTypes[identifier.Name].Add(identifier.Type); + } + } + + private void RemoveFunctions(List instances) + { + instances.RemoveAll(i => i.Type.Equals(SnippetType.Function)); + } + + private void PopulateCleanNamesTemplate(List instances, Dictionary>> instancesAndIdentifiers) + { + var identifierCount = 0; + for (var i = 0; i < instances.Count; i++) + { + var row = 3 + i + identifierCount; + _sheet.Cells[row, 1].Value = instances[i].CodeSnippetId; + _sheet.Cells[row, 2].Value = instances[i].Link; + + var identifiersAndTypes = instancesAndIdentifiers[instances[i].Id]; + var mainIdentifier = instances[i].Identifiers.Find(i => i.Type.Equals(IdentifierType.Class)); + PopulateIdentifiers(row, mainIdentifier, identifiersAndTypes); + identifierCount += identifiersAndTypes.Count; + } + } + + private void PopulateIdentifiers(int row, Identifier mainIdentifier, Dictionary> identifiersAndTypes) + { + var j = 0; + + _sheet.Cells[row + j, 3].Value = mainIdentifier.Name; + _sheet.Cells[row + j, 4].Value = mainIdentifier.Type.ToString(); + j++; + + foreach (var identifierAndType in identifiersAndTypes) + { + if (identifierAndType.Key.Equals(mainIdentifier.Name)) continue; + _sheet.Cells[row + j, 3].Value = identifierAndType.Key; + PopulateIdentifierTypes(row, j, identifierAndType.Value); + j++; + } + } + + private void PopulateIdentifierTypes(int row, int j, List types) + { + Dictionary typeOccurrence = CountTypeOccurrence(types); + _sheet.Cells[row + j, 4].Value = ""; + foreach (var occurrence in typeOccurrence) + { + _sheet.Cells[row + j, 4].Value += occurrence.Value + "x " + occurrence.Key + "; "; + } + } + + private static Dictionary CountTypeOccurrence(List types) + { + return types.GroupBy(t => t.ToString()).ToDictionary(t => t.Key, t => t.Count()); + } + + private void ExportCleanFunctionsAnalysis(Dictionary> instances) + { + if (instances == default) return; + + foreach (var projectName in instances.Keys) + { + ExcelPackage.LicenseContext = LicenseContext.NonCommercial; + _excelFile = new ExcelPackage(new FileInfo(_cleanFunctionsAnalysisTemplatePath)); + _sheet = _excelFile.Workbook.Worksheets[0]; + PopulateCleanFunctionsTemplate(instances[projectName]); + Serialize(projectName + "_CleanFunctions"); + } + } + + private void PopulateCleanFunctionsTemplate(List instances) + { + instances = FilterInstances(instances); + for (var i = 0; i < instances.Count; i++) + { + var row = 5 + i; + PopulateInstanceInfo(row, instances[i]); + + PopulateMetrics(row, 3, CaDETMetric.MELOC, 15, 25, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 3, instances.Count + 4, 3), _sheet.Cells[3, 3].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 3, instances.Count + 4, 3), _sheet.Cells[2, 3].Value, Color.Yellow); + + PopulateMetrics(row, 4, CaDETMetric.CYCLO_SWITCH, 5, 12, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 4, instances.Count + 4, 4), _sheet.Cells[3, 4].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 4, instances.Count + 4, 4), _sheet.Cells[2, 4].Value, Color.Yellow); + + PopulateMetrics(row, 5, CaDETMetric.MNOL, 2, 4, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 5, instances.Count + 4, 5), _sheet.Cells[3, 5].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 5, instances.Count + 4, 5), _sheet.Cells[2, 5].Value, Color.Yellow); + + PopulateMetrics(row, 6, CaDETMetric.NOP, 3, 6, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 6, instances.Count + 4, 6), _sheet.Cells[3, 6].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 6, instances.Count + 4, 6), _sheet.Cells[2, 6].Value, Color.Yellow); + + PopulateMetrics(row, 7, CaDETMetric.MNOC, 2, 4, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 7, instances.Count + 4, 7), _sheet.Cells[3, 7].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 7, instances.Count + 4, 7), _sheet.Cells[2, 7].Value, Color.Yellow); + } + } + + private List FilterInstances(List instances) + { + instances.RemoveAll(i => i.Type.Equals(SnippetType.Class)); + return RemoveConstructors(instances); + } + + private List RemoveConstructors(List instances) + { + var instancesWithoutConstructors = new List(); + foreach (Instance instance in instances) + { + var snippetPartsWithoutParams = instance.CodeSnippetId.Split("(")[0]; + var snippetParts = snippetPartsWithoutParams.Split("."); + var methodName = snippetParts.Last(); + var className = snippetParts[snippetParts.Length - 2]; + if (!methodName.Equals(className)) instancesWithoutConstructors.Add(instance); + } + return instancesWithoutConstructors; + } + + private void ExportCleanClassesAnalysis(Dictionary> instances) + { + if (instances == default) return; + + foreach (var projectName in instances.Keys) + { + ExcelPackage.LicenseContext = LicenseContext.NonCommercial; + _excelFile = new ExcelPackage(new FileInfo(_cleanClassesAnalysisTemplatePath)); + _sheet = _excelFile.Workbook.Worksheets[0]; + PopulateCleanClassesTemplate(instances[projectName]); + Serialize(projectName + "_CleanClasses"); + } + } + + private void PopulateCleanClassesTemplate(List instances) + { + instances.RemoveAll(i => i.Type.Equals(SnippetType.Function)); + for (var i = 0; i < instances.Count; i++) + { + var row = 5 + i; + PopulateInstanceInfo(row, instances[i]); + + PopulateMetrics(row, 3, CaDETMetric.CLOC, 80, 150, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 3, instances.Count + 4, 3), _sheet.Cells[3, 3].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 3, instances.Count + 4, 3), _sheet.Cells[2, 3].Value, Color.Yellow); + + PopulateMetrics(row, 4, CaDETMetric.WMC, 15, 25, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 4, instances.Count + 4, 4), _sheet.Cells[3, 4].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 4, instances.Count + 4, 4), _sheet.Cells[2, 4].Value, Color.Yellow); + + PopulateMetrics(row, 5, CaDETMetric.NAD, 10, 15, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 5, instances.Count + 4, 5), _sheet.Cells[3, 5].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 5, instances.Count + 4, 5), _sheet.Cells[2, 5].Value, Color.Yellow); + + PopulateMetrics(row, 6, CaDETMetric.NMD, 12, 16, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 6, instances.Count + 4, 6), _sheet.Cells[3, 6].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 6, instances.Count + 4, 6), _sheet.Cells[2, 6].Value, Color.Yellow); + + PopulateMetrics(row, 7, CaDETMetric.CBO, 8, 12, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 7, instances.Count + 4, 7), _sheet.Cells[3, 7].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 7, instances.Count + 4, 7), _sheet.Cells[2, 7].Value, Color.Yellow); + + PopulateMetrics(row, 8, CaDETMetric.DIT, 3, 5, instances[i]); + SetConditionalFormatting(new ExcelAddress(5, 8, instances.Count + 4, 8), _sheet.Cells[3, 8].Value, Color.Red); + SetConditionalFormatting(new ExcelAddress(5, 8, instances.Count + 4, 8), _sheet.Cells[2, 8].Value, Color.Yellow); + } + } + + private void PopulateInstanceInfo(int row, Instance instance) + { + _sheet.Cells[row, 1].Value = instance.CodeSnippetId; + _sheet.Cells[row, 2].Value = instance.Link; + } + + private void PopulateMetrics(int row, int column, CaDETMetric metric, int suspiciousValue, int criticalValue, Instance instance) + { + _sheet.Cells[2, column].Value = suspiciousValue; + _sheet.Cells[3, column].Value = criticalValue; + _sheet.Cells[4, column].Value = metric; + _sheet.Cells[row, column].Value = instance.MetricFeatures[metric]; + } + + private void SetConditionalFormatting(ExcelAddress excelAddress, object formula, Color color) + { + var cf = _sheet.ConditionalFormatting.AddGreaterThan(excelAddress); + cf.Formula = formula.ToString(); + cf.Style.Fill.BackgroundColor.Color = color; + } + + private void Serialize(string fileName) + { + var filePath = _exportPath + fileName + ".xlsx"; + _excelFile.SaveAs(new FileInfo(filePath)); + } + } +} diff --git a/DataSetExplorer/Core/CleanCodeAnalysis/ICleanCodeAnalysisService.cs b/DataSetExplorer/Core/CleanCodeAnalysis/ICleanCodeAnalysisService.cs new file mode 100644 index 00000000..b217380e --- /dev/null +++ b/DataSetExplorer/Core/CleanCodeAnalysis/ICleanCodeAnalysisService.cs @@ -0,0 +1,11 @@ +using DataSetExplorer.UI.Controllers.Dataset.DTOs; +using FluentResults; + +namespace DataSetExplorer.Core.CleanCodeAnalysis +{ + public interface ICleanCodeAnalysisService + { + public Result ExportDatasetAnalysis(int datasetId, CleanCodeAnalysisDTO analysisOptions); + public Result ExportProjectAnalysis(int projectId, CleanCodeAnalysisDTO analysisOptions); + } +} diff --git a/DataSetExplorer/Core/CleanCodeAnalysis/Model/Identifier.cs b/DataSetExplorer/Core/CleanCodeAnalysis/Model/Identifier.cs new file mode 100644 index 00000000..3ff252bf --- /dev/null +++ b/DataSetExplorer/Core/CleanCodeAnalysis/Model/Identifier.cs @@ -0,0 +1,28 @@ +namespace DataSetExplorer.Core.CleanCodeAnalysis.Model +{ + public class Identifier + { + public int Id { get; private set; } + public string Name { get; private set; } + public IdentifierType Type { get; private set; } + + public Identifier(string name, IdentifierType type) + { + Name = name; + Type = type; + } + + private Identifier() { } + + } + + public enum IdentifierType + { + Class, + Field, + Variable, + Parameter, + Property, + Method + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/CommunityDetection/GraphInstanceService.cs b/DataSetExplorer/Core/CommunityDetection/GraphInstanceService.cs new file mode 100644 index 00000000..126f93dc --- /dev/null +++ b/DataSetExplorer/Core/CommunityDetection/GraphInstanceService.cs @@ -0,0 +1,33 @@ +using DataSetExplorer.Core.DataSets.Model; +using DataSetExplorer.Core.DataSets.Repository; +using FluentResults; + +namespace DataSetExplorer.Core.DataSets +{ + public class GraphInstanceService : IGraphInstanceService + { + private readonly IGraphInstanceRepository _graphInstanceRepository; + private readonly IInstanceRepository _instanceRepository; + + public GraphInstanceService(IGraphInstanceRepository graphInstanceRepository, IInstanceRepository instanceRepository) + { + _graphInstanceRepository = graphInstanceRepository; + _instanceRepository = instanceRepository; + } + + public Result GetGraphInstanceWithRelatedInstances(int projectId, int instanceId) + { + var instance = _instanceRepository.Get(instanceId); + var graphInstance = _graphInstanceRepository.GetGraphInstanceWithRelatedInstances(projectId, instance.CodeSnippetId); + if (graphInstance == default) return Result.Fail($"Project with id: {projectId} or instance with id: {instanceId} does not exist."); + return Result.Ok(graphInstance); + } + + public Result GetGraphInstanceWithRelatedInstances(int projectId, string instanceCodeSnippetId) + { + var graphInstance = _graphInstanceRepository.GetGraphInstanceWithRelatedInstances(projectId, instanceCodeSnippetId); + if (graphInstance == default) return Result.Fail($"Project with id: {projectId} or instance with codeSnippetId: {instanceCodeSnippetId} does not exist."); + return Result.Ok(graphInstance); + } + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/CommunityDetection/IGraphInstanceService.cs b/DataSetExplorer/Core/CommunityDetection/IGraphInstanceService.cs new file mode 100644 index 00000000..09f6b537 --- /dev/null +++ b/DataSetExplorer/Core/CommunityDetection/IGraphInstanceService.cs @@ -0,0 +1,11 @@ +using DataSetExplorer.Core.DataSets.Model; +using FluentResults; + +namespace DataSetExplorer.Core.DataSets +{ + public interface IGraphInstanceService + { + Result GetGraphInstanceWithRelatedInstances(int projectId, int instanceId); + Result GetGraphInstanceWithRelatedInstances(int projectId, string instanceCodeSnippetId); + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/CommunityDetection/Model/GraphInstance.cs b/DataSetExplorer/Core/CommunityDetection/Model/GraphInstance.cs new file mode 100644 index 00000000..16d59898 --- /dev/null +++ b/DataSetExplorer/Core/CommunityDetection/Model/GraphInstance.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; + +namespace DataSetExplorer.Core.DataSets.Model +{ + public class GraphInstance + { + public int Id { get; private set; } + public string CodeSnippetId { get; private set; } + public string Link { get; private set; } + public List RelatedInstances { get; private set; } + + internal GraphInstance(string codeSnippetId, string link, List relatedInstances) + { + CodeSnippetId = codeSnippetId; + Link = link; + RelatedInstances = relatedInstances; + Validate(); + } + + private GraphInstance() + { + } + + public GraphInstance(string codeSnippetId) + { + CodeSnippetId = codeSnippetId; + } + + private void Validate() + { + if (string.IsNullOrEmpty(CodeSnippetId)) throw new ArgumentException("CodeSnippetId cannot be empty."); + } + + public override int GetHashCode() + { + return CodeSnippetId.GetHashCode(); + } + + public override bool Equals(object other) + { + return other is Instance instance && CodeSnippetId.Equals(instance.CodeSnippetId); + } + } +} diff --git a/DataSetExplorer/Core/CommunityDetection/Model/GraphRelatedInstance.cs b/DataSetExplorer/Core/CommunityDetection/Model/GraphRelatedInstance.cs new file mode 100644 index 00000000..2f71264a --- /dev/null +++ b/DataSetExplorer/Core/CommunityDetection/Model/GraphRelatedInstance.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +namespace DataSetExplorer.Core.DataSets.Model +{ + public class GraphRelatedInstance + { + public int Id { get; private set; } + public string CodeSnippetId { get; private set; } + public RelationType RelationType { get; private set; } + public Dictionary CouplingTypeAndStrength { get; private set; } + public string Link { get; private set; } + + public GraphRelatedInstance(string codeSnippetId, RelationType relationType, Dictionary couplingTypeAndStrength, + string link) + { + CodeSnippetId = codeSnippetId; + CouplingTypeAndStrength = couplingTypeAndStrength; + RelationType = relationType; + Link = link; + } + + private GraphRelatedInstance() + { + } + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/CommunityDetection/Repository/GraphInstanceRepository.cs b/DataSetExplorer/Core/CommunityDetection/Repository/GraphInstanceRepository.cs new file mode 100644 index 00000000..dc3f8d80 --- /dev/null +++ b/DataSetExplorer/Core/CommunityDetection/Repository/GraphInstanceRepository.cs @@ -0,0 +1,26 @@ +using System.Linq; +using DataSetExplorer.Core.DataSets.Model; +using DataSetExplorer.Infrastructure.Database; +using Microsoft.EntityFrameworkCore; + +namespace DataSetExplorer.Core.DataSets.Repository +{ + public class GraphInstanceRepository : IGraphInstanceRepository + { + private readonly DataSetExplorerContext _dbContext; + + public GraphInstanceRepository(DataSetExplorerContext dbContext) + { + _dbContext = dbContext; + } + + public GraphInstance GetGraphInstanceWithRelatedInstances(int projectId, string codeSnippetId) + { + var graphInstances = _dbContext.DataSetProjects + .Include(p => p.GraphInstances).ThenInclude(i => i.RelatedInstances) + .FirstOrDefault(p => p.Id == projectId).GraphInstances; + + return graphInstances.Find(i => i.CodeSnippetId.Equals(codeSnippetId)); + } + } +} diff --git a/DataSetExplorer/Core/CommunityDetection/Repository/IGraphInstanceRepository.cs b/DataSetExplorer/Core/CommunityDetection/Repository/IGraphInstanceRepository.cs new file mode 100644 index 00000000..0d345a72 --- /dev/null +++ b/DataSetExplorer/Core/CommunityDetection/Repository/IGraphInstanceRepository.cs @@ -0,0 +1,9 @@ +using DataSetExplorer.Core.DataSets.Model; + +namespace DataSetExplorer.Core.DataSets.Repository +{ + public interface IGraphInstanceRepository + { + GraphInstance GetGraphInstanceWithRelatedInstances(int projectId, string codeSnippetId); + } +} diff --git a/DataSetExplorer/Core/DataSetSerializer/AnnotationConsistencyByMetricsExporter.cs b/DataSetExplorer/Core/DataSetSerializer/AnnotationConsistencyByMetricsExporter.cs index 58ce3426..42ff227b 100644 --- a/DataSetExplorer/Core/DataSetSerializer/AnnotationConsistencyByMetricsExporter.cs +++ b/DataSetExplorer/Core/DataSetSerializer/AnnotationConsistencyByMetricsExporter.cs @@ -28,7 +28,7 @@ public void ExportAnnotationsFromAnnotator(int annotatorId, List insta Serialize(fileName); } - public void ExportAnnotatorsForSeverity(int severity, List instances, + public void ExportAnnotatorsForSeverity(string severity, List instances, string fileName) { InitializeExcelSheet(_multipleAnnotatorsTemplatePath); @@ -53,14 +53,14 @@ private void PopulateTemplateForAnnotator(int annotatorId, List instan } } - private void PopulateTemplateForSeverity(int severity, List instances) + private void PopulateTemplateForSeverity(string severity, List instances) { var j = 0; foreach (var instance in instances) { foreach (var annotation in instance.Annotations) { - if (annotation.Severity == severity) + if (annotation.Severity.Equals(severity)) { _sheet.Cells[2 + j, 1].Value = annotation.Annotator.Id; PopulateMetrics(instance.MetricFeatures, 2 + j, 2); diff --git a/DataSetExplorer/Core/DataSetSerializer/CompleteDataSetExporter.cs b/DataSetExplorer/Core/DataSetSerializer/CompleteDataSetExportationService.cs similarity index 81% rename from DataSetExplorer/Core/DataSetSerializer/CompleteDataSetExporter.cs rename to DataSetExplorer/Core/DataSetSerializer/CompleteDataSetExportationService.cs index ff1c0e9e..852f8b4c 100644 --- a/DataSetExplorer/Core/DataSetSerializer/CompleteDataSetExporter.cs +++ b/DataSetExplorer/Core/DataSetSerializer/CompleteDataSetExportationService.cs @@ -3,29 +3,31 @@ using System.Linq; using CodeModel.CaDETModel.CodeItems; using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.AnnotationSchema.Model; +using DataSetExplorer.Core.AnnotationSchema.Repository; using DataSetExplorer.Core.DataSets.Model; -using DataSetExplorer.Core.DataSetSerializer.ViewModel; using OfficeOpenXml; namespace DataSetExplorer.Core.DataSetSerializer { - class CompleteDataSetExporter + class CompleteDataSetExportationService: ICompleteDataSetExportationService { private readonly string _dataSetWithAnnotationsTemplatePath = "./Core/DataSetSerializer/Template/Complete_Dataset_With_Annotations_Template.xlsx"; private readonly string _dataSetWithHeuristicsTemplatePath = "./Core/DataSetSerializer/Template/Complete_Dataset_With_Heuristics_Template.xlsx"; private readonly string _dataSetWithMetricsTemplatePath = "./Core/DataSetSerializer/Template/Complete_Dataset_With_Metrics_Template.xlsx"; - private readonly string _exportPath; + private string _exportPath; private ExcelPackage _excelFile; private ExcelWorksheet _sheet; - private readonly ColumnHeuristicsModel _requiredSmells = new ColumnHeuristicsModel(); + private readonly IAnnotationSchemaRepository _annotationSchemaRepository; - public CompleteDataSetExporter(string exportPath) + public CompleteDataSetExportationService(IAnnotationSchemaRepository annotationSchemaRepository) { - _exportPath = exportPath; + _annotationSchemaRepository = annotationSchemaRepository; } - public void Export(List dataSetInstances, string smell, string fileName) + public void Export(string outputPath, List dataSetInstances, string smell, string fileName) { + _exportPath = outputPath; ExportDataSetWithAnnotations(dataSetInstances, fileName); ExportDataSetWithMetrics(dataSetInstances, fileName); ExportDataSetWithHeuristics(dataSetInstances, smell, fileName); @@ -48,8 +50,11 @@ private void PopulateAnnotationsTemplate(List instances) var row = 3 + i; _sheet.Cells[row, 1].Value = instances[i].CodeSnippetId; _sheet.Cells[row, 2].Value = instances[i].Link; - _sheet.Cells[row, 3].Value = + if (instances[i].Annotations.Count > 0) + { + _sheet.Cells[row, 3].Value = instances[i].Annotations.First().InstanceSmell.Name; + } _sheet.Cells[row, 4].Value = instances[i].ProjectLink; PopulateAnnotations(instances[i], row); } @@ -160,7 +165,8 @@ private void PopulateHeuristicsTemplate(List instances, string smell) private void PopulateHeuristicsHeader(List instances, string smell, List annotators) { - var smellHeuristics = _requiredSmells.GetHeuristicsByCodeSmellName(smell); + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinitionByName(smell); + var smellHeuristics = _annotationSchemaRepository.GetCodeSmellDefinition(codeSmellDefinition.Id).Heuristics.ToList(); var numOfHeuristics = smellHeuristics.Count(); _sheet.Cells[1, 2, 1, 1 + (numOfHeuristics * annotators.Count)].Merge = true; @@ -173,7 +179,8 @@ private void PopulateHeuristicsHeader(List instances, string smell, Li private void PopulateHeuristics(List annotations, int row, List annotators, string smell) { - var smellHeuristics = _requiredSmells.GetHeuristicsByCodeSmellName(smell); + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinitionByName(smell); + var smellHeuristics = _annotationSchemaRepository.GetCodeSmellDefinition(codeSmellDefinition.Id).Heuristics.ToList(); var numOfHeuristics = smellHeuristics.Count(); for (int i = 0; i < annotations.Count(); i++) { @@ -188,28 +195,40 @@ private void PopulateHeuristics(List annotations, int row, List smellHeuristics, List applicableHeuristics, int row, int annotationNum) + private void PopulateHeuristicValues(List smellHeuristics, List applicableHeuristics, int row, int annotationNum) { var numOfHeuristics = smellHeuristics.Count(); applicableHeuristics = GetCodeSmellHeuristicsForExport(smellHeuristics, applicableHeuristics); - for (var i = 0; i < applicableHeuristics.Count(); i++) + for (var i = 0; i < smellHeuristics.Count(); i++) { - _sheet.Cells[4, 2 + (numOfHeuristics * annotationNum) + i].Value = applicableHeuristics[i].Description; + _sheet.Cells[4, 2 + (numOfHeuristics * annotationNum) + i].Value = smellHeuristics[i].Name; _sheet.Cells[3, 2 + (numOfHeuristics * annotationNum), 3, 1 + (numOfHeuristics * annotationNum) + numOfHeuristics].Merge = true; _sheet.Cells[3, 2 + (numOfHeuristics * annotationNum)].Value = "Heuristics"; + } + + if (applicableHeuristics.Count == 0) + { + for (var i = 0; i < smellHeuristics.Count(); i++) + { + _sheet.Cells[row, 2 + (numOfHeuristics * annotationNum) + i].Value = "FALSE"; + } + } + + for (var i = 0; i < applicableHeuristics.Count(); i++) + { _sheet.Cells[row, 2 + (numOfHeuristics * annotationNum) + i].Value = applicableHeuristics[i].IsApplicable; } } - private List GetCodeSmellHeuristicsForExport(List heuristics, List applicableHeuristics) + private List GetCodeSmellHeuristicsForExport(List heuristics, List applicableHeuristics) { var heuristicsForExport = new List(); foreach (var heuristic in heuristics) { foreach (var applicableHeur in applicableHeuristics) { - if (heuristic.Equals(applicableHeur.Description)) { + if (heuristic.Name.Equals(applicableHeur.Description)) { heuristicsForExport.Add(applicableHeur); break; } diff --git a/DataSetExplorer/Core/DataSetSerializer/DataSetExportationService.cs b/DataSetExplorer/Core/DataSetSerializer/DataSetExportationService.cs index c293d8aa..b4a65f48 100644 --- a/DataSetExplorer/Core/DataSetSerializer/DataSetExportationService.cs +++ b/DataSetExplorer/Core/DataSetSerializer/DataSetExportationService.cs @@ -1,7 +1,11 @@ using System.Collections.Generic; using System.IO; using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.Auth; using DataSetExplorer.Core.DataSets; +using DataSetExplorer.Core.DataSets.Model; +using DataSetExplorer.Core.DataSets.Repository; +using DataSetExplorer.UI.Controllers.Dataset.DTOs; using FluentResults; namespace DataSetExplorer.Core.DataSetSerializer @@ -9,21 +13,51 @@ namespace DataSetExplorer.Core.DataSetSerializer class DataSetExportationService : IDataSetExportationService { private readonly FullDataSetFactory _fullDataSetFactory; + private readonly IDraftDataSetExportationService _draftDataSetExportationService; + private readonly ICompleteDataSetExportationService _completeDataSetExportationService; + private readonly IDataSetRepository _dataSetRepository; + private readonly IAuthService _authService; - public DataSetExportationService(FullDataSetFactory fullDataSetFactory) + public DataSetExportationService(FullDataSetFactory fullDataSetFactory, IDraftDataSetExportationService draftDataSetExportationService, + IDataSetRepository dataSetRepository, ICompleteDataSetExportationService completeDataSetExportationService, + IAuthService authService) { _fullDataSetFactory = fullDataSetFactory; + _dataSetRepository = dataSetRepository; + _draftDataSetExportationService = draftDataSetExportationService; + _completeDataSetExportationService = completeDataSetExportationService; + _authService = authService; } - public Result Export(IDictionary projects, List annotators, string outputPath) + public Result ExportDraft(DraftDataSetExportDTO dataSetDTO) { + var dataSet = GetDataSetForExport(dataSetDTO.Id).Value; + var exportPath = _draftDataSetExportationService.Export(dataSetDTO.ExportPath, dataSetDTO.AnnotatorId, dataSet); + return Result.Ok(exportPath); + } + + public Result ExportComplete(int datasetId, CompleteDataSetExportDTO dataSetDTO) + { + var annotationsFilesPaths = File.ReadAllLines(dataSetDTO.AnnotationsPath); + return this.Export(datasetId, annotationsFilesPaths, dataSetDTO.ExportPath); + } + + private Result GetDataSetForExport(int id) + { + var dataSet = _dataSetRepository.GetDataSetForExport(id); + if (dataSet == default) return Result.Fail($"DataSet with id: {id} does not exist."); + return Result.Ok(dataSet); + } + + public Result Export(int datasetId, string[] annotationsFilesPaths, string outputPath) + { + List annotators = _authService.GetAll().Value; try { - var instancesGroupedBySmells = _fullDataSetFactory.GetAnnotatedInstancesGroupedBySmells(projects, annotators, annotatorId: null); - var exporter = new CompleteDataSetExporter(outputPath); + var instancesGroupedBySmells = _fullDataSetFactory.GetAnnotatedInstancesGroupedBySmells(datasetId, annotators, annotationsFilesPaths); foreach (var codeSmellGroup in instancesGroupedBySmells) { - exporter.Export(codeSmellGroup.Instances, codeSmellGroup.CodeSmell.Name, "DataSet_" + codeSmellGroup.CodeSmell.Name); + _completeDataSetExportationService.Export(outputPath, codeSmellGroup.Instances, codeSmellGroup.CodeSmell.Name, "DataSet_" + codeSmellGroup.CodeSmell.Name); } return Result.Ok("Data set exported: " + outputPath); } catch (IOException e) diff --git a/DataSetExplorer/Core/DataSetSerializer/DraftDataSetExporter.cs b/DataSetExplorer/Core/DataSetSerializer/DraftDataSetExportationService.cs similarity index 65% rename from DataSetExplorer/Core/DataSetSerializer/DraftDataSetExporter.cs rename to DataSetExplorer/Core/DataSetSerializer/DraftDataSetExportationService.cs index e19fc289..0eafe024 100644 --- a/DataSetExplorer/Core/DataSetSerializer/DraftDataSetExporter.cs +++ b/DataSetExplorer/Core/DataSetSerializer/DraftDataSetExportationService.cs @@ -2,28 +2,29 @@ using System.IO; using System.Linq; using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.AnnotationSchema.Model; +using DataSetExplorer.Core.AnnotationSchema.Repository; using DataSetExplorer.Core.DataSets.Model; -using DataSetExplorer.Core.DataSetSerializer.ViewModel; using OfficeOpenXml; namespace DataSetExplorer.Core.DataSetSerializer { - class DraftDataSetExporter + public class DraftDataSetExportationService : IDraftDataSetExportationService { private readonly string _templatePath = "./Core/DataSetSerializer/Template/New_Dataset_Template.xlsx"; private string _exportPath; private ExcelPackage _excelFile; private ExcelWorksheet _sheet; - private readonly ColumnHeuristicsModel _requiredSmells = new(); + private readonly IAnnotationSchemaRepository _annotationSchemaRepository; - public DraftDataSetExporter(string exportPath) + public DraftDataSetExportationService(IAnnotationSchemaRepository annotationSchemaRepository) { - _exportPath = exportPath; + _annotationSchemaRepository = annotationSchemaRepository; } - public string Export(int annotatorId, DataSet dataSet) + public string Export(string exportPath, int annotatorId, DataSet dataSet) { - _exportPath += dataSet.Name + "/"; + _exportPath = exportPath + dataSet.Name + "/"; ExcelPackage.LicenseContext = LicenseContext.NonCommercial; foreach (var project in dataSet.Projects) { @@ -50,7 +51,7 @@ private void PopulateSheets(int annotatorId, DataSetProject project) _sheet = _excelFile.Workbook.Worksheets[i]; PopulateBasicInfo(annotatorId, project, candidate); var smellHeuristics = PopulateSmellHeuristics(candidate); - PopulateAnnotatedInstances(candidate, smellHeuristics); + PopulateAnnotatedInstances(candidate, smellHeuristics, annotatorId); Serialize(project.Name + annotatorId); i++; } @@ -64,18 +65,20 @@ private void PopulateBasicInfo(int annotatorId, DataSetProject project, SmellCan _sheet.Cells[2, 3].Value = annotatorId; } - private List PopulateSmellHeuristics(SmellCandidateInstances candidate) + private List PopulateSmellHeuristics(SmellCandidateInstances candidate) { - var smellHeuristics = _requiredSmells.GetHeuristicsByCodeSmellName(candidate.CodeSmell.Name); + var codeSmellDefinition = _annotationSchemaRepository.GetCodeSmellDefinitionByName(candidate.CodeSmell.Name); + var smellHeuristics = _annotationSchemaRepository.GetCodeSmellDefinition(codeSmellDefinition.Id).Heuristics.ToList(); for (var i = 0; i < smellHeuristics.Count; i++) { - _sheet.Cells[2, 4 + (2 * i)].Value = smellHeuristics[i]; + _sheet.Cells[2, 4 + (2 * i)].Value = smellHeuristics[i].Name; } _sheet.Cells[3, 4 + (smellHeuristics.Count * 2)].Value = "Note"; return smellHeuristics; } - private void PopulateAnnotatedInstances(SmellCandidateInstances candidate, List smellHeuristics) + private void PopulateAnnotatedInstances(SmellCandidateInstances candidate, List smellHeuristics, + int annotatorId) { var row = 4; foreach (var instance in candidate.Instances) @@ -83,34 +86,41 @@ private void PopulateAnnotatedInstances(SmellCandidateInstances candidate, List< if (instance.Annotations.Count == 0) continue; _sheet.Cells[row, 1].Value = instance.CodeSnippetId; _sheet.Cells[row, 2].Value = instance.Link; - PopulateAnnotations(smellHeuristics, row, instance); + PopulateAnnotations(smellHeuristics, row, instance, annotatorId); row++; } } - private void PopulateAnnotations(List smellHeuristics, int row, Instance instance) + private void PopulateAnnotations(List smellHeuristics, int row, Instance instance, + int annotatorId) { foreach (var annotation in instance.Annotations) { + if (annotation.Annotator.Id != annotatorId) continue; _sheet.Cells[row, 3].Value = annotation.Severity; foreach (var applicableHeuristic in annotation.ApplicableHeuristics) { - var index = smellHeuristics.FindIndex(h => h.Equals(applicableHeuristic.Description)); + var index = smellHeuristics.FindIndex(h => h.Name.Equals(applicableHeuristic.Description)); + _sheet.Cells[3, 4 + (2 * index)].Value = "Applicable?"; _sheet.Cells[row, 4 + (2 * index)].Value = "Yes"; + _sheet.Cells[3, 4 + (2 * index) + 1].Value = "Reasoning"; + _sheet.Cells[row, 4 + (2 * index) + 1].Value = applicableHeuristic.ReasonForApplicability; } PopulateNotApplicableAnnotations(smellHeuristics, row, annotation); _sheet.Cells[row, 4 + (smellHeuristics.Count * 2)].Value = annotation.Note; } } - private void PopulateNotApplicableAnnotations(List smellHeuristics, int row, Annotation annotation) + private void PopulateNotApplicableAnnotations(List smellHeuristics, int row, Annotation annotation) { var notApplicableHeuristics = - smellHeuristics.Except(annotation.ApplicableHeuristics.ConvertAll(h => h.Description)); + smellHeuristics.ConvertAll(h => h.Name).Except(annotation.ApplicableHeuristics.ConvertAll(h => h.Description)); foreach (var heuristic in notApplicableHeuristics) { - var index = smellHeuristics.FindIndex(h => h.Equals(heuristic)); + var index = smellHeuristics.FindIndex(h => h.Name.Equals(heuristic)); _sheet.Cells[row, 4 + (2 * index)].Value = "No"; + _sheet.Cells[3, 4 + (2 * index)].Value = "Applicable?"; + _sheet.Cells[3, 4 + (2 * index) + 1].Value = "Reasoning"; } } diff --git a/DataSetExplorer/Core/DataSetSerializer/ExcelImporter.cs b/DataSetExplorer/Core/DataSetSerializer/ExcelImporter.cs index 29a1fd85..b2bf75bb 100644 --- a/DataSetExplorer/Core/DataSetSerializer/ExcelImporter.cs +++ b/DataSetExplorer/Core/DataSetSerializer/ExcelImporter.cs @@ -4,6 +4,7 @@ using System.Linq; using DataSetExplorer.Core.Annotations.Model; using DataSetExplorer.Core.DataSets.Model; +using DataSetExplorer.Core.DataSets.Repository; using OfficeOpenXml; namespace DataSetExplorer.Core.DataSetSerializer @@ -13,19 +14,31 @@ public class ExcelImporter private const int StartingInstanceRow = 4; private const int StartingHeuristicColumn = 4; private readonly string _sourceFolder; + private readonly IAnnotationRepository _annotationRepository; public ExcelImporter(string sourceFolder) { _sourceFolder = sourceFolder; } + public ExcelImporter(string sourceFolder, IAnnotationRepository annotationRepository) + { + _sourceFolder = sourceFolder; + _annotationRepository = annotationRepository; + } + + public ExcelImporter(IAnnotationRepository annotationRepository) + { + _annotationRepository = annotationRepository; + } + /// /// This logic is highly dependent on the appropriate excel file structure. /// It examines excel documents in the sourceFolder directory and its subdirectories. /// The excel documents must be formatted following these guidelines https://github.com/Clean-CaDET/platform/wiki/Dataset-Explorer#building-your-dataset /// /// Name of the returned dataset project. - /// A dataset project constructed from one or more excel documents. + /// A dataset project constructed from one or more excel documents. public DataSetProject Import(string projectName) { var project = new DataSetProject(projectName); @@ -33,7 +46,8 @@ public DataSetProject Import(string projectName) var sheets = GetWorksheets(GetExcelDocuments()); foreach (var excelWorksheet in sheets) { - project.AddCandidateInstance(new SmellCandidateInstances(new CodeSmell(excelWorksheet.Name), ExtractInstances(excelWorksheet))); + if (_annotationRepository != null) project.AddCandidateInstance(new SmellCandidateInstances(_annotationRepository.GetCodeSmell(excelWorksheet.Name), ExtractInstances(excelWorksheet))); + else project.AddCandidateInstance(new SmellCandidateInstances(new CodeSmell(excelWorksheet.Name), ExtractInstances(excelWorksheet))); } return project; @@ -57,7 +71,29 @@ private static List GetWorksheets(IEnumerable docu return sheets; } - private static List ExtractInstances(ExcelWorksheet sheet) + internal DataSetProject ImportAnnotationsFile(string annotationsFilePath) + { + var project = new DataSetProject(); + + ExcelPackage.LicenseContext = LicenseContext.NonCommercial; + var sheets = new ExcelPackage(new FileInfo(annotationsFilePath)).Workbook.Worksheets; + foreach (var excelWorksheet in sheets) + { + project.AddCandidateInstance(new SmellCandidateInstances(_annotationRepository.GetCodeSmell(excelWorksheet.Name), ExtractInstances(excelWorksheet))); + project.Url = excelWorksheet.Cells["A2"].Text; + } + + return project; + } + + internal string GetProjectUrl(string annotationsFilePath) + { + ExcelPackage.LicenseContext = LicenseContext.NonCommercial; + var sheets = new ExcelPackage(new FileInfo(annotationsFilePath)).Workbook.Worksheets; + return sheets[0].Cells["A2"].Text; + } + + private List ExtractInstances(ExcelWorksheet sheet) { var instances = new List(); for (var row = StartingInstanceRow; row <= sheet.Dimension.End.Row; row++) @@ -107,7 +143,7 @@ internal List ImportAnnotatedInstancesFromDataSet(string path) var finalAnnotation = sheet.Cells["AD" + row].Text; Instance instance = new Instance(codeSnippetId, projectLink); // Dummy values for DataSetAnnotation constructor to pass validations (the final annotation is the only important parameter in this case). - Annotation annotation = new Annotation(new CodeSmell("Large_Class"), int.Parse(finalAnnotation), new Annotator(1), new List() { new SmellHeuristic("", true, "") }, ""); + Annotation annotation = new Annotation(new CodeSmell("Large_Class", SnippetType.Class), finalAnnotation, new Annotator(1), new List() { new SmellHeuristic("", true, "") }, ""); instance.AddAnnotation(annotation); instances.Add(instance); } @@ -115,15 +151,16 @@ internal List ImportAnnotatedInstancesFromDataSet(string path) return instances; } - private static Annotation GetAnnotation(ExcelWorksheet sheet, int row) + private Annotation GetAnnotation(ExcelWorksheet sheet, int row) { try { - var smellSeverity = int.Parse(sheet.Cells["C" + row].Text); + var smellSeverity = sheet.Cells["C" + row].Text; var annotatorId = int.Parse(sheet.Cells["C2"].Text); - var codeSmell = sheet.Cells["B2"].Text; + var codeSmellName = sheet.Cells["B2"].Text; var heuristics = GetHeuristics(sheet, row); - return new Annotation(codeSmell, smellSeverity, new Annotator(annotatorId), heuristics, ""); // TODO - extract note from sheet + if (_annotationRepository != null) return new Annotation(_annotationRepository.GetCodeSmell(codeSmellName), smellSeverity, new Annotator(annotatorId), heuristics, ""); // TODO - extract note from sheet + return new Annotation(codeSmellName, smellSeverity, new Annotator(annotatorId), heuristics, ""); // TODO - extract note from sheet } catch (InvalidOperationException e) { diff --git a/DataSetExplorer/Core/DataSetSerializer/ICompleteDataSetExportationService.cs b/DataSetExplorer/Core/DataSetSerializer/ICompleteDataSetExportationService.cs new file mode 100644 index 00000000..6c9f959c --- /dev/null +++ b/DataSetExplorer/Core/DataSetSerializer/ICompleteDataSetExportationService.cs @@ -0,0 +1,10 @@ +using DataSetExplorer.Core.DataSets.Model; +using System.Collections.Generic; + +namespace DataSetExplorer.Core.DataSetSerializer +{ + public interface ICompleteDataSetExportationService + { + public void Export(string exportPath, List dataSetInstances, string smell, string fileName); + } +} diff --git a/DataSetExplorer/Core/DataSetSerializer/IDataSetExportationService.cs b/DataSetExplorer/Core/DataSetSerializer/IDataSetExportationService.cs index ed649a2c..cc69e3d1 100644 --- a/DataSetExplorer/Core/DataSetSerializer/IDataSetExportationService.cs +++ b/DataSetExplorer/Core/DataSetSerializer/IDataSetExportationService.cs @@ -1,11 +1,12 @@ -using System.Collections.Generic; -using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.UI.Controllers.Dataset.DTOs; using FluentResults; namespace DataSetExplorer.Core.DataSetSerializer { public interface IDataSetExportationService { - public Result Export(IDictionary projects, List annotators, string outputPath); + public Result Export(int datasetId, string[] annotationsFilesPaths, string outputPath); + public Result ExportDraft(DraftDataSetExportDTO dataSetDTO); + public Result ExportComplete(int datasetId, CompleteDataSetExportDTO dataSetDTO); } } diff --git a/DataSetExplorer/Core/DataSetSerializer/IDraftDataSetExportationService.cs b/DataSetExplorer/Core/DataSetSerializer/IDraftDataSetExportationService.cs new file mode 100644 index 00000000..3eb4d3c7 --- /dev/null +++ b/DataSetExplorer/Core/DataSetSerializer/IDraftDataSetExportationService.cs @@ -0,0 +1,9 @@ +using DataSetExplorer.Core.DataSets.Model; + +namespace DataSetExplorer.Core.DataSetSerializer +{ + public interface IDraftDataSetExportationService + { + public string Export(string exportPath, int annotatorId, DataSet dataSet); + } +} diff --git a/DataSetExplorer/Core/DataSetSerializer/Template/Clean_Classes_Analysis_Template.xlsx b/DataSetExplorer/Core/DataSetSerializer/Template/Clean_Classes_Analysis_Template.xlsx new file mode 100644 index 00000000..c9d9e282 Binary files /dev/null and b/DataSetExplorer/Core/DataSetSerializer/Template/Clean_Classes_Analysis_Template.xlsx differ diff --git a/DataSetExplorer/Core/DataSetSerializer/Template/Clean_Functions_Analysis_Template.xlsx b/DataSetExplorer/Core/DataSetSerializer/Template/Clean_Functions_Analysis_Template.xlsx new file mode 100644 index 00000000..875cac0f Binary files /dev/null and b/DataSetExplorer/Core/DataSetSerializer/Template/Clean_Functions_Analysis_Template.xlsx differ diff --git a/DataSetExplorer/Core/DataSetSerializer/Template/Clean_Names_Analysis_Template.xlsx b/DataSetExplorer/Core/DataSetSerializer/Template/Clean_Names_Analysis_Template.xlsx new file mode 100644 index 00000000..2dd54591 Binary files /dev/null and b/DataSetExplorer/Core/DataSetSerializer/Template/Clean_Names_Analysis_Template.xlsx differ diff --git a/DataSetExplorer/Core/DataSetSerializer/TextFileExporter.cs b/DataSetExplorer/Core/DataSetSerializer/TextFileExporter.cs index 20552a45..c2480315 100644 --- a/DataSetExplorer/Core/DataSetSerializer/TextFileExporter.cs +++ b/DataSetExplorer/Core/DataSetSerializer/TextFileExporter.cs @@ -56,7 +56,7 @@ private void SaveSnippetLinkToFile(List instances, string fileName) WriteToFile(sb.ToString(), fileName); } - internal void ExportMembersFromAnnotatedClasses(Dictionary> classesGroupedBySeverity, List annotatedClasses) + internal void ExportMembersFromAnnotatedClasses(Dictionary> classesGroupedBySeverity, List annotatedClasses) { foreach (var severity in classesGroupedBySeverity.Keys) { diff --git a/DataSetExplorer/Core/DataSetSerializer/ViewModel/ColumnHeuristicsModel.cs b/DataSetExplorer/Core/DataSetSerializer/ViewModel/ColumnHeuristicsModel.cs index 4c0090c6..f52521c5 100644 --- a/DataSetExplorer/Core/DataSetSerializer/ViewModel/ColumnHeuristicsModel.cs +++ b/DataSetExplorer/Core/DataSetSerializer/ViewModel/ColumnHeuristicsModel.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.DataSets.Model; namespace DataSetExplorer.Core.DataSetSerializer.ViewModel { @@ -12,11 +13,11 @@ internal ColumnHeuristicsModel() { _heuristics = new Dictionary>(); //TODO: Load from DB or config. - _heuristics.Add(new CodeSmell("Large_Class"), LCHeuristics()); - _heuristics.Add(new CodeSmell("Long_Method"), LMHeuristics()); - _heuristics.Add(new CodeSmell("Feature_Envy"), FEHeuristics()); - _heuristics.Add(new CodeSmell("Data_Class"), DCHeuristics()); - _heuristics.Add(new CodeSmell("Refused_Bequest"), RBHeuristics()); + _heuristics.Add(new CodeSmell("Large_Class", SnippetType.Class), LCHeuristics()); + _heuristics.Add(new CodeSmell("Long_Method", SnippetType.Function), LMHeuristics()); + _heuristics.Add(new CodeSmell("Feature_Envy", SnippetType.Function), FEHeuristics()); + _heuristics.Add(new CodeSmell("Data_Class", SnippetType.Class), DCHeuristics()); + _heuristics.Add(new CodeSmell("Refused_Bequest", SnippetType.Class), RBHeuristics()); } internal List GetHeuristics(CodeSmell smell) diff --git a/DataSetExplorer/Core/DataSets/CaDETToDataSetProjectBuilder.cs b/DataSetExplorer/Core/DataSets/CaDETToDataSetProjectBuilder.cs index 11aa1d7a..f12a07f2 100644 --- a/DataSetExplorer/Core/DataSets/CaDETToDataSetProjectBuilder.cs +++ b/DataSetExplorer/Core/DataSets/CaDETToDataSetProjectBuilder.cs @@ -6,6 +6,7 @@ using CodeModel.CaDETModel.CodeItems; using DataSetExplorer.Core.Annotations.Model; using DataSetExplorer.Core.DataSets.Model; +using DataSetExplorer.Core.CleanCodeAnalysis.Model; namespace DataSetExplorer.Core.DataSets { @@ -87,6 +88,7 @@ internal CaDETToDataSetProjectBuilder RandomizeMemberSelection() internal DataSetProject Build() { var builtDataSetProject = new DataSetProject(_projectName, _projectAndCommitUrl); + builtDataSetProject.GraphInstances = CaDETToGraphClasses(_cadetProject.Classes); foreach (var smell in _codeSmells) { @@ -96,6 +98,101 @@ internal DataSetProject Build() return builtDataSetProject; } + private List CaDETToGraphClasses(List cadetClasses) + { + CreateCouplingMap(cadetClasses); + return cadetClasses.Select(c => new GraphInstance( + c.FullName, GetCodeUrl(c.FullName), FindGraphClassRelatedInstances(c))).ToList(); + } + + private List FindGraphClassRelatedInstances(CaDETClass c) + { + var relatedInstances = new List(); + if (c.Parent != null) + { + var couplingTypeAndStrength = new Dictionary(); + couplingTypeAndStrength.Add(CouplingType.Parent, 1); + relatedInstances.Add(new GraphRelatedInstance(c.Parent.FullName, RelationType.Parent, couplingTypeAndStrength, GetCodeUrl(c.Parent.FullName))); + } + AddSubclassesToGraphRelatedInstances(c.Subclasses, relatedInstances); + relatedInstances.AddRange(FindReferencedGraphInstances(c)); + relatedInstances.AddRange(FindGraphInstancesThatReference(c)); + return relatedInstances; + } + + private void AddSubclassesToGraphRelatedInstances(List subclasses, List relatedInstances) + { + foreach (var subclass in subclasses) + { + var couplingTypeAndSt = new Dictionary(); + couplingTypeAndSt.Add(CouplingType.Subclass, 1); + relatedInstances.Add(new GraphRelatedInstance(subclass.FullName, RelationType.Subclass, couplingTypeAndSt, GetCodeUrl(subclass.FullName))); + } + } + + private List FindReferencedGraphInstances(CaDETClass referencingClass) + { + var relatedInstances = new List(); + GetReferencedInstances(referencingClass).ForEach(cc => CoupledClassToGraphRelatedInstance(relatedInstances, cc)); + return relatedInstances; + } + + private void CoupledClassToGraphRelatedInstance(List relatedInstances, CoupledClassStrength cc) + { + var index = relatedInstances.FindIndex(i => i.CodeSnippetId.Equals(cc.CoupledClass.FullName)); + if (index != -1) + { + try + { + relatedInstances[index].CouplingTypeAndStrength.Add(cc.CouplingType, cc.CouplingStrength); + } + catch (Exception e) + { + relatedInstances[index].CouplingTypeAndStrength[cc.CouplingType] += cc.CouplingStrength; + } + } + else + { + var couplingTypeAndSt = new Dictionary(); + couplingTypeAndSt.Add(cc.CouplingType, cc.CouplingStrength); + relatedInstances.Add(new GraphRelatedInstance(cc.CoupledClass.FullName, RelationType.Referenced, couplingTypeAndSt, GetCodeUrl(cc.CoupledClass.FullName))); + } + } + + private List FindGraphInstancesThatReference(CaDETClass referencedClass) + { + if (_classCouplings.TryGetValue(referencedClass, out var instancesThatReference)) + { + var relatedInstances = new List(); + instancesThatReference.ForEach(cc => + CoupledClassToGraphRelatedInstance(relatedInstances, cc, RelationType.References)); + return relatedInstances; + } + return new List(); + } + + private void CoupledClassToGraphRelatedInstance(List relatedInstances, CoupledClassStrength cc, RelationType relationType) + { + var index = relatedInstances.FindIndex(i => i.CodeSnippetId.Equals(cc.CoupledClass.FullName)); + if (index != -1) + { + try + { + relatedInstances[index].CouplingTypeAndStrength.Add(cc.CouplingType, cc.CouplingStrength); + } + catch (Exception e) + { + relatedInstances[index].CouplingTypeAndStrength[cc.CouplingType] += cc.CouplingStrength; + } + } + else + { + var couplingTypeAndStrength = new Dictionary(); + couplingTypeAndStrength.Add(cc.CouplingType, cc.CouplingStrength); + relatedInstances.Add(new GraphRelatedInstance(cc.CoupledClass.FullName, RelationType.References, couplingTypeAndStrength, GetCodeUrl(cc.CoupledClass.FullName))); + } + } + private List BuildClasses() { var cadetClasses = _cadetProject.Classes; @@ -111,13 +208,33 @@ private int DetermineNumberOfInstances(int totalNumber) private List CaDETToDataSetProjectClasses(List cadetClasses) { - CreateCouplingMap(cadetClasses); return cadetClasses.Select(c => new Instance( c.FullName, GetCodeUrl(c.FullName), _projectAndCommitUrl, SnippetType.Class, - _cadetProject.GetMetricsForCodeSnippet(c.FullName), FindClassRelatedInstances(c) + _cadetProject.GetMetricsForCodeSnippet(c.FullName), FindClassRelatedInstances(c), FindAllIdentifiers(c) )).ToList(); } + private List FindAllIdentifiers(CaDETClass cadetClass) + { + var identifiers = new List(); + + identifiers.Add(new Identifier(cadetClass.Name, IdentifierType.Class)); + cadetClass.Fields.ForEach(field => identifiers.Add(new Identifier(field.Name, IdentifierType.Field))); + + var properties = cadetClass.Members.FindAll(member => member.Type.Equals(CaDETMemberType.Property)); + properties.ForEach(property => identifiers.Add(new Identifier(property.Name, IdentifierType.Property))); + + var methods = cadetClass.Members.FindAll(member => member.Type.Equals(CaDETMemberType.Method)); + + foreach (var method in methods) + { + identifiers.Add(new Identifier(method.Name, IdentifierType.Method)); + method.Params.ForEach(param => identifiers.Add(new Identifier(param.Name, IdentifierType.Parameter))); + method.Variables.ForEach(variable => identifiers.Add(new Identifier(variable.Name, IdentifierType.Variable))); + } + return identifiers; + } + private void CreateCouplingMap(List cadetClasses) { foreach (var instance in cadetClasses) @@ -177,11 +294,22 @@ private List FindClassRelatedInstances(CaDETClass c) couplingTypeAndSt.Add(CouplingType.Parent, 1); relatedInstances.Add(new RelatedInstance(c.Parent.FullName, GetCodeUrl(c.Parent.FullName), RelationType.Parent, couplingTypeAndSt)); } + AddSubclassesToRelatedInstances(c.Subclasses, relatedInstances); relatedInstances.AddRange(FindReferencedInstances(c)); relatedInstances.AddRange(FindInstancesThatReference(c)); return relatedInstances; } + private void AddSubclassesToRelatedInstances(List subclasses, List relatedInstances) + { + foreach (var subclass in subclasses) + { + var couplingTypeAndSt = new Dictionary(); + couplingTypeAndSt.Add(CouplingType.Subclass, 1); + relatedInstances.Add(new RelatedInstance(subclass.FullName, GetCodeUrl(subclass.FullName), RelationType.Subclass, couplingTypeAndSt)); + } + } + private List FindReferencedInstances(CaDETClass referencingClass) { var relatedInstances = new List(); @@ -253,7 +381,8 @@ private List BuildMembers() private List CaDETToDataSetFunction(List cadetMembers) { return cadetMembers.Select(m => new Instance( - m.Signature(), GetCodeUrl(m.Signature()), _projectAndCommitUrl, SnippetType.Function, _cadetProject.GetMetricsForCodeSnippet(m.Signature()), FindMethodRelatedInstances(m) + m.Signature(), GetCodeUrl(m.Signature()), _projectAndCommitUrl, SnippetType.Function, + _cadetProject.GetMetricsForCodeSnippet(m.Signature()), FindMethodRelatedInstances(m) )).ToList(); } @@ -267,10 +396,15 @@ private List FindMethodRelatedInstances(CaDETMember m) private IEnumerable FindReferencedInstances(CaDETMember referencingMember) { var relatedInstances = new List(); + + var couplingTypeAndSt = new Dictionary(); + couplingTypeAndSt.Add(CouplingType.BelongsTo, 1); + relatedInstances.Add(new RelatedInstance(referencingMember.Parent.FullName, GetCodeUrl(referencingMember.Parent.FullName), RelationType.BelongsTo, couplingTypeAndSt)); + var classParent = referencingMember.Parent.Parent; if (classParent != null) { - var couplingTypeAndSt = new Dictionary(); + couplingTypeAndSt = new Dictionary(); couplingTypeAndSt.Add(CouplingType.Parent, 1); relatedInstances.Add(new RelatedInstance(classParent.FullName, GetCodeUrl(classParent.FullName), RelationType.Parent, couplingTypeAndSt)); } diff --git a/DataSetExplorer/Core/DataSets/DataSetCreationService.cs b/DataSetExplorer/Core/DataSets/DataSetCreationService.cs index cda95783..e3e9cf69 100644 --- a/DataSetExplorer/Core/DataSets/DataSetCreationService.cs +++ b/DataSetExplorer/Core/DataSets/DataSetCreationService.cs @@ -42,7 +42,7 @@ public Result CreateEmptyDataSet(string dataSetName, List co return Result.Ok(dataSet); } - public Result AddProjectToDataSet(int dataSetId, string basePath, DataSetProject project, List smellFilters, ProjectBuildSettingsDTO projectBuildSettings) + public Result AddProjectToDataSet(int dataSetId, string basePath, DataSetProject project, List smellFilters, ProjectBuildSettingsDTO projectBuildSettings) { var initialDataSet = _dataSetRepository.GetDataSetWithProjectsAndCodeSmells(dataSetId); if (initialDataSet == default) return Result.Fail($"DataSet with id: {dataSetId} does not exist."); @@ -51,7 +51,7 @@ public Result AddProjectToDataSet(int dataSetId, string basePath, DataS initialDataSet.AddProject(project); _dataSetRepository.Update(initialDataSet); - return Result.Ok(initialDataSet); + return Result.Ok(project); } public Result CreateDataSetSpreadsheet(string dataSetName, string basePath, IDictionary projects, List codeSmells) @@ -80,16 +80,15 @@ public Result GetDataSet(int id) return Result.Ok(dataSet); } - public Result GetDataSetForExport(int id) + public Result> GetAllDataSets() { - var dataSet = _dataSetRepository.GetDataSetForExport(id); - if (dataSet == default) return Result.Fail($"DataSet with id: {id} does not exist."); - return Result.Ok(dataSet); + var dataSets = _dataSetRepository.GetAll(); + return Result.Ok(dataSets); } - public Result> GetAllDataSets() + public Result> GetDataSetsByCodeSmell(string codeSmellName) { - var dataSets = _dataSetRepository.GetAll(); + var dataSets = _dataSetRepository.GetAllByCodeSmell(codeSmellName); return Result.Ok(dataSets); } @@ -110,7 +109,7 @@ private DataSetProject CreateDataSetProject(string basePath, string projectName, private static DataSetProject CreateDataSetProjectFromRepository(string projectAndCommitUrl, string projectName, string projectPath, List codeSmells, List smellFilters, ProjectBuildSettingsDTO projectBuildSettings) { //TODO: Introduce Director as a separate class and insert through DI. - var builder = new CaDETToDataSetProjectBuilder(new InstanceFilter(smellFilters), projectAndCommitUrl, projectName, projectPath, projectBuildSettings.foldersToIgnore, codeSmells); + var builder = new CaDETToDataSetProjectBuilder(new InstanceFilter(smellFilters), projectAndCommitUrl, projectName, projectPath, projectBuildSettings.IgnoredFolders, codeSmells); if (projectBuildSettings.RandomizeClassSelection) builder = builder.RandomizeClassSelection(); if (projectBuildSettings.RandomizeMemberSelection) builder = builder.RandomizeMemberSelection(); if (projectBuildSettings.NumOfInstancesType.Equals("Percentage")) return builder.SetProjectExtractionPercentile(projectBuildSettings.NumOfInstances).Build(); @@ -119,18 +118,19 @@ private static DataSetProject CreateDataSetProjectFromRepository(string projectA private void ProcessInitialDataSetProject(string basePath, DataSetProject initialProject, List codeSmells, List smellFilters, ProjectBuildSettingsDTO projectBuildSettings) { - //try - //{ + try + { var project = CreateDataSetProject(basePath, initialProject.Name, initialProject.Url, codeSmells, smellFilters, projectBuildSettings); initialProject.CandidateInstances = project.CandidateInstances; + initialProject.GraphInstances = project.GraphInstances; initialProject.Processed(); _projectRepository.Update(initialProject); - //} - //catch (Exception e) when (e is LibGit2SharpException || e is NonUniqueFullNameException) - //{ + } + catch (Exception e) when (e is LibGit2SharpException || e is NonUniqueFullNameException) + { initialProject.Failed(); _projectRepository.Update(initialProject); - //} + } } private string ExportToExcel(string basePath, string projectName, NewSpreadSheetColumnModel columnModel, DataSet dataSet) @@ -144,7 +144,7 @@ private string ExportToExcel(string basePath, string projectName, NewSpreadSheet return fileName; } - public Result>> GetDataSetCodeSmells(int id) + public Result> GetDataSetCodeSmells(int id) { var codeSmells = _dataSetRepository.GetDataSetCodeSmells(id); if (codeSmells == default) return Result.Fail($"DataSet with id: {id} does not exist."); diff --git a/DataSetExplorer/Core/DataSets/FullDataSetFactory.cs b/DataSetExplorer/Core/DataSets/FullDataSetFactory.cs index e363e9b7..4caa02a8 100644 --- a/DataSetExplorer/Core/DataSets/FullDataSetFactory.cs +++ b/DataSetExplorer/Core/DataSets/FullDataSetFactory.cs @@ -12,10 +12,15 @@ namespace DataSetExplorer.Core.DataSets public class FullDataSetFactory { private readonly IInstanceRepository _instanceRepository; + private readonly IAnnotationRepository _annotationRepository; + private readonly IProjectService _projectService; - public FullDataSetFactory(IInstanceRepository instanceRepository) + public FullDataSetFactory(IInstanceRepository instanceRepository, IAnnotationRepository annotationRepository, + IProjectService projectService) { _instanceRepository = instanceRepository; + _annotationRepository = annotationRepository; + _projectService = projectService; } public FullDataSetFactory() { } @@ -28,7 +33,7 @@ public List GetAnnotatedInstancesGroupedBySmells(IDicti CodeModelFactory factory = new CodeModelFactory(); CaDETProject project = factory.CreateProjectWithCodeFileLinks(projectSourceLocation); - var importer = new ExcelImporter(projects[projectSourceLocation]); + var importer = new ExcelImporter(projects[projectSourceLocation], _annotationRepository); var annotatedCandidates = importer.Import(projectSourceLocation).CandidateInstances.ToList(); LoadAnnotators(annotators, annotatedCandidates); @@ -38,6 +43,40 @@ public List GetAnnotatedInstancesGroupedBySmells(IDicti return allAnnotatedInstances; } + public List GetAnnotatedInstancesGroupedBySmells(int datasetId, List annotators, string[] annotationsFilesPaths) + { + var allAnnotatedInstances = new List(); + var projects = _projectService.GetAllByDatasetId(datasetId).Value; + + foreach (var project in projects) + { + AddInstancesToCandidates(allAnnotatedInstances, project.CandidateInstances.ToList()); + var importer = new ExcelImporter(_annotationRepository); + foreach (var annotationsFilePath in annotationsFilesPaths) + { + if (project.Url.Equals(importer.GetProjectUrl(annotationsFilePath))) + { + var importedProject = importer.ImportAnnotationsFile(annotationsFilePath); + if (importedProject.Url.Equals(project.Url)) JoinAnnotations(project, importedProject); + } + } + } + return allAnnotatedInstances; + } + + private void JoinAnnotations(DataSetProject project, DataSetProject importedProject) + { + foreach(var candidate in project.CandidateInstances) + { + var importedCandidate = importedProject.CandidateInstances.First(c => c.CodeSmell.Name.Equals(candidate.CodeSmell.Name)); + foreach (var importedInstance in importedCandidate.Instances) + { + var instance = candidate.Instances.Find(x => x.Link.Equals(importedInstance.Link)); + importedInstance.Annotations.ToList().ForEach(a => instance.AddAnnotation(a)); + } + } + } + private static void AddInstancesToCandidates(List allAnnotatedInstances, List annotatedInstances) { foreach (var annotated in annotatedInstances) @@ -73,7 +112,8 @@ public List GetAnnotatedInstancesGroupedBySmells(int pr var instancesBySmell = instances.GroupBy(i => i.Annotations.ToList()[0].InstanceSmell.Name); foreach (var group in instancesBySmell) { - candidateInstances.Add(new SmellCandidateInstances(new CodeSmell(group.Key), group.ToList())); + if (_annotationRepository != null) candidateInstances.Add(new SmellCandidateInstances(_annotationRepository.GetCodeSmell(group.Key), group.ToList())); + else candidateInstances.Add(new SmellCandidateInstances(new CodeSmell(group.Key), group.ToList())); } return candidateInstances; } diff --git a/DataSetExplorer/Core/DataSets/IDataSetCreationService.cs b/DataSetExplorer/Core/DataSets/IDataSetCreationService.cs index cbb80ec0..a4c391fd 100644 --- a/DataSetExplorer/Core/DataSets/IDataSetCreationService.cs +++ b/DataSetExplorer/Core/DataSets/IDataSetCreationService.cs @@ -14,12 +14,12 @@ public interface IDataSetCreationService Result CreateDataSetSpreadsheet(string dataSetName, string basePath, IDictionary projects, List codeSmells); Result CreateDataSetSpreadsheet(string dataSetName, string basePath, IDictionary projects, List codeSmells, NewSpreadSheetColumnModel columnModel); Result CreateEmptyDataSet(string dataSetName, List codeSmells); - Result AddProjectToDataSet(int dataSetId, string basePath, DataSetProject project, List smellFilters, ProjectBuildSettingsDTO projectBuildSettings); + Result AddProjectToDataSet(int dataSetId, string basePath, DataSetProject project, List smellFilters, ProjectBuildSettingsDTO projectBuildSettings); Result GetDataSet(int id); - Result GetDataSetForExport(int id); Result> GetAllDataSets(); + Result> GetDataSetsByCodeSmell(string codeSmellName); Result GetDataSetProject(int id); - Result>> GetDataSetCodeSmells(int id); + Result> GetDataSetCodeSmells(int id); Result DeleteDataSet(int id); Result UpdateDataSet(DataSet dataset); Result DeleteDataSetProject(int id); diff --git a/DataSetExplorer/Core/DataSets/IInstanceService.cs b/DataSetExplorer/Core/DataSets/IInstanceService.cs index ba466f08..2fdc2b91 100644 --- a/DataSetExplorer/Core/DataSets/IInstanceService.cs +++ b/DataSetExplorer/Core/DataSets/IInstanceService.cs @@ -1,3 +1,4 @@ +using DataSetExplorer.Core.AnnotationSchema.Model; using DataSetExplorer.Core.DataSets.Model; using DataSetExplorer.UI.Controllers.Dataset.DTOs; using FluentResults; @@ -7,8 +8,14 @@ namespace DataSetExplorer.Core.DataSets { public interface IInstanceService { + Result>> GetInstancesWithIdentifiersByDatasetId(int datasetId); + Result>> GetInstancesWithIdentifiersByProjectId(int projectId); + Result>> GetInstancesByDatasetId(int datasetId); + Result>> GetInstancesByProjectId(int projectId); Result GetInstanceWithRelatedInstances(int id); Result GetInstanceWithAnnotations(int id); + Result> GetInstancesForSmell(string codeSmellName); + Result> DeleteCandidateInstancesForSmell(CodeSmellDefinition codeSmellDefinition); public string GetFileFromGit(string url); } } \ No newline at end of file diff --git a/DataSetExplorer/Core/DataSets/IProjectService.cs b/DataSetExplorer/Core/DataSets/IProjectService.cs new file mode 100644 index 00000000..cdb3db86 --- /dev/null +++ b/DataSetExplorer/Core/DataSets/IProjectService.cs @@ -0,0 +1,12 @@ +using DataSetExplorer.Core.DataSets.Model; +using FluentResults; +using System.Collections.Generic; + +namespace DataSetExplorer.Core.DataSets +{ + public interface IProjectService + { + Result GetProjectWithGraphInstances(int id); + Result> GetAllByDatasetId(int datasetId); + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/DataSets/InstanceFilter.cs b/DataSetExplorer/Core/DataSets/InstanceFilter.cs index 92ad41ef..ac61fe06 100644 --- a/DataSetExplorer/Core/DataSets/InstanceFilter.cs +++ b/DataSetExplorer/Core/DataSets/InstanceFilter.cs @@ -10,7 +10,7 @@ namespace DataSetExplorer.Core.DataSets internal class InstanceFilter { public List SmellFilters { get; } - + public InstanceFilter(List smellFilters) { SmellFilters = smellFilters; @@ -33,7 +33,7 @@ public List FilterInstances(CodeSmell codeSmell, List instan private static bool ValidSnippetTypeForSmell(Instance instance, CodeSmell codeSmell) { - return codeSmell.RelevantSnippetTypes()[0].ToString().Equals(instance.Type.ToString()); + return codeSmell.SnippetType.Equals(instance.Type); } private static bool InstancePassesMetricThresholds(Instance instance, List metricsThreholds) diff --git a/DataSetExplorer/Core/DataSets/InstanceService.cs b/DataSetExplorer/Core/DataSets/InstanceService.cs index 762a8449..75dcac67 100644 --- a/DataSetExplorer/Core/DataSets/InstanceService.cs +++ b/DataSetExplorer/Core/DataSets/InstanceService.cs @@ -1,26 +1,53 @@ -using AutoMapper; +using DataSetExplorer.Core.AnnotationSchema.Model; using DataSetExplorer.Core.DataSets.Model; using DataSetExplorer.Core.DataSets.Repository; using DataSetExplorer.UI.Controllers.Dataset.DTOs; using FluentResults; -using System; using System.Collections.Generic; -using System.IO; -using System.Net; using System.Net.Http; -using System.Text; namespace DataSetExplorer.Core.DataSets { public class InstanceService : IInstanceService { private readonly IInstanceRepository _instanceRepository; - private readonly IProjectRepository _projectRepository; + private readonly IDataSetCreationService _dataSetCreationService; + private readonly IAnnotationRepository _annotationRepository; - public InstanceService(IMapper mapper, IInstanceRepository instanceRepository, IProjectRepository projectRepository) + public InstanceService(IInstanceRepository instanceRepository, IDataSetCreationService dataSetCreationService, + IAnnotationRepository annotationRepository) { _instanceRepository = instanceRepository; - _projectRepository = projectRepository; + _dataSetCreationService = dataSetCreationService; + _annotationRepository = annotationRepository; + } + + public Result>> GetInstancesWithIdentifiersByDatasetId(int datasetId) + { + var instances = _instanceRepository.GetInstancesWithIdentifiersByDatasetId(datasetId); + if (instances == default) return Result.Fail($"Dataset with id: {datasetId} does not exist."); + return Result.Ok(instances); + } + + public Result>> GetInstancesWithIdentifiersByProjectId(int projectId) + { + var instances = _instanceRepository.GetInstancesWithIdentifiersByProjectId(projectId); + if (instances == default) return Result.Fail($"Project with id: {projectId} does not exist."); + return Result.Ok(instances); + } + + public Result>> GetInstancesByDatasetId(int datasetId) + { + var instances = _instanceRepository.GetInstancesByDatasetId(datasetId); + if (instances == default) return Result.Fail($"Dataset with id: {datasetId} does not exist."); + return Result.Ok(instances); + } + + public Result>> GetInstancesByProjectId(int projectId) + { + var instances = _instanceRepository.GetInstancesByProjectId(projectId); + if (instances == default) return Result.Fail($"Project with id: {projectId} does not exist."); + return Result.Ok(instances); } public Result GetInstanceWithRelatedInstances(int id) @@ -37,6 +64,32 @@ public Result GetInstanceWithAnnotations(int id) return Result.Ok(instance); } + public Result> GetInstancesForSmell(string codeSmellName) + { + List instances = new List(); + var datasets = _dataSetCreationService.GetDataSetsByCodeSmell(codeSmellName).Value; + foreach (var dataset in datasets) + { + foreach (var project in dataset.Projects) + { + foreach (var candidate in project.CandidateInstances) + { + if (!candidate.CodeSmell.Name.Equals(codeSmellName)) continue; + instances.AddRange(candidate.Instances); + } + } + } + return Result.Ok(instances); + } + + public Result> DeleteCandidateInstancesForSmell(CodeSmellDefinition codeSmellDefinition) + { + var codeSmells = _annotationRepository.GetCodeSmellsByDefinition(codeSmellDefinition); + var deletedCandidates = _instanceRepository.DeleteCandidateInstancesBySmell(codeSmells); + _annotationRepository.DeleteCodeSmells(codeSmells); + return Result.Ok(deletedCandidates); + } + public string GetFileFromGit(string url) { string rawUrl = "https://raw.githubusercontent.com/" + url.Split("https://github.com/")[1]; diff --git a/DataSetExplorer/Core/DataSets/Model/CouplingType.cs b/DataSetExplorer/Core/DataSets/Model/CouplingType.cs index 7ad49859..ef2ff360 100644 --- a/DataSetExplorer/Core/DataSets/Model/CouplingType.cs +++ b/DataSetExplorer/Core/DataSets/Model/CouplingType.cs @@ -9,6 +9,8 @@ public enum CouplingType Variable, Parent, AccessedAccessor, - AccessedField + AccessedField, + BelongsTo, + Subclass } } diff --git a/DataSetExplorer/Core/DataSets/Model/DataSetProject.cs b/DataSetExplorer/Core/DataSets/Model/DataSetProject.cs index 65d7e846..75afd372 100644 --- a/DataSetExplorer/Core/DataSets/Model/DataSetProject.cs +++ b/DataSetExplorer/Core/DataSets/Model/DataSetProject.cs @@ -7,8 +7,9 @@ public class DataSetProject { public int Id { get; private set; } public string Name { get; private set; } - public string Url { get; private set; } + public string Url { get; set; } public HashSet CandidateInstances { get; internal set; } + public List GraphInstances { get; internal set; } public ProjectState State { get; private set; } internal DataSetProject(string name, string url) @@ -21,6 +22,11 @@ internal DataSetProject(string name, string url) public DataSetProject(string name) : this(name, null) { } + public DataSetProject() + { + CandidateInstances = new HashSet(); + } + internal void AddCandidateInstance(SmellCandidateInstances newCandidate) { if (CandidateInstances.TryGetValue(newCandidate, out var existingCandidate)) diff --git a/DataSetExplorer/Core/DataSets/Model/Instance.cs b/DataSetExplorer/Core/DataSets/Model/Instance.cs index 9d8b998a..ddeee7fb 100644 --- a/DataSetExplorer/Core/DataSets/Model/Instance.cs +++ b/DataSetExplorer/Core/DataSets/Model/Instance.cs @@ -3,6 +3,7 @@ using System.Linq; using CodeModel.CaDETModel.CodeItems; using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.CleanCodeAnalysis.Model; namespace DataSetExplorer.Core.DataSets.Model { @@ -16,20 +17,37 @@ public class Instance public ISet Annotations { get; private set; } public Dictionary MetricFeatures { get; internal set; } // TODO: Expand and replace with the IFeature if a new feature type is introduced public List RelatedInstances { get; private set; } + public List Identifiers { get; private set; } - internal Instance(string codeSnippetId, string link, string projectLink, SnippetType type, Dictionary metricFeatures, List relatedInstances) + internal Instance(string codeSnippetId, string link, string projectLink, SnippetType type, Dictionary metricFeatures, List relatedInstances, List identifiers) { CodeSnippetId = codeSnippetId; Link = link; ProjectLink = projectLink; Type = type; RelatedInstances = relatedInstances; + Identifiers = identifiers; Annotations = new HashSet(); SetMetricFeatures(metricFeatures); Validate(); } + internal Instance(string codeSnippetId, string link, string projectLink, SnippetType type, Dictionary metricFeatures, List relatedInstances) + { + CodeSnippetId = codeSnippetId; + Link = link; + ProjectLink = projectLink; + Type = type; + RelatedInstances = relatedInstances; + + Annotations = new HashSet(); + SetMetricFeatures(metricFeatures); + Validate(); + } + private Instance() { } @@ -92,14 +110,14 @@ internal string GetSortedAnnotatorIds() return string.Join(",", list); } - public int GetFinalAnnotation() + public string GetFinalAnnotation() { var majorityVote = GetMajorityAnnotation(); - if (majorityVote != null) return (int)majorityVote; + if (majorityVote != null) return majorityVote; return GetAnnotationFromMostExperiencedAnnotator(); } - private int? GetMajorityAnnotation() + private string GetMajorityAnnotation() { var annotationsGroupedBySeverity = Annotations.GroupBy(a => a.Severity); if (HasMajoritySeverityVote(annotationsGroupedBySeverity)) @@ -112,15 +130,16 @@ public int GetFinalAnnotation() } private bool HasMajoritySeverityVote( - IEnumerable> annotationsGroupedBySeverity) + IEnumerable> annotationsGroupedBySeverity) { var severityCounts = annotationsGroupedBySeverity.Select(group => group.Count()); if (severityCounts.Count() == 1) return true; return severityCounts.Any(count => count != severityCounts.First()); } - private int GetAnnotationFromMostExperiencedAnnotator() + private string GetAnnotationFromMostExperiencedAnnotator() { + if (Annotations.Count == 0) return "0"; return Annotations.OrderBy(a => a.Annotator.Ranking).First().Severity; } diff --git a/DataSetExplorer/Core/DataSets/Model/RelationType.cs b/DataSetExplorer/Core/DataSets/Model/RelationType.cs index 2dfdefc4..4a5a1e43 100644 --- a/DataSetExplorer/Core/DataSets/Model/RelationType.cs +++ b/DataSetExplorer/Core/DataSets/Model/RelationType.cs @@ -4,6 +4,8 @@ public enum RelationType { Referenced, References, - Parent + Parent, + BelongsTo, + Subclass } } \ No newline at end of file diff --git a/DataSetExplorer/Core/DataSets/ProjectService.cs b/DataSetExplorer/Core/DataSets/ProjectService.cs new file mode 100644 index 00000000..704a8c54 --- /dev/null +++ b/DataSetExplorer/Core/DataSets/ProjectService.cs @@ -0,0 +1,29 @@ +using DataSetExplorer.Core.DataSets.Model; +using DataSetExplorer.Core.DataSets.Repository; +using FluentResults; +using System.Collections.Generic; + +namespace DataSetExplorer.Core.DataSets +{ + public class ProjectService : IProjectService + { + private readonly IProjectRepository _projectRepository; + + public ProjectService(IProjectRepository projectRepository) + { + _projectRepository = projectRepository; + } + + public Result GetProjectWithGraphInstances(int id) + { + var project = _projectRepository.GetProjectWithGraphInstances(id); + if (project == default) return Result.Fail($"Project with id: {id} does not exist."); + return Result.Ok(project); + } + + public Result> GetAllByDatasetId(int datasetId) + { + return Result.Ok(_projectRepository.GetAllByDatasetId(datasetId)); + } + } +} \ No newline at end of file diff --git a/DataSetExplorer/Core/DataSets/Repository/AnnotationDatabaseRepository.cs b/DataSetExplorer/Core/DataSets/Repository/AnnotationDatabaseRepository.cs index 28ab510e..05b516ed 100644 --- a/DataSetExplorer/Core/DataSets/Repository/AnnotationDatabaseRepository.cs +++ b/DataSetExplorer/Core/DataSets/Repository/AnnotationDatabaseRepository.cs @@ -1,5 +1,7 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.AnnotationSchema.Model; using DataSetExplorer.Infrastructure.Database; using Microsoft.EntityFrameworkCore; @@ -16,7 +18,7 @@ public AnnotationDatabaseRepository(DataSetExplorerContext dbContext) public Annotation Get(int id) { - return _dbContext.DataSetAnnotations + return _dbContext.Annotations .Include(a => a.Annotator) .Include(a => a.ApplicableHeuristics) .FirstOrDefault(a => a.Id == id); @@ -36,5 +38,43 @@ public void Update(Annotation annotation) _dbContext.Update(annotation); _dbContext.SaveChanges(); } + + public Annotation Delete(int id) + { + var deleted = _dbContext.Annotations.Remove(_dbContext.Annotations.Find(id)).Entity; + _dbContext.SaveChanges(); + return deleted; + } + + public SmellHeuristic DeleteHeuristic(int id) + { + var deletedHeuristic = _dbContext.SmellHeuristics.Remove(_dbContext.SmellHeuristics.Find(id)).Entity; + _dbContext.SaveChanges(); + return deletedHeuristic; + } + + public List GetCodeSmellsByDefinition(CodeSmellDefinition codeSmellDefinition) + { + return _dbContext.CodeSmells.Where(s => s.Name.Equals(codeSmellDefinition.Name)).ToList(); + } + + public void DeleteCodeSmells(List codeSmells) + { + _dbContext.CodeSmells.RemoveRange(codeSmells); + _dbContext.SaveChanges(); + } + + public void UpdateAppliedHeuristic(SmellHeuristic heuristic) + { + _dbContext.Update(heuristic); + _dbContext.SaveChanges(); + } + + public CodeSmell UpdateCodeSmell(CodeSmell codeSmell) + { + var updated = _dbContext.Update(codeSmell).Entity; + _dbContext.SaveChanges(); + return updated; + } } } diff --git a/DataSetExplorer/Core/DataSets/Repository/DataSetDatabaseRepository.cs b/DataSetExplorer/Core/DataSets/Repository/DataSetDatabaseRepository.cs index e6abccc1..71cabd49 100644 --- a/DataSetExplorer/Core/DataSets/Repository/DataSetDatabaseRepository.cs +++ b/DataSetExplorer/Core/DataSets/Repository/DataSetDatabaseRepository.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using AutoMapper.Internal; +using DataSetExplorer.Core.Annotations.Model; using DataSetExplorer.Core.DataSets.Model; using DataSetExplorer.Infrastructure.Database; using DataSetExplorer.UI.Controllers.Dataset.DTOs.Summary; @@ -71,7 +72,7 @@ public DataSet GetDataSetWithProjectsAndCodeSmells(int id) { return _dbContext.DataSets .Include(d => d.SupportedCodeSmells) - .Include(s => s.Projects) + .Include(d => d.Projects) .FirstOrDefault(d => d.Id == id); } @@ -82,6 +83,29 @@ public IEnumerable GetAll() return result; } + public IEnumerable GetAllByCodeSmell(string codeSmellName) + { + var datasets = _dbContext.DataSets + .Include(d => d.SupportedCodeSmells) + .Include(d => d.Projects) + .ThenInclude(p => p.CandidateInstances) + .ThenInclude(c => c.Instances) + .ThenInclude(i => i.Annotations) + .ThenInclude(a => a.Annotator) + .Include(d => d.Projects) + .ThenInclude(p => p.CandidateInstances) + .ThenInclude(c => c.Instances) + .ThenInclude(i => i.Annotations) + .ThenInclude(a => a.ApplicableHeuristics); + + var result = new List(); + foreach(var dataset in datasets) + { + if (dataset.SupportedCodeSmells.Exists(s => s.Name.Equals(codeSmellName))) result.Add(dataset); + } + return result; + } + private DatasetSummaryDTO GetDatasetSummary(int id) { var dataset = _dbContext.DataSets.FirstOrDefault(d => d.Id == id); @@ -97,17 +121,11 @@ public DataSet Update(DataSet dataSet) return updatedDataset; } - public Dictionary> GetDataSetCodeSmells(int id) + public List GetDataSetCodeSmells(int id) { - var result = new Dictionary>(); var dataSet = _dbContext.DataSets.Include(d => d.SupportedCodeSmells).FirstOrDefault(s => s.Id == id); if (dataSet == null) return null; - - foreach (var smell in dataSet.SupportedCodeSmells) - { - result.Add(smell.Name, smell.RelevantSnippetTypes().Select(t => t.ToString()).ToList()); - } - return result; + return dataSet.SupportedCodeSmells; } public DataSet Delete(int id) diff --git a/DataSetExplorer/Core/DataSets/Repository/IAnnotationRepository.cs b/DataSetExplorer/Core/DataSets/Repository/IAnnotationRepository.cs index a4b3bdc0..a454bbb7 100644 --- a/DataSetExplorer/Core/DataSets/Repository/IAnnotationRepository.cs +++ b/DataSetExplorer/Core/DataSets/Repository/IAnnotationRepository.cs @@ -1,4 +1,6 @@ using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.AnnotationSchema.Model; +using System.Collections.Generic; namespace DataSetExplorer.Core.DataSets.Repository { @@ -8,5 +10,11 @@ public interface IAnnotationRepository Annotator GetAnnotator(int id); CodeSmell GetCodeSmell(string name); void Update(Annotation annotation); + Annotation Delete(int id); + SmellHeuristic DeleteHeuristic(int id); + List GetCodeSmellsByDefinition(CodeSmellDefinition codeSmellDefinition); + void DeleteCodeSmells(List codeSmells); + void UpdateAppliedHeuristic(SmellHeuristic heuristic); + CodeSmell UpdateCodeSmell(CodeSmell codeSmell); } } diff --git a/DataSetExplorer/Core/DataSets/Repository/IDataSetRepository.cs b/DataSetExplorer/Core/DataSets/Repository/IDataSetRepository.cs index c9f09b32..eb174be2 100644 --- a/DataSetExplorer/Core/DataSets/Repository/IDataSetRepository.cs +++ b/DataSetExplorer/Core/DataSets/Repository/IDataSetRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using DataSetExplorer.Core.Annotations.Model; using DataSetExplorer.Core.DataSets.Model; using DataSetExplorer.UI.Controllers.Dataset.DTOs.Summary; @@ -11,8 +12,9 @@ public interface IDataSetRepository DataSet GetDataSetForExport(int id); DataSet GetDataSetWithProjectsAndCodeSmells(int id); IEnumerable GetAll(); + IEnumerable GetAllByCodeSmell(string codeSmellName); DataSet Update(DataSet dataSet); - Dictionary> GetDataSetCodeSmells(int id); + List GetDataSetCodeSmells(int id); DataSet Delete(int id); } } diff --git a/DataSetExplorer/Core/DataSets/Repository/IInstanceRepository.cs b/DataSetExplorer/Core/DataSets/Repository/IInstanceRepository.cs index c0da305e..a26d5626 100644 --- a/DataSetExplorer/Core/DataSets/Repository/IInstanceRepository.cs +++ b/DataSetExplorer/Core/DataSets/Repository/IInstanceRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using DataSetExplorer.Core.Annotations.Model; using DataSetExplorer.Core.DataSets.Model; using DataSetExplorer.UI.Controllers.Dataset.DTOs; @@ -7,10 +8,15 @@ namespace DataSetExplorer.Core.DataSets.Repository public interface IInstanceRepository { Instance Get(int id); + Dictionary> GetInstancesWithIdentifiersByDatasetId(int datasetId); + Dictionary> GetInstancesWithIdentifiersByProjectId(int projectId); + Dictionary> GetInstancesByDatasetId(int datasetId); + Dictionary> GetInstancesByProjectId(int projectId); InstanceDTO GetInstanceWithRelatedInstances(int id); IEnumerable GetInstancesAnnotatedByAnnotator(int projectId, int? annotatorId); IEnumerable GetAnnotatedInstances(int projectId); void Update(Instance instance); Instance GetInstanceWithAnnotations(int id); + List DeleteCandidateInstancesBySmell(List codeSmells); } } diff --git a/DataSetExplorer/Core/DataSets/Repository/IProjectRepository.cs b/DataSetExplorer/Core/DataSets/Repository/IProjectRepository.cs index 586e51a1..439a9521 100644 --- a/DataSetExplorer/Core/DataSets/Repository/IProjectRepository.cs +++ b/DataSetExplorer/Core/DataSets/Repository/IProjectRepository.cs @@ -9,5 +9,7 @@ public interface IProjectRepository IEnumerable GetAll(IEnumerable projectIds); DataSetProject Update(DataSetProject dataSetProject); DataSetProject Delete(int id); + DataSetProject GetProjectWithGraphInstances(int id); + List GetAllByDatasetId(int datasetId); } } diff --git a/DataSetExplorer/Core/DataSets/Repository/InstanceDatabaseRepository.cs b/DataSetExplorer/Core/DataSets/Repository/InstanceDatabaseRepository.cs index 15e33d6b..8ddc7b9c 100644 --- a/DataSetExplorer/Core/DataSets/Repository/InstanceDatabaseRepository.cs +++ b/DataSetExplorer/Core/DataSets/Repository/InstanceDatabaseRepository.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using DataSetExplorer.Core.Annotations.Model; using DataSetExplorer.Core.DataSets.Model; using DataSetExplorer.Infrastructure.Database; using DataSetExplorer.UI.Controllers.Dataset.DTOs; @@ -18,19 +19,76 @@ public InstanceDatabaseRepository(DataSetExplorerContext dbContext) public Instance Get(int id) { - return _dbContext.DataSetInstances.Include(i => i.Annotations).FirstOrDefault(i => i.Id == id); + return _dbContext.Instances.Include(i => i.Annotations).FirstOrDefault(i => i.Id == id); + } + + public Dictionary> GetInstancesWithIdentifiersByDatasetId(int datasetId) + { + var instances = new Dictionary>(); + var dataset = _dbContext.DataSets + .Include(d => d.Projects) + .ThenInclude(p => p.CandidateInstances) + .ThenInclude(c => c.Instances) + .ThenInclude(i => i.Identifiers) + .FirstOrDefault(d => d.Id == datasetId); + if (dataset == default) return null; + + foreach(var project in dataset.Projects) + instances.Add(project.Name, project.CandidateInstances.SelectMany(c => c.Instances).ToList()); + + return instances; + } + + public Dictionary> GetInstancesWithIdentifiersByProjectId(int projectId) + { + var instances = new Dictionary>(); + var project = _dbContext.DataSetProjects + .Include(p => p.CandidateInstances) + .ThenInclude(c => c.Instances) + .ThenInclude(i => i.Identifiers) + .FirstOrDefault(p => p.Id == projectId); + if (project == default) return null; + + instances.Add(project.Name, project.CandidateInstances.SelectMany(c => c.Instances).ToList()); + return instances; + } + + public Dictionary> GetInstancesByDatasetId(int datasetId) + { + var instances = new Dictionary>(); + var dataset = _dbContext.DataSets + .Include(d => d.Projects).ThenInclude(p => p.CandidateInstances).ThenInclude(c => c.Instances) + .FirstOrDefault(d => d.Id == datasetId); + if (dataset == default) return null; + + foreach (var project in dataset.Projects) + instances.Add(project.Name, project.CandidateInstances.SelectMany(c => c.Instances).ToList()); + + return instances; + } + + public Dictionary> GetInstancesByProjectId(int projectId) + { + var instances = new Dictionary>(); + var project = _dbContext.DataSetProjects + .Include(p => p.CandidateInstances).ThenInclude(c => c.Instances) + .FirstOrDefault(p => p.Id == projectId); + if (project == default) return null; + + instances.Add(project.Name, project.CandidateInstances.SelectMany(c => c.Instances).ToList()); + return instances; } public InstanceDTO GetInstanceWithRelatedInstances(int id) { - return new InstanceDTO(_dbContext.DataSetInstances + return new InstanceDTO(_dbContext.Instances .Include(i => i.RelatedInstances) .FirstOrDefault(i => i.Id == id)); } public Instance GetInstanceWithAnnotations(int id) { - return _dbContext.DataSetInstances + return _dbContext.Instances .Include(i => i.Annotations).ThenInclude(a => a.Annotator) .Include(i => i.Annotations).ThenInclude(a => a.ApplicableHeuristics) .FirstOrDefault(i => i.Id == id); @@ -73,5 +131,17 @@ public void Update(Instance instance) _dbContext.Update(instance); _dbContext.SaveChanges(); } + + public List DeleteCandidateInstancesBySmell(List codeSmells) + { + var candidatesToDelete = new List(); + foreach (var codeSmell in codeSmells) + { + candidatesToDelete.AddRange(_dbContext.SmellCandidateInstances.Where(c => c.CodeSmell.Id == codeSmell.Id)); + } + _dbContext.SmellCandidateInstances.RemoveRange(candidatesToDelete); + _dbContext.SaveChanges(); + return candidatesToDelete; + } } } diff --git a/DataSetExplorer/Core/DataSets/Repository/ProjectDatabaseRepository.cs b/DataSetExplorer/Core/DataSets/Repository/ProjectDatabaseRepository.cs index 45dca1b8..9b569faa 100644 --- a/DataSetExplorer/Core/DataSets/Repository/ProjectDatabaseRepository.cs +++ b/DataSetExplorer/Core/DataSets/Repository/ProjectDatabaseRepository.cs @@ -52,5 +52,23 @@ public DataSetProject Delete(int id) _dbContext.SaveChanges(); return deletedProject; } + + public DataSetProject GetProjectWithGraphInstances(int id) + { + return _dbContext.DataSetProjects + .Include(p => p.GraphInstances).ThenInclude(i => i.RelatedInstances) + .FirstOrDefault(p => p.Id == id); + } + + public List GetAllByDatasetId(int datasetId) + { + return _dbContext.DataSets + .Include(d => d.Projects).ThenInclude(p => p.CandidateInstances).ThenInclude(c => c.Instances) + .Include(d => d.Projects).ThenInclude(p => p.CandidateInstances).ThenInclude(c => c.Instances).ThenInclude(i => i.Annotations).ThenInclude(a => a.Annotator) + .Include(d => d.Projects).ThenInclude(p => p.CandidateInstances).ThenInclude(c => c.Instances).ThenInclude(i => i.Annotations).ThenInclude(a => a.ApplicableHeuristics) + .Include(d => d.Projects).ThenInclude(p => p.CandidateInstances).ThenInclude(c => c.Instances).ThenInclude(i => i.Annotations).ThenInclude(a => a.InstanceSmell) + .Include(d => d.Projects).ThenInclude(p => p.CandidateInstances).ThenInclude(c => c.CodeSmell) + .FirstOrDefault(d => d.Id == datasetId).Projects.ToList(); + } } } diff --git a/DataSetExplorer/Infrastructure/Database/DataSetExplorerContext.cs b/DataSetExplorer/Infrastructure/Database/DataSetExplorerContext.cs index 9a0a9481..8809dce2 100644 --- a/DataSetExplorer/Infrastructure/Database/DataSetExplorerContext.cs +++ b/DataSetExplorer/Infrastructure/Database/DataSetExplorerContext.cs @@ -5,6 +5,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Newtonsoft.Json; +using DataSetExplorer.Core.AnnotationSchema.Model; +using DataSetExplorer.Core.CleanCodeAnalysis.Model; namespace DataSetExplorer.Infrastructure.Database { @@ -13,10 +15,17 @@ public class DataSetExplorerContext : DbContext public DbSet Annotators { get; set; } public DbSet CodeSmells { get; set; } public DbSet SmellHeuristics { get; set; } - public DbSet DataSetAnnotations { get; set; } - public DbSet DataSetInstances { get; set; } + public DbSet Annotations { get; set; } + public DbSet Instances { get; set; } public DbSet DataSets { get; set; } public DbSet DataSetProjects { get; set; } + public DbSet CodeSmellDefinitions { get; set; } + public DbSet HeuristicDefinitions { get; set; } + public DbSet SeverityDefinitions { get; set; } + public DbSet SmellCandidateInstances { get; set; } + public DbSet GraphInstances { get; set; } + public DbSet GraphRelatedInstances { get; set; } + public DbSet Identifiers { get; set; } public DataSetExplorerContext(DbContextOptions options) : base(options) { } @@ -35,6 +44,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) m => JsonConvert.SerializeObject(m), m => JsonConvert.DeserializeObject>(m)); + modelBuilder.Entity() + .Property(i => i.CouplingTypeAndStrength) + .HasConversion( + m => JsonConvert.SerializeObject(m), + m => JsonConvert.DeserializeObject>(m)); + modelBuilder.Entity().HasOne().WithMany(d => d.SupportedCodeSmells) .OnDelete(DeleteBehavior.Cascade); @@ -50,6 +65,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().HasOne().WithMany(p => p.CandidateInstances) .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne().WithMany(p => p.GraphInstances) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne().WithMany(c => c.Instances) .OnDelete(DeleteBehavior.Cascade); @@ -62,10 +80,49 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity().HasOne().WithMany(i => i.RelatedInstances) .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne().WithMany(i => i.Identifiers) + .OnDelete(DeleteBehavior.Cascade); + + modelBuilder.Entity().HasOne().WithMany(i => i.RelatedInstances) + .OnDelete(DeleteBehavior.Cascade); + modelBuilder .Entity() .Property(i => i.RelationType) .HasConversion(new EnumToStringConverter()); + + modelBuilder + .Entity() + .Property(i => i.RelationType) + .HasConversion(new EnumToStringConverter()); + + modelBuilder.Entity().HasOne().WithMany(d => d.Heuristics) + .OnDelete(DeleteBehavior.Cascade); + + modelBuilder.Entity().HasOne().WithMany(d => d.Severities) + .OnDelete(DeleteBehavior.Cascade); + + modelBuilder + .Entity() + .Property(c => c.SnippetType) + .HasConversion(); + + modelBuilder + .Entity() + .Property(i => i.Type) + .HasConversion(); + + modelBuilder + .Entity() + .Property(c => c.SnippetType) + .HasConversion(); + + modelBuilder.Entity(codeSmell => { + codeSmell.HasIndex(c => c.Name).IsUnique(); + }); + modelBuilder.Entity(annotator => { + annotator.HasIndex(a => a.Email).IsUnique(); + }); } } } diff --git a/DataSetExplorer/Program.cs b/DataSetExplorer/Program.cs index fbceba2a..5b1cc334 100644 --- a/DataSetExplorer/Program.cs +++ b/DataSetExplorer/Program.cs @@ -24,7 +24,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) => private static void CreateConsoleUI() { new MainMenu( - new DataSetExportationService(new FullDataSetFactory()), + new DataSetExportationService(new FullDataSetFactory(), null, null, null, null), new DataSetCreationService(new GitCodeRepository(), null, null, null)) .Run(); } diff --git a/DataSetExplorer/Startup.cs b/DataSetExplorer/Startup.cs index 61edde63..cf1a0f6e 100644 --- a/DataSetExplorer/Startup.cs +++ b/DataSetExplorer/Startup.cs @@ -6,10 +6,16 @@ using System; using DataSetExplorer.Core.AnnotationConsistency; using DataSetExplorer.Core.Annotations; +using DataSetExplorer.Core.AnnotationSchema; +using DataSetExplorer.Core.AnnotationSchema.Repository; using DataSetExplorer.Core.DataSets; using DataSetExplorer.Core.DataSets.Repository; using DataSetExplorer.Infrastructure.Database; using DataSetExplorer.Infrastructure.RepositoryAdapters; +using DataSetExplorer.Core.DataSetSerializer; +using DataSetExplorer.Core.Auth; +using DataSetExplorer.Core.Auth.Repository; +using DataSetExplorer.Core.CleanCodeAnalysis; namespace DataSetExplorer { @@ -34,6 +40,7 @@ public void ConfigureServices(IServiceCollection services) opt.UseNpgsql(CreateConnectionStringFromEnvironment())); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -43,11 +50,23 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); - + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); - + + services.AddScoped(); services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/DataSetExplorer/UI/ConsoleApp/AnnotationConsistencySubmenu.cs b/DataSetExplorer/UI/ConsoleApp/AnnotationConsistencySubmenu.cs index 272d7d8a..3d327fa0 100644 --- a/DataSetExplorer/UI/ConsoleApp/AnnotationConsistencySubmenu.cs +++ b/DataSetExplorer/UI/ConsoleApp/AnnotationConsistencySubmenu.cs @@ -30,16 +30,16 @@ internal void CheckAnnotationsConsistency() if (annotatorId.HasValue) _annotationConsistencyService.CheckAnnotationConsistencyForAnnotator(annotatorId.Value, projects, annotators); break; case "2": - var severityId = GetId("Severity"); - if (severityId.HasValue) _annotationConsistencyService.CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(severityId.Value, projects, annotators); + var severity = ConsoleIO.GetAnswerOnQuestion("Severity: "); + if (!severity.Equals("")) _annotationConsistencyService.CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(severity, projects, annotators); break; case "3": annotatorId = GetId("Annotator"); if (annotatorId.HasValue) _annotationConsistencyService.CheckMetricsSignificanceInAnnotationsForAnnotator(annotatorId.Value, projects, annotators); break; case "4": - severityId = GetId("Severity"); - if (severityId.HasValue) _annotationConsistencyService.CheckMetricsSignificanceBetweenAnnotatorsForSeverity(severityId.Value, projects, annotators); + severity = ConsoleIO.GetAnswerOnQuestion("Severity: "); + if (!severity.Equals("")) _annotationConsistencyService.CheckMetricsSignificanceBetweenAnnotatorsForSeverity(severity, projects, annotators); break; case "x": break; @@ -66,7 +66,6 @@ private static void WriteAnnotationsConsistencyOptions() Console.WriteLine("3. Check metrics significance for a single annotator"); Console.WriteLine("4. Check metrics significance between annotators"); Console.WriteLine("x. Exit\n"); - } } } diff --git a/DataSetExplorer/UI/ConsoleApp/DataSetIO.cs b/DataSetExplorer/UI/ConsoleApp/DataSetIO.cs index a2d49ed4..c04040df 100644 --- a/DataSetExplorer/UI/ConsoleApp/DataSetIO.cs +++ b/DataSetExplorer/UI/ConsoleApp/DataSetIO.cs @@ -29,7 +29,9 @@ internal static List GetAnnotators() foreach (string line in lines) { string[] columns = line.Split(','); - annotators.Add(new Annotator(int.Parse(columns[0]), int.Parse(columns[1]), int.Parse(columns[2]))); + if (columns.Length == 4) annotators.Add(new Annotator(int.Parse(columns[0]), columns[1], int.Parse(columns[2]), int.Parse(columns[3]))); + else annotators.Add(new Annotator(int.Parse(columns[0]), columns[1], columns[2], int.Parse(columns[3]), int.Parse(columns[4]))); + } return annotators; } diff --git a/DataSetExplorer/UI/ConsoleApp/MainMenu.cs b/DataSetExplorer/UI/ConsoleApp/MainMenu.cs index c46e32a9..701f4232 100644 --- a/DataSetExplorer/UI/ConsoleApp/MainMenu.cs +++ b/DataSetExplorer/UI/ConsoleApp/MainMenu.cs @@ -27,7 +27,6 @@ internal void Run() chosenOption = ConsoleIO.GetAnswerOnQuestion("Your option: "); Console.Clear(); ProcessChosenOption(chosenOption); - } while (!chosenOption.Equals("x")); } @@ -39,7 +38,7 @@ private void ProcessChosenOption(string chosenOption) CreateDataSet(); break; case "2": - new DataSetAnalysisSubmenu(new DataSetAnalysisService(null)).AnalyzeDataSet(); + new DataSetAnalysisSubmenu(new DataSetAnalysisService(null, null)).AnalyzeDataSet(); break; case "3": ExportDataSet(); @@ -68,11 +67,11 @@ private void CreateDataSet() private void ExportDataSet() { - var projects = DataSetIO.GetProjects("local repo folder and annotations folder"); + /*var projects = DataSetIO.GetProjects("local repo folder and annotations folder"); var annotators = DataSetIO.GetAnnotators(); string outputPath = ConsoleIO.GetAnswerOnQuestion("Enter output folder path: "); Result result = _dataSetExportationService.Export(projects, annotators, outputPath); - Console.Write(result.ToString()); + Console.Write(result);*/ } private static void WriteMenuWithOptions() diff --git a/DataSetExplorer/UI/Controllers/AnnotationConsistency/AnnotationConsistencyController.cs b/DataSetExplorer/UI/Controllers/AnnotationConsistency/AnnotationConsistencyController.cs index cfb8bc0b..c584ab3e 100644 --- a/DataSetExplorer/UI/Controllers/AnnotationConsistency/AnnotationConsistencyController.cs +++ b/DataSetExplorer/UI/Controllers/AnnotationConsistency/AnnotationConsistencyController.cs @@ -24,7 +24,7 @@ public IActionResult GetAnnotationConsistencyForAnnotator([FromRoute] int projec [HttpGet] [Route("annotators/{projectId}/{severity}")] - public IActionResult GetAnnotationConsistencyBetweenAnnotatorsForSeverity([FromRoute] int projectId, [FromRoute] int severity) + public IActionResult GetAnnotationConsistencyBetweenAnnotatorsForSeverity([FromRoute] int projectId, [FromRoute] string severity) { var result = _annotationConsistencyService.CheckAnnotationConsistencyBetweenAnnotatorsForSeverity(projectId, severity); return Ok(result.Value); @@ -40,7 +40,7 @@ public IActionResult GetMetricsSignificanceInAnnotationsForAnnotator([FromRoute] [HttpGet] [Route("metrics/annotators/{projectId}/{severity}")] - public IActionResult GetMetricsSignificanceBetweenAnnotatorsForSeverity([FromRoute] int projectId, [FromRoute] int severity) + public IActionResult GetMetricsSignificanceBetweenAnnotatorsForSeverity([FromRoute] int projectId, [FromRoute] string severity) { var result = _annotationConsistencyService.CheckMetricsSignificanceBetweenAnnotatorsForSeverity(projectId, severity); return Ok(result.Value); diff --git a/DataSetExplorer/UI/Controllers/AnnotationSchema/AnnotationSchemaController.cs b/DataSetExplorer/UI/Controllers/AnnotationSchema/AnnotationSchemaController.cs new file mode 100644 index 00000000..f9c26984 --- /dev/null +++ b/DataSetExplorer/UI/Controllers/AnnotationSchema/AnnotationSchemaController.cs @@ -0,0 +1,170 @@ +using AutoMapper; +using DataSetExplorer.Core.AnnotationSchema; +using DataSetExplorer.Core.AnnotationSchema.Model; +using DataSetExplorer.UI.Controllers.AnnotationSchema.DTOs; +using Microsoft.AspNetCore.Mvc; + +namespace DataSetExplorer.UI.Controllers.AnnotationSchema +{ + [Route("api/annotation-schema/code-smells/")] + [ApiController] + public class AnnotationSchemaController : ControllerBase + { + private readonly IMapper _mapper; + private readonly IAnnotationSchemaService _annotationSchemaService; + + public AnnotationSchemaController(IMapper mapper, IAnnotationSchemaService annotationSchemaService) + { + _mapper = mapper; + _annotationSchemaService = annotationSchemaService; + } + + [HttpGet] + public IActionResult GetAllCodeSmellDefinitions() + { + var result = _annotationSchemaService.GetAllCodeSmellDefinitions(); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpGet] + [Route("{id}")] + public IActionResult GetCodeSmellDefinition([FromRoute] int id) + { + var result = _annotationSchemaService.GetCodeSmellDefinition(id); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpGet] + [Route("name/{name}")] + public IActionResult GetCodeSmellDefinitionByName([FromRoute] string name) + { + var result = _annotationSchemaService.GetCodeSmellDefinitionByName(name); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpPost] + public IActionResult CreateCodeSmellDefinition([FromBody] CodeSmellDefinitionDTO codeSmellDefinitionDto) + { + var codeSmellDefinition = _mapper.Map(codeSmellDefinitionDto); + var result = _annotationSchemaService.CreateCodeSmellDefinition(codeSmellDefinition); + if (result.IsFailed) return NotFound(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpPut] + [Route("{id}")] + public IActionResult UpdateCodeSmellDefinition([FromRoute] int id, [FromBody] CodeSmellDefinitionDTO codeSmellDefinitionDto) + { + var codeSmellDefinition = _mapper.Map(codeSmellDefinitionDto); + var result = _annotationSchemaService.UpdateCodeSmellDefinition(id, codeSmellDefinition); + if (result.IsFailed) return NotFound(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpDelete] + [Route("{id}")] + public IActionResult DeleteCodeSmellDefinition([FromRoute] int id) + { + var result = _annotationSchemaService.DeleteCodeSmellDefinition(id); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpGet] + [Route("{id}/heuristics")] + public IActionResult GetHeuristicsForCodeSmell([FromRoute] int id) + { + var result = _annotationSchemaService.GetHeuristicsForCodeSmell(id); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpGet] + [Route("heuristics")] + public IActionResult GetHeuristicsForEachCodeSmell() + { + var result = _annotationSchemaService.GetHeuristicsForEachCodeSmell(); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpPost] + [Route("{id}/heuristics")] + public IActionResult AddHeuristicToCodeSmell([FromRoute] int id, [FromBody] HeuristicDefinitionDTO heuristicDto) + { + var heuristic = _mapper.Map(heuristicDto); + var result = _annotationSchemaService.AddHeuristicToCodeSmell(id, heuristic); + if (result.IsFailed) return NotFound(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpDelete] + [Route("{smellId}/heuristics/{heuristicId}")] + public IActionResult RemoveHeuristicFromCodeSmell([FromRoute] int smellId, [FromRoute] int heuristicId) + { + var result = _annotationSchemaService.DeleteHeuristicFromCodeSmell(smellId, heuristicId); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpPut] + [Route("{id}/heuristics")] + public IActionResult UpdateHeuristicInCodeSmell([FromRoute] int id, [FromBody] HeuristicDefinitionDTO heuristicDto) + { + var heuristic = _mapper.Map(heuristicDto); + var result = _annotationSchemaService.UpdateHeuristicInCodeSmell(id, heuristic); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpGet] + [Route("{id}/severities")] + public IActionResult GetSeveritiesForCodeSmell([FromRoute] int id) + { + var result = _annotationSchemaService.GetSeveritiesForCodeSmell(id); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpGet] + [Route("severities")] + public IActionResult GetSeveritiesForEachCodeSmell() + { + var result = _annotationSchemaService.GetSeveritiesForEachCodeSmell(); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpPost] + [Route("{id}/severities")] + public IActionResult AddSeverityToCodeSmell([FromRoute] int id, [FromBody] SeverityDefinitionDTO severityDto) + { + var severity = _mapper.Map(severityDto); + var result = _annotationSchemaService.AddSeverityToCodeSmell(id, severity); + if (result.IsFailed) return NotFound(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpDelete] + [Route("{smellId}/severities/{severityId}")] + public IActionResult RemoveSeverityFromCodeSmell([FromRoute] int smellId, [FromRoute] int severityId) + { + var result = _annotationSchemaService.DeleteSeverityFromCodeSmell(smellId, severityId); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpPut] + [Route("{id}/severities")] + public IActionResult UpdateSeverityInCodeSmell([FromRoute] int id, [FromBody] SeverityDefinitionDTO severityDTO) + { + var severity = _mapper.Map(severityDTO); + var result = _annotationSchemaService.UpdateSeverityInCodeSmell(id, severity); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + } +} diff --git a/DataSetExplorer/UI/Controllers/AnnotationSchema/DTOs/CodeSmellDefinitionDTO.cs b/DataSetExplorer/UI/Controllers/AnnotationSchema/DTOs/CodeSmellDefinitionDTO.cs new file mode 100644 index 00000000..38ce8d8f --- /dev/null +++ b/DataSetExplorer/UI/Controllers/AnnotationSchema/DTOs/CodeSmellDefinitionDTO.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace DataSetExplorer.UI.Controllers.AnnotationSchema.DTOs +{ + public class CodeSmellDefinitionDTO + { + public string Name { get; set; } + public string Description { get; set; } + public string SnippetType { get; set; } + public List Heuristics { get; set; } + public List SeverityValues { get; set; } + } +} diff --git a/DataSetExplorer/UI/Controllers/AnnotationSchema/DTOs/HeuristicDefinitionDTO.cs b/DataSetExplorer/UI/Controllers/AnnotationSchema/DTOs/HeuristicDefinitionDTO.cs new file mode 100644 index 00000000..96f267bc --- /dev/null +++ b/DataSetExplorer/UI/Controllers/AnnotationSchema/DTOs/HeuristicDefinitionDTO.cs @@ -0,0 +1,9 @@ +namespace DataSetExplorer.UI.Controllers.AnnotationSchema.DTOs +{ + public class HeuristicDefinitionDTO + { + public int Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + } +} diff --git a/DataSetExplorer/UI/Controllers/AnnotationSchema/DTOs/SeverityDefinitionDTO.cs b/DataSetExplorer/UI/Controllers/AnnotationSchema/DTOs/SeverityDefinitionDTO.cs new file mode 100644 index 00000000..10118a0d --- /dev/null +++ b/DataSetExplorer/UI/Controllers/AnnotationSchema/DTOs/SeverityDefinitionDTO.cs @@ -0,0 +1,9 @@ +namespace DataSetExplorer.UI.Controllers.AnnotationSchema.DTOs +{ + public class SeverityDefinitionDTO + { + public int Id { get; set; } + public string Value { get; set; } + public string Description { get; set; } + } +} diff --git a/DataSetExplorer/UI/Controllers/Annotations/AnnotationController.cs b/DataSetExplorer/UI/Controllers/Annotations/AnnotationController.cs index 7112b24f..45d5b576 100644 --- a/DataSetExplorer/UI/Controllers/Annotations/AnnotationController.cs +++ b/DataSetExplorer/UI/Controllers/Annotations/AnnotationController.cs @@ -20,7 +20,8 @@ public class AnnotationController : ControllerBase private readonly IAnnotationService _annotationService; private readonly IDataSetAnalysisService _dataSetAnalysisService; - public AnnotationController(IMapper mapper, IConfiguration configuration, IAnnotationService annotationService, IDataSetAnalysisService dataSetAnalysisService) + public AnnotationController(IMapper mapper, IConfiguration configuration, IAnnotationService annotationService, + IDataSetAnalysisService dataSetAnalysisService) { _mapper = mapper; _configuration = configuration; @@ -28,13 +29,6 @@ public AnnotationController(IMapper mapper, IConfiguration configuration, IAnnot _dataSetAnalysisService = dataSetAnalysisService; } - [HttpGet] - [Route("available-code-smells")] - public IActionResult GetAllCodeSmells() - { - return Ok(_configuration.GetSection("Annotating:AvailableCodeSmells").Get>()); - } - [HttpGet] [Route("available-metrics")] public IActionResult GetAllMetrics() @@ -42,13 +36,6 @@ public IActionResult GetAllMetrics() return Ok(_configuration.GetSection("Annotating:AvailableMetrics").Get>()); } - [HttpGet] - [Route("available-heuristics")] - public IActionResult GetAllAvailableHeuristics() - { - return Ok(_configuration.GetSection("Annotating:AvailableHeuristics").Get>()); - } - [HttpPost] public IActionResult AddDataSetAnnotation([FromBody] AnnotationDTO annotation) { diff --git a/DataSetExplorer/UI/Controllers/Annotations/DTOs/AnnotationDTO.cs b/DataSetExplorer/UI/Controllers/Annotations/DTOs/AnnotationDTO.cs index 519799f2..0269179c 100644 --- a/DataSetExplorer/UI/Controllers/Annotations/DTOs/AnnotationDTO.cs +++ b/DataSetExplorer/UI/Controllers/Annotations/DTOs/AnnotationDTO.cs @@ -5,7 +5,7 @@ namespace DataSetExplorer.UI.Controllers.Annotations.DTOs public class AnnotationDTO { public int InstanceId { get; set; } - public int Severity { get; set; } + public string Severity { get; set; } public string CodeSmell { get; set; } public List ApplicableHeuristics { get; set; } public int AnnotatorId { get; set; } diff --git a/DataSetExplorer/UI/Controllers/Annotations/Mappers/AnnotationProfile.cs b/DataSetExplorer/UI/Controllers/Annotations/Mappers/AnnotationProfile.cs index 14489d00..ad2f04f7 100644 --- a/DataSetExplorer/UI/Controllers/Annotations/Mappers/AnnotationProfile.cs +++ b/DataSetExplorer/UI/Controllers/Annotations/Mappers/AnnotationProfile.cs @@ -2,6 +2,8 @@ using AutoMapper; using DataSetExplorer.Core.Annotations.Model; using DataSetExplorer.UI.Controllers.Annotations.DTOs; +using DataSetExplorer.Core.AnnotationSchema.Model; +using DataSetExplorer.UI.Controllers.AnnotationSchema.DTOs; namespace DataSetExplorer.UI.Controllers.Annotations.Mappers { @@ -12,6 +14,9 @@ public AnnotationProfile() CreateMap() .IgnoreAllPropertiesWithAnInaccessibleSetter() .ConstructUsing(src => new Annotation(src.CodeSmell, src.Severity, new Annotator(src.AnnotatorId), CreateHeuristics(src.ApplicableHeuristics), src.Note)); + CreateMap(); + CreateMap(); + CreateMap(); } private List CreateHeuristics(List heuristics) diff --git a/DataSetExplorer/UI/Controllers/Auth/AuthController.cs b/DataSetExplorer/UI/Controllers/Auth/AuthController.cs new file mode 100644 index 00000000..97bbdf18 --- /dev/null +++ b/DataSetExplorer/UI/Controllers/Auth/AuthController.cs @@ -0,0 +1,52 @@ +using AutoMapper; +using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.Core.Auth; +using DataSetExplorer.UI.Controllers.Auth.DTOs; +using Microsoft.AspNetCore.Mvc; + +namespace DataSetExplorer.UI.Controllers.Auth +{ + [Route("api/auth/")] + [ApiController] + public class AuthController : ControllerBase + { + private readonly IMapper _mapper; + private readonly IAuthService _authService; + + public AuthController(IMapper mapper, IAuthService authService) + { + _mapper = mapper; + _authService = authService; + } + + [HttpPost] + public IActionResult RegisterAnnotator([FromBody] AnnotatorDTO annotatorDTO) + { + var annotator = _authService.Save(_mapper.Map(annotatorDTO)); + return Ok(annotator); + } + + [HttpGet] + [Route("email/{email}")] + public IActionResult GetAnnotatorByEmail([FromRoute] string email) + { + return Ok(_authService.GetAnnotatorByEmail(email).Value); + } + + [HttpGet] + [Route("id/{id}")] + public IActionResult GetAnnotatorById([FromRoute] int id) + { + return Ok(_authService.GetAnnotatorById(id).Value); + } + + [HttpPut] + [Route("{id}")] + public IActionResult UpdateAnnotator([FromRoute] int id, [FromBody] AnnotatorDTO annotatorDTO) + { + var annotator = _mapper.Map(annotatorDTO); + annotator.Id = id; + return Ok(_authService.Save(annotator).Value); + } + } +} diff --git a/DataSetExplorer/UI/Controllers/Auth/DTOs/AnnotatorDTO.cs b/DataSetExplorer/UI/Controllers/Auth/DTOs/AnnotatorDTO.cs new file mode 100644 index 00000000..3a945220 --- /dev/null +++ b/DataSetExplorer/UI/Controllers/Auth/DTOs/AnnotatorDTO.cs @@ -0,0 +1,10 @@ +namespace DataSetExplorer.UI.Controllers.Auth.DTOs +{ + public class AnnotatorDTO + { + public string Name { get; set; } + public string Email { get; set; } + public int YearsOfExperience { get; set; } + public int Ranking { get; set; } + } +} diff --git a/DataSetExplorer/UI/Controllers/Auth/Mapper/AuthProfile.cs b/DataSetExplorer/UI/Controllers/Auth/Mapper/AuthProfile.cs new file mode 100644 index 00000000..cb59747c --- /dev/null +++ b/DataSetExplorer/UI/Controllers/Auth/Mapper/AuthProfile.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using DataSetExplorer.Core.Annotations.Model; +using DataSetExplorer.UI.Controllers.Auth.DTOs; + +namespace DataSetExplorer.UI.Controllers.Auth.Mapper +{ + public class AuthProfile : Profile + { + public AuthProfile() + { + CreateMap(); + } + } +} diff --git a/DataSetExplorer/UI/Controllers/Dataset/DTOs/CleanCodeAnalysisDTO.cs b/DataSetExplorer/UI/Controllers/Dataset/DTOs/CleanCodeAnalysisDTO.cs new file mode 100644 index 00000000..457b4025 --- /dev/null +++ b/DataSetExplorer/UI/Controllers/Dataset/DTOs/CleanCodeAnalysisDTO.cs @@ -0,0 +1,8 @@ +namespace DataSetExplorer.UI.Controllers.Dataset.DTOs +{ + public class CleanCodeAnalysisDTO + { + public string ExportPath { get; set; } + public string[] CleanCodeOptions { get; set; } + } +} diff --git a/DataSetExplorer/UI/Controllers/Dataset/DTOs/CodeSmellDTO.cs b/DataSetExplorer/UI/Controllers/Dataset/DTOs/CodeSmellDTO.cs index 5d239302..db910a12 100644 --- a/DataSetExplorer/UI/Controllers/Dataset/DTOs/CodeSmellDTO.cs +++ b/DataSetExplorer/UI/Controllers/Dataset/DTOs/CodeSmellDTO.cs @@ -3,5 +3,6 @@ public class CodeSmellDTO { public string Name { get; set; } + public string SnippetType { get; set; } } } diff --git a/DataSetExplorer/UI/Controllers/Dataset/DTOs/CompleteDataSetExportDTO.cs b/DataSetExplorer/UI/Controllers/Dataset/DTOs/CompleteDataSetExportDTO.cs new file mode 100644 index 00000000..f6ffbf08 --- /dev/null +++ b/DataSetExplorer/UI/Controllers/Dataset/DTOs/CompleteDataSetExportDTO.cs @@ -0,0 +1,8 @@ +namespace DataSetExplorer.UI.Controllers.Dataset.DTOs +{ + public class CompleteDataSetExportDTO + { + public string AnnotationsPath { get; set; } + public string ExportPath { get; set; } + } +} diff --git a/DataSetExplorer/UI/Controllers/Dataset/DTOs/ProjectBuildSettingsDTO.cs b/DataSetExplorer/UI/Controllers/Dataset/DTOs/ProjectBuildSettingsDTO.cs index 9cf3e5df..2d6458d8 100644 --- a/DataSetExplorer/UI/Controllers/Dataset/DTOs/ProjectBuildSettingsDTO.cs +++ b/DataSetExplorer/UI/Controllers/Dataset/DTOs/ProjectBuildSettingsDTO.cs @@ -8,6 +8,6 @@ public class ProjectBuildSettingsDTO public string NumOfInstancesType { get; set; } public bool RandomizeClassSelection { get; set; } public bool RandomizeMemberSelection { get; set; } - public List foldersToIgnore { get; set; } + public List IgnoredFolders { get; set; } } } diff --git a/DataSetExplorer/UI/Controllers/Dataset/DataSetController.cs b/DataSetExplorer/UI/Controllers/Dataset/DataSetController.cs index e6d46654..fbf136e8 100644 --- a/DataSetExplorer/UI/Controllers/Dataset/DataSetController.cs +++ b/DataSetExplorer/UI/Controllers/Dataset/DataSetController.cs @@ -4,6 +4,7 @@ using DataSetExplorer.Core.DataSets; using DataSetExplorer.Core.DataSets.Model; using DataSetExplorer.Core.DataSetSerializer; +using DataSetExplorer.Core.CleanCodeAnalysis; using DataSetExplorer.UI.Controllers.Dataset.DTOs; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; @@ -18,11 +19,16 @@ public class DataSetController : ControllerBase private readonly IMapper _mapper; private readonly IDataSetCreationService _dataSetCreationService; + private readonly IDataSetExportationService _dataSetExportationService; + private readonly ICleanCodeAnalysisService _cleanCodeAnalysisService; - public DataSetController(IMapper mapper, IDataSetCreationService creationService, IConfiguration configuration) + public DataSetController(IMapper mapper, IDataSetCreationService creationService, IConfiguration configuration, + IDataSetExportationService exportationService, ICleanCodeAnalysisService cleanCodeAnalysisService) { _mapper = mapper; _dataSetCreationService = creationService; + _dataSetExportationService = exportationService; + _cleanCodeAnalysisService = cleanCodeAnalysisService; _gitClonePath = configuration.GetValue("Workspace:GitClonePath"); } @@ -36,11 +42,26 @@ public IActionResult UpdateDataSet([FromBody] DatasetDTO datasetDto) } [HttpPost] - [Route("export")] - public IActionResult ExportDataSet([FromBody] DraftDataSetExportDTO dataSetDTO) + [Route("export-draft")] + public IActionResult ExportDraftDataSet([FromBody] DraftDataSetExportDTO dataSetDTO) { - var dataSet = _dataSetCreationService.GetDataSetForExport(dataSetDTO.Id).Value; - var exportPath = new DraftDataSetExporter(dataSetDTO.ExportPath).Export(dataSetDTO.AnnotatorId, dataSet); + var exportPath = _dataSetExportationService.ExportDraft(dataSetDTO); + return Ok(new FluentResults.Result().WithSuccess("Successfully exported to " + exportPath)); + } + + [HttpPost] + [Route("{id}/export-complete")] + public IActionResult ExportCompleteDataSet([FromRoute] int id, [FromBody] CompleteDataSetExportDTO dataSetDTO) + { + var exportPath = _dataSetExportationService.ExportComplete(id, dataSetDTO); + return Ok(new FluentResults.Result().WithSuccess("Successfully exported to " + exportPath)); + } + + [HttpPost] + [Route("{id}/export-clean-code-analysis")] + public IActionResult ExportCleanCodeAnalysis([FromRoute] int id, [FromBody] CleanCodeAnalysisDTO dataSetDTO) + { + var exportPath = _cleanCodeAnalysisService.ExportDatasetAnalysis(id, dataSetDTO).Value; return Ok(new FluentResults.Result().WithSuccess("Successfully exported to " + exportPath)); } diff --git a/DataSetExplorer/UI/Controllers/Dataset/InstanceController.cs b/DataSetExplorer/UI/Controllers/Dataset/InstanceController.cs index 74d75cf4..f61c0c18 100644 --- a/DataSetExplorer/UI/Controllers/Dataset/InstanceController.cs +++ b/DataSetExplorer/UI/Controllers/Dataset/InstanceController.cs @@ -35,7 +35,7 @@ public IActionResult GetInstanceWithAnnotations([FromRoute] int id) } [HttpGet] - [Route("{id}/cohesion-graph")] + [Route("{id}/class-cohesion-graph")] public IActionResult GetCohesionGraphForInstance([FromRoute] int id) { var result = _instanceService.GetInstanceWithAnnotations(id); diff --git a/DataSetExplorer/UI/Controllers/Dataset/ProjectController.cs b/DataSetExplorer/UI/Controllers/Dataset/ProjectController.cs index 171b8ace..57c34ee1 100644 --- a/DataSetExplorer/UI/Controllers/Dataset/ProjectController.cs +++ b/DataSetExplorer/UI/Controllers/Dataset/ProjectController.cs @@ -1,15 +1,11 @@ using AutoMapper; using DataSetExplorer.Core.CommunityDetection.Model; using DataSetExplorer.Core.DataSets; +using DataSetExplorer.Core.CleanCodeAnalysis; using DataSetExplorer.Core.DataSets.Model; using DataSetExplorer.UI.Controllers.Dataset.DTOs; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; namespace DataSetExplorer.UI.Controllers.Dataset { @@ -21,11 +17,18 @@ public class ProjectController : ControllerBase private readonly IMapper _mapper; private readonly IDataSetCreationService _dataSetCreationService; + private readonly IProjectService _projectService; + private readonly IGraphInstanceService _graphInstanceService; + private readonly ICleanCodeAnalysisService _cleanCodeAnalysisService; - public ProjectController(IMapper mapper, IDataSetCreationService creationService, IConfiguration configuration) + public ProjectController(IMapper mapper, IDataSetCreationService creationService, IConfiguration configuration, + IProjectService projectService, IGraphInstanceService graphInstanceService, ICleanCodeAnalysisService cleanCodeAnalysisService) { _mapper = mapper; _dataSetCreationService = creationService; + _projectService = projectService; + _graphInstanceService = graphInstanceService; + _cleanCodeAnalysisService = cleanCodeAnalysisService; _gitClonePath = configuration.GetValue("Workspace:GitClonePath"); } @@ -62,5 +65,40 @@ public IActionResult GetCommunities([FromBody] Graph Graph) { return Ok(_dataSetCreationService.ExportCommunities(Graph).Value); } + + [HttpGet] + [Route("{id}/graph")] + public IActionResult GetProjectWithGraphInstances([FromRoute] int id) + { + var result = _projectService.GetProjectWithGraphInstances(id); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpGet] + [Route("{projectId}/instances/{instanceId}/graph")] + public IActionResult GetGraphNeighboursInstances([FromRoute] int projectId, [FromRoute] int instanceId) + { + var result = _graphInstanceService.GetGraphInstanceWithRelatedInstances(projectId, instanceId); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpGet] + [Route("{projectId}/instances/{instanceCodeSnippetId}/graph-extended")] + public IActionResult GetGraphNeighboursInstances([FromRoute] int projectId, [FromRoute] string instanceCodeSnippetId) + { + var result = _graphInstanceService.GetGraphInstanceWithRelatedInstances(projectId, instanceCodeSnippetId); + if (result.IsFailed) return BadRequest(new { message = result.Reasons[0].Message }); + return Ok(result.Value); + } + + [HttpPost] + [Route("{id}/export-clean-code-analysis")] + public IActionResult ExportCleanCodeAnalysis([FromRoute] int id, [FromBody] CleanCodeAnalysisDTO analysisExportOptions) + { + var exportPath = _cleanCodeAnalysisService.ExportProjectAnalysis(id, analysisExportOptions).Value; + return Ok(new FluentResults.Result().WithSuccess("Successfully exported to " + exportPath)); + } } } diff --git a/DataSetExplorer/appsettings.Development.json b/DataSetExplorer/appsettings.Development.json index 7a3d1d2e..05e3a73e 100644 --- a/DataSetExplorer/appsettings.Development.json +++ b/DataSetExplorer/appsettings.Development.json @@ -3,17 +3,6 @@ "GitClonePath": "../../ClonedProjects/" }, "Annotating": { - "AvailableCodeSmells": { - "Class": [ "Large_Class", "Data_Class", "Refused_Bequest" ], - "Function": [ "Long_Method", "Feature_Envy" ] - }, - "AvailableHeuristics": { - "Large_Class": [ "Class is too long.", "Class is too complex.", "Class has multiple concerns." ], - "Data_Class": [ "No logic in methods.", "Not DTO, DAO,..." ], - "Refused_Bequest": [ "Few parent members used.", "Many members overriden.", "Unnecessary hierarchy." ], - "Long_Method": [ "Function is too long.", "Function is too complex.", "Function does multiple things." ], - "Feature_Envy": [ "Function has method chains.", "Function uses foreign data.", "Function has few foreign providers.", "Function does not belong here (semantic)." ] - }, "AvailableMetrics": { "Class": [ "CLOC", "CELOC", "LCOM", "LCOM3", "LCOM4", "NMD", "NAD", "NMD_NAD", "WMC", "WMC_NO_CASE", "ATFD", "ATFD_10", "TCC", "CNOR", "CNOL", "CNOC", "CNOA", "NOPM", "NOPF", "DIT", "DCC", "CMNB", "RFC", "CBO", "NIC", "WOC", "NOPA", "NOPP", "WMCNAMM", "BUR", "BOvR" ], "Function": [ "CYCLO", "CYCLO_SWITCH", "MLOC", "MELOC", "NOP", "NOLV", "NOTC", "MNOL", "MNOR", "MNOC", "MNOA", "NONL", "NOSL", "NOMO", "NOPE", "NOLE", "MMNB", "NOUW", "AID" ] diff --git a/DataSetExplorerTests/Unit/ImporterTests.cs b/DataSetExplorerTests/Unit/ImporterTests.cs index 9fbf213c..b44396da 100644 --- a/DataSetExplorerTests/Unit/ImporterTests.cs +++ b/DataSetExplorerTests/Unit/ImporterTests.cs @@ -16,8 +16,8 @@ public void Imports_data_set_instances_and_annotations() { var codeSmells = new List { - new CodeSmell("Long_Method"), - new CodeSmell("Large_Class") + new CodeSmell("Long_Method", SnippetType.Function), + new CodeSmell("Large_Class", SnippetType.Class) }; var dataSet = new DataSet("Test", codeSmells); ExcelImporter importer = new ExcelImporter(new ExcelFactory().GetTestDataFolder()); @@ -38,9 +38,9 @@ public void Imports_data_set_instances_and_annotations() var annotators = new List() { - new Annotator(1, 6, 1), - new Annotator(2, 2, 2), - new Annotator(3, 2, 3) + new Annotator(1, "Nikola Luburic", "nikola.luburic@uns.ac.rs", 6, 1), + new Annotator(2, "Simona Prokic", "simona.prokic@uns.ac.rs", 2, 2), + new Annotator(3, "Katarina-Glorija Grujic", "katarina.glorija@uns.ac.rs", 2, 3) }; JoinInstancesAndAnnotators(distinctClasses.ToList(), annotators);