Added JSON Nuget Package, updated DataGenerator to accept more Inputs, for example for the Sin function. Added A Feature, to create mutliple Datasets to show in the chart. Added Trendline to show the Trendline as a Dataset
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
using System.Diagnostics;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DataGeneratorMVC.Models;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||
using Microsoft.VisualBasic;
|
||||
using Newtonsoft.Json;
|
||||
using JsonConverter = System.Text.Json.Serialization.JsonConverter;
|
||||
|
||||
namespace DataGeneratorMVC.Controllers;
|
||||
|
||||
@@ -47,61 +54,72 @@ public class HomeController : Controller
|
||||
private void GenerateNew(GeneratorDataViewModel model)
|
||||
{
|
||||
var tmp = DataGenerator.Generate(model.Settings);
|
||||
var labelsSb = new StringBuilder();
|
||||
labelsSb.Append("[");
|
||||
var dataSb = new StringBuilder();
|
||||
dataSb.Append("[");
|
||||
|
||||
// var allDataSets = new StringBuilder();
|
||||
//
|
||||
// string datasetTemplate = "{{label: '{0}', data: {1}, backgroundColor: [\'rgba({2}, {3}, {4}, 0.2)\'], borderColor: [\'rgba(255, 99, 132, 1)\'], fill: false, tension: 0.4, spangaps: true}}{5} ";
|
||||
//
|
||||
// double[,] years = new double[model.Settings.Years, 366];
|
||||
//
|
||||
// foreach (var turnover in tmp)
|
||||
// {
|
||||
// years[turnover.Key.Year - model.Settings.StartYear, turnover.Key.DayOfYear - 1] = turnover.Value;
|
||||
// }
|
||||
//
|
||||
// for (int i = 0; i < model.Settings.Years; i++)
|
||||
// {
|
||||
// var dataSb = new StringBuilder();
|
||||
// dataSb.Append("[");
|
||||
// for (int j = 0; j < 366; j++)
|
||||
// {
|
||||
// if(years[i, j] != 0)
|
||||
// dataSb.Append($"{years[i, j].ToString("0.00").Replace(',', '.')}, ");
|
||||
// else
|
||||
// dataSb.Append($"{years[i, j-1].ToString("0.00").Replace(',', '.')}, ");
|
||||
// }
|
||||
// dataSb.Append("]");
|
||||
// allDataSets.Append(string.Format(datasetTemplate, model.Settings.StartYear + i,
|
||||
// dataSb.ToString(), 255, 99 + i, 132 + i, i-1 == model.Settings.Years ? "" : ","));
|
||||
// dataSb.Clear();
|
||||
// }
|
||||
//
|
||||
// for (int i = 0; i < 366; i++)
|
||||
// {
|
||||
// labelsSb.Append($"{i}, ");
|
||||
// }
|
||||
|
||||
int counter = 0;
|
||||
var labels = new List<double>();
|
||||
|
||||
foreach (var value in tmp)
|
||||
for (int i = 0; i < tmp.Count; i++)
|
||||
{
|
||||
counter++;
|
||||
labelsSb.Append($"{counter}, ");
|
||||
//labels.Append($"{value.Key}, ");
|
||||
dataSb.Append($"{value.Value.ToString("0.00").Replace(',', '.')}, ");
|
||||
labels.Add(i);
|
||||
}
|
||||
|
||||
labelsSb.Append("]");
|
||||
dataSb.Append("]");
|
||||
var datasets = new List<Dataset>();
|
||||
datasets.Add(new Dataset("Umsatz", tmp.Values, "rgba(255, 99, 132, 0.2)", "rgba(255, 99, 132, 1)", false, 0.4f, true));
|
||||
|
||||
var trendline = new Trendline( new List<double>(tmp.Values), labels);
|
||||
|
||||
var average = new Collection<double>();
|
||||
var minimum = new Collection<double>();
|
||||
var maximum = new Collection<double>();
|
||||
var summed = new Collection<double>();
|
||||
var trend = new Collection<double>();
|
||||
var trendStandardDeviationUp = new Collection<double>();
|
||||
var trendStandardDeviationDown = new Collection<double>();
|
||||
|
||||
|
||||
double stdDeviation = StandardDeviation(tmp.Values);
|
||||
double avg = tmp.Values.Average();
|
||||
double min = tmp.Values.Min();
|
||||
double max = tmp.Values.Max();
|
||||
|
||||
for (int i = 0; i < labels.Count; i++)
|
||||
{
|
||||
double trendValue = trendline.GetYValue(i);
|
||||
|
||||
average.Add(avg);
|
||||
minimum.Add(min);
|
||||
maximum.Add(max);
|
||||
|
||||
trendStandardDeviationUp.Add(trendValue + stdDeviation);
|
||||
trendStandardDeviationDown.Add(trendValue - stdDeviation);
|
||||
trend.Add(trendValue);
|
||||
}
|
||||
|
||||
datasets.Add(new Dataset("Trendlinie", trend){BorderColor = "rgba(11,127,171)"});
|
||||
datasets.Add(new Dataset("Standard Deviation Up", trendStandardDeviationUp){BorderColor = "rgba(0,181,204)"});
|
||||
datasets.Add(new Dataset("Standard Deviation Down", trendStandardDeviationDown){BorderColor = "rgba(0,181,204)"});
|
||||
datasets.Add(new Dataset("Average", average){BorderColor = "rgba(8,14,44)"});
|
||||
datasets.Add(new Dataset("Min", minimum){BorderColor = "rgba(8,14,44)"});
|
||||
datasets.Add(new Dataset("Max", maximum){BorderColor = "rgba(8,14,44)"});
|
||||
|
||||
|
||||
model.Sql = DataGenerator.Save(tmp);
|
||||
model.DataSet = dataSb.ToString();
|
||||
model.LabelSet = labelsSb.ToString();
|
||||
model.DataSet = JsonConvert.SerializeObject(datasets, new JsonSerializerSettings(){NullValueHandling = NullValueHandling.Ignore});
|
||||
model.LabelSet = JsonConvert.SerializeObject(labels);
|
||||
}
|
||||
|
||||
private static double StandardDeviation(IEnumerable<double> sequence)
|
||||
{
|
||||
double result = 0;
|
||||
|
||||
if (sequence.Any())
|
||||
{
|
||||
double average = sequence.Average();
|
||||
double sum = sequence.Sum(d => Math.Pow(d - average, 2));
|
||||
result = Math.Sqrt((sum) / (sequence.Count() - 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public IActionResult Privacy()
|
||||
{
|
||||
return View();
|
||||
|
||||
@@ -5,8 +5,8 @@ using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DataGeneratorMVC;
|
||||
|
||||
[Bind("StartYear,Years,MinDiff,MaxDiff,HasSin,Smoothing,RSmoothing,MinSmoothing,MaxSmoothing,StartTurnover,Linear,HasPeak,PeakLength,PeakStrength,RPeakInYear,PeakInYear")]
|
||||
public class GeneratorSettings
|
||||
//[Bind("StartYear,Years,MinDiff,MaxDiff,HasSin,Smoothing,RSmoothing,MinSmoothing,MaxSmoothing,StartTurnover,Linear,HasPeak,PeakLength,PeakStrength,RPeakInYear,PeakInYear")]
|
||||
public class GeneratorSettings : IValidatableObject
|
||||
{
|
||||
[Range(1500,3000)]
|
||||
public int StartYear { get; set; }
|
||||
@@ -25,12 +25,14 @@ public class GeneratorSettings
|
||||
public int MaxSmoothing { get; set; }
|
||||
[DataType(DataType.Currency)]
|
||||
public double StartTurnover { get; set; }
|
||||
|
||||
public double Linear { get; set; }
|
||||
public bool HasSin { get; set; }
|
||||
public int Linear { get; set; }
|
||||
public double SinStrength { get; set; }
|
||||
public double SinLength { get; set; }
|
||||
public bool SinNegative { get; set; }
|
||||
public bool HasPeak { get; set; }
|
||||
public int PeakLength { get; set; }
|
||||
public int PeakStrength { get; set; }
|
||||
public double PeakLength { get; set; }
|
||||
public double PeakStrength { get; set; }
|
||||
public bool RPeakInYear { get; set; }
|
||||
public int PeakInYear { get; set; }
|
||||
|
||||
@@ -51,12 +53,24 @@ public class GeneratorSettings
|
||||
MinSmoothing = Clamp(minSmoothing, 1, 100);
|
||||
MaxSmoothing = Clamp(maxSmoothing, 1, 100);
|
||||
StartTurnover = startTurnover;
|
||||
SinLength = 182.5;
|
||||
SinStrength = 10;
|
||||
SinNegative = true;
|
||||
}
|
||||
|
||||
private static int Clamp(int value, int min, int max)
|
||||
{
|
||||
return (value < min) ? min : (value > max) ? max : value;
|
||||
}
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (HasPeak && (PeakLength < 1 || PeakStrength < 1 || RPeakInYear == false && 1 > PeakInYear && PeakInYear < 360))
|
||||
yield return new ValidationResult("If Peak is enabled, all Peak settings must be set", new []{nameof(HasPeak), nameof(PeakLength), nameof(PeakStrength), nameof(RPeakInYear), nameof(PeakInYear)});
|
||||
|
||||
if (HasSin && (SinLength < 1 || SinStrength < 1))
|
||||
yield return new ValidationResult("If Sin is enabled, all Sin settings must be set", new []{nameof(HasSin), nameof(SinLength), nameof(SinStrength)});
|
||||
}
|
||||
}
|
||||
|
||||
public static class DataGenerator
|
||||
@@ -74,11 +88,11 @@ public static class DataGenerator
|
||||
int smoothing = settings.Smoothing;
|
||||
|
||||
int counter = 0;
|
||||
int linear = settings.Linear;
|
||||
double linear = settings.Linear;
|
||||
|
||||
int peakInYear = settings.RPeakInYear ? _r.Next(10, 350) : settings.PeakInYear;
|
||||
int peakLength = settings.PeakLength;
|
||||
int peakStrength = settings.PeakStrength;
|
||||
double peakLength = settings.PeakLength;
|
||||
double peakStrength = settings.PeakStrength;
|
||||
int peakCounter = 0;
|
||||
|
||||
for (int year = 0; year < settings.Years; year++)
|
||||
@@ -119,7 +133,12 @@ public static class DataGenerator
|
||||
currentCurve += _r.Next(minDiff, maxDiff + 1);
|
||||
|
||||
if(settings.HasSin)
|
||||
currentCurve += -(10 * Math.Sin(counter * Math.PI / 182.5));
|
||||
currentCurve += settings.SinNegative switch
|
||||
{
|
||||
true => -(settings.SinStrength * Math.Sin(counter * Math.PI / settings.SinLength)),
|
||||
false => (settings.SinStrength * Math.Sin(counter * Math.PI / settings.SinLength)) //182.5
|
||||
};
|
||||
|
||||
currentCurve += linear;
|
||||
|
||||
// If it is the first Day of the Simulation start
|
||||
|
||||
@@ -6,4 +6,8 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
38
DataGeneratorMVC/Models/Dataset.cs
Normal file
38
DataGeneratorMVC/Models/Dataset.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace DataGeneratorMVC.Models;
|
||||
|
||||
public class Dataset
|
||||
{
|
||||
[JsonProperty("label")]
|
||||
public string Label { get; set; }
|
||||
[JsonProperty("data")]
|
||||
public ICollection<double> Data { get; set; }
|
||||
[JsonProperty("backgroundColor")]
|
||||
public string? BackgroundColor { get; set; }
|
||||
[JsonProperty("borderColor")]
|
||||
public string? BorderColor { get; set; }
|
||||
[JsonProperty("fill")]
|
||||
public bool? Fill { get; set; }
|
||||
[JsonProperty("tension")]
|
||||
public float? Tension { get; set; }
|
||||
[JsonProperty("spangaps")]
|
||||
public bool? Spangaps { get; set; }
|
||||
|
||||
public Dataset(string label, ICollection<double> data)
|
||||
{
|
||||
Label = label;
|
||||
Data = data;
|
||||
}
|
||||
public Dataset(string label, ICollection<double> data, string backgroundColor, string borderColor, bool fill, float tension, bool spangaps)
|
||||
{
|
||||
Label = label;
|
||||
Data = data;
|
||||
BackgroundColor = backgroundColor;
|
||||
BorderColor = borderColor;
|
||||
Fill = fill;
|
||||
Tension = tension;
|
||||
Spangaps = spangaps;
|
||||
}
|
||||
}
|
||||
39
DataGeneratorMVC/Trendline.cs
Normal file
39
DataGeneratorMVC/Trendline.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
namespace DataGeneratorMVC;
|
||||
|
||||
public class Trendline
|
||||
{
|
||||
public Trendline(IList<double> yAxisValues, IList<double> xAxisValues)
|
||||
: this(yAxisValues.Select((t, i) => new Tuple<double, double>(xAxisValues[i], t)))
|
||||
{ }
|
||||
public Trendline(IEnumerable<Tuple<double, double>> data)
|
||||
{
|
||||
var cachedData = data.ToList();
|
||||
|
||||
int n = cachedData.Count;
|
||||
double sumX = cachedData.Sum(x => x.Item1);
|
||||
double sumX2 = cachedData.Sum(x => x.Item1 * x.Item1);
|
||||
double sumY = cachedData.Sum(x => x.Item2);
|
||||
double sumXY = cachedData.Sum(x => x.Item1 * x.Item2);
|
||||
|
||||
//b = (sum(x*y) - sum(x)sum(y)/n)
|
||||
// / (sum(x^2) - sum(x)^2/n)
|
||||
Slope = (sumXY - ((sumX * sumY) / n))
|
||||
/ (sumX2 - (sumX * sumX / n));
|
||||
|
||||
//a = sum(y)/n - b(sum(x)/n)
|
||||
Intercept = (sumY / n) - (Slope * (sumX / n));
|
||||
|
||||
Start = GetYValue(cachedData.Min(a => a.Item1));
|
||||
End = GetYValue(cachedData.Max(a => a.Item1));
|
||||
}
|
||||
|
||||
public double Slope { get; private set; }
|
||||
public double Intercept { get; private set; }
|
||||
public double Start { get; private set; }
|
||||
public double End { get; private set; }
|
||||
|
||||
public double GetYValue(double xValue)
|
||||
{
|
||||
return Intercept + Slope * xValue;
|
||||
}
|
||||
}
|
||||
@@ -22,66 +22,97 @@
|
||||
<div>
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.StartYear, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.StartYear)
|
||||
@Html.ValidationMessageFor(model => model.Settings.StartYear, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.Years, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.Years)
|
||||
@Html.ValidationMessageFor(model => model.Settings.Years, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.StartTurnover, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.StartTurnover)
|
||||
@Html.ValidationMessageFor(model => model.Settings.StartTurnover, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.MaxDiff, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.MaxDiff)
|
||||
@Html.ValidationMessageFor(model => model.Settings.MaxDiff, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.MinDiff, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.MinDiff)
|
||||
@Html.ValidationMessageFor(model => model.Settings.MinDiff, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.RSmoothing, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.RSmoothing)
|
||||
@Html.ValidationMessageFor(model => model.Settings.RSmoothing, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.MaxSmoothing, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.MaxSmoothing)
|
||||
@Html.ValidationMessageFor(model => model.Settings.MaxSmoothing, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.MinSmoothing, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.MinSmoothing)
|
||||
@Html.ValidationMessageFor(model => model.Settings.MinSmoothing, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.Smoothing, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.Smoothing)
|
||||
@Html.ValidationMessageFor(model => model.Settings.Smoothing, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.Linear, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.Linear)
|
||||
@Html.ValidationMessageFor(model => model.Settings.Linear, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.HasSin, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.HasSin)
|
||||
@Html.ValidationMessageFor(model => model.Settings.HasSin, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.SinLength, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.SinLength)
|
||||
@Html.ValidationMessageFor(model => model.Settings.SinLength, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.SinStrength, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.SinStrength)
|
||||
@Html.ValidationMessageFor(model => model.Settings.SinStrength, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.SinNegative, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.SinNegative)
|
||||
@Html.ValidationMessageFor(model => model.Settings.SinNegative, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.HasPeak, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.HasPeak)
|
||||
@Html.ValidationMessageFor(model => model.Settings.HasPeak, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.PeakLength, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.PeakLength)
|
||||
@Html.ValidationMessageFor(model => model.Settings.PeakLength, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.PeakStrength, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.PeakStrength)
|
||||
@Html.ValidationMessageFor(model => model.Settings.PeakStrength, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.RPeakInYear, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.RPeakInYear)
|
||||
@Html.ValidationMessageFor(model => model.Settings.RPeakInYear, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.LabelFor(model => model.Settings.PeakInYear, new {@class = "control-label col-md-1"}) @Html.EditorFor(model => model.Settings.PeakInYear)
|
||||
@Html.ValidationMessageFor(model => model.Settings.PeakInYear, "",new {@class = "text-danger"})
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -103,19 +134,7 @@ const myChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: @Model.LabelSet,
|
||||
datasets: [{
|
||||
label: 'Umsatz',
|
||||
data: @Model.DataSet,
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(255, 99, 132, 1)'
|
||||
],
|
||||
fill: false,
|
||||
tension: 0.4,
|
||||
spangaps: true
|
||||
}]
|
||||
datasets: @Html.Raw(@Model.DataSet)
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
|
||||
Reference in New Issue
Block a user