commit ce0940b5ca874c9062054b76e4af9e8b060edd90
Author: Roman Kalivoda <kalivoda@students.zcu.cz>
Date:   Fri Jun 4 11:40:42 2021 +0200

    Re #9034 fixing files corrupted by merge

diff --git a/Server/ServerApp/Connection/XMLProtocolHandler/Request.cs b/Server/ServerApp/Connection/XMLProtocolHandler/Request.cs
index 41f228e..0f314aa 100644
--- a/Server/ServerApp/Connection/XMLProtocolHandler/Request.cs
+++ b/Server/ServerApp/Connection/XMLProtocolHandler/Request.cs
@@ -1,8 +1,4 @@
 ﻿using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using System.Xml.Serialization;
 
 namespace ServerApp.Connection.XMLProtocolHandler
diff --git a/Server/ServerApp/Parser/Parsers/DataParser.cs b/Server/ServerApp/Parser/Parsers/DataParser.cs
index 223fdc6..28af53e 100644
--- a/Server/ServerApp/Parser/Parsers/DataParser.cs
+++ b/Server/ServerApp/Parser/Parsers/DataParser.cs
@@ -81,6 +81,13 @@ namespace ServerApp.Parser.Parsers
             var jisFiles = downloader.GetData(pathJis, start, end);
             var loginFiles = downloader.GetData(pathLogIn, start, end);
 
+            WeatherDataUsed = new List<string>();
+            ActivityDataUsed = new List<string>();
+
+            WeatherDataUsed.AddRange(weatherFiles);
+            ActivityDataUsed.AddRange(jisFiles);
+            ActivityDataUsed.AddRange(loginFiles);
+
             WeatherList = weatherParser.ParseWeatherData(weatherFiles, startTime, endTime, wholeDay, interval);
             jisList = jisParser.ParseJisData(jisFiles, startTime, endTime, wholeDay, interval);
             loginList = loginParser.ParseLogInData(loginFiles, startTime, endTime, wholeDay, interval);
diff --git a/Server/ServerApp/Parser/Parsers/IDataParser.cs b/Server/ServerApp/Parser/Parsers/IDataParser.cs
index ae67a00..886db4c 100644
--- a/Server/ServerApp/Parser/Parsers/IDataParser.cs
+++ b/Server/ServerApp/Parser/Parsers/IDataParser.cs
@@ -21,6 +21,15 @@ namespace ServerApp.Parser.Parsers
         List<ActivityInfo> attendanceList;
         public List<ActivityInfo> AttendanceList { get => attendanceList; internal set => attendanceList = value; }
 
+        /// <summary> List of weather file names the parser was last used on </summary>
+        List<String> weatherdataUsed;
+        public List<string> WeatherDataUsed { get => weatherdataUsed; set => weatherdataUsed = value; }
+
+        /// <summary> List of activity file names the parser was last used on </summary>
+        List<String> activitydataUsed;
+        public List<string> ActivityDataUsed { get => activitydataUsed; set => activitydataUsed = value; }
+
+
         /// <summary>
         /// Parse data
         /// </summary>
diff --git a/Server/ServerApp/Predictor/FeatureExtractor.cs b/Server/ServerApp/Predictor/FeatureExtractor.cs
index a019c71..0f95350 100644
--- a/Server/ServerApp/Predictor/FeatureExtractor.cs
+++ b/Server/ServerApp/Predictor/FeatureExtractor.cs
@@ -13,23 +13,26 @@ namespace ServerApp.Predictor
     /// <summary>
     /// A class responsible for preparation of features for classifiers.
     /// </summary>
-    public class FeatureExtractor
+    class FeatureExtractor
     {
         /// <summary>
         /// A DataParser instance used to access info objects.
         /// </summary>
-        private readonly IDataParser dataParser;
+        private readonly IDataParser DataParser;
 
-        private Dictionary<string, int> buildingsToAreas;
+        /// <summary>
+        /// A configuration object of the <c>Predictor</c> package
+        /// </summary>
+        private PredictorConfiguration Configuration;
 
         /// <summary>
         /// Instantiates new FeatureExtractor class.
         /// </summary>
         /// <param name="dataParser">Data parser used to access training data.</param>
-        public FeatureExtractor(IDataParser dataParser, Dictionary<string, int> buildingsToAreas)
+        public FeatureExtractor(IDataParser dataParser, PredictorConfiguration configuration)
         {
-            this.dataParser = dataParser;
-            this.buildingsToAreas = buildingsToAreas;
+            this.DataParser = dataParser;
+            this.Configuration = configuration;
         }
 
         /// <summary>
@@ -41,13 +44,12 @@ namespace ServerApp.Predictor
         /// <param name="interval"></param>
         /// <param name="wholeDay"></param>
         /// <returns></returns>
-        public List<ModelInput> PrepareTrainingInput(int area, DateTime startDate, DateTime endDate, int interval = 1, bool wholeDay = true)
+        public List<ModelInput> PrepareTrainingInput(int area)
         {
-            dataParser.Parse(startDate, endDate, interval, wholeDay);
             List<string> buildings = new List<string>();
 
             // find all buildings in area
-            foreach (KeyValuePair<string, int> kvp in buildingsToAreas)
+            foreach (KeyValuePair<string, int> kvp in Configuration.BuildingsToAreas)
             {
                 if (kvp.Value == area)
                 {
@@ -56,18 +58,19 @@ namespace ServerApp.Predictor
             }
 
             var res = new List<ModelInput>();
-            foreach (WeatherInfo val in dataParser.WeatherList)
+            foreach (WeatherInfo val in DataParser.WeatherList)
             {
                 res.Add(new ModelInput
                 {
-                    Temp = (float)val.temp,
                     Time = val.startTime,
+                    Temp = (float)val.temp,
+                    Hour = val.startTime.Hour,
                     Wind = (float)val.wind,
                     Rain = (float)val.rain,
                 });
             }
 
-            List<ActivityInfo> attendance = dataParser.AttendanceList;
+            List<ActivityInfo> attendance = DataParser.AttendanceList;
             foreach (ModelInput input in res)
             {
                 List<int> amounts = new List<int>();
@@ -90,62 +93,81 @@ namespace ServerApp.Predictor
             if (ratio < 0.1f)
             {
                 return "10%";
-            } else if (ratio < 0.2f)
+            }
+            else if (ratio < 0.2f)
             {
                 return "20%";
-            } else if (ratio < 0.3f)
+            }
+            else if (ratio < 0.3f)
             {
                 return "30%";
-            } else if (ratio < 0.4f)
+            }
+            else if (ratio < 0.4f)
             {
                 return "40%";
-            } else if (ratio < 0.5f)
+            }
+            else if (ratio < 0.5f)
             {
                 return "50%";
-            } else if (ratio < 0.6f)
+            }
+            else if (ratio < 0.6f)
             {
                 return "60%";
-            } else if(ratio < 0.7f) {
+            }
+            else if (ratio < 0.7f)
+            {
                 return "70%";
-            } else if (ratio < 0.8f)
+            }
+            else if (ratio < 0.8f)
             {
                 return "80%";
-            } else if (ratio < 0.9f)
+            }
+            else if (ratio < 0.9f)
             {
                 return "90%";
-            } else
+            }
+            else
             {
                 return "100%";
             }
         }
 
-        private double LabelToRatio(string label)
+        internal double LabelToRatio(string label)
         {
-            if (label.Equals("10%")) {
+            if (label.Equals("10%"))
+            {
                 return 0.1f;
             }
-            else if (label.Equals("20%"))  {
+            else if (label.Equals("20%"))
+            {
                 return 0.2f;
             }
-            else if (label.Equals("30%")) {
+            else if (label.Equals("30%"))
+            {
                 return 0.3f;
             }
-            else if (label.Equals("40%")) {
+            else if (label.Equals("40%"))
+            {
                 return 0.4f;
             }
-            else if (label.Equals("50%")) {
+            else if (label.Equals("50%"))
+            {
                 return 0.5f;
             }
-            else if (label.Equals("60%")) {
+            else if (label.Equals("60%"))
+            {
                 return 0.6f;
             }
-            else if (label.Equals("70%")) {
+            else if (label.Equals("70%"))
+            {
                 return 0.7f;
             }
-            else if (label.Equals("80%")) {
+            else if (label.Equals("80%"))
+            {
                 return 0.8f;
             }
-            else if (label.Equals("90%")) {
+            else if (label.Equals("90%"))
+            {
                 return 0.9f;
             }
             else
diff --git a/Server/ServerApp/Predictor/IPredictionController.cs b/Server/ServerApp/Predictor/IPredictionController.cs
index 023e3d8..a434a2b 100644
--- a/Server/ServerApp/Predictor/IPredictionController.cs
+++ b/Server/ServerApp/Predictor/IPredictionController.cs
@@ -2,12 +2,8 @@
 // Author: Roman Kalivoda
 //
 
-using System;
 using System.Collections.Generic;
-using System.IO;
-using Microsoft.ML;
 using ServerApp.Connection.XMLProtocolHandler;
-using ServerApp.Parser.OutputInfo;
 
 namespace ServerApp.Predictor
 {
@@ -32,7 +28,7 @@ namespace ServerApp.Predictor
         /// </summary>
         /// <param name="locationKey">A string identifier of the location for which to load a predictor.</param>
         /// <param name="path">A path to folder with trained prediction model.</param>
-        void Load(string locationKey = null,string path = null);
+        void Load(string locationKey = null, string path = null);
 
         /// <summary>
         /// Predicts turnout level at given time supposing given weather conditions.
diff --git a/Server/ServerApp/Predictor/ModelInput.cs b/Server/ServerApp/Predictor/ModelInput.cs
index 576dc0a..45b94e1 100644
--- a/Server/ServerApp/Predictor/ModelInput.cs
+++ b/Server/ServerApp/Predictor/ModelInput.cs
@@ -13,6 +13,11 @@ namespace ServerApp.Predictor
     /// </summary>
     public class ModelInput
     {
+        /// <summary>
+        /// Start time of the information.
+        /// </summary>
+        public DateTime Time { get; set; }
+
         /// <summary>
         /// A label of this training input.
         /// </summary>
@@ -26,19 +31,19 @@ namespace ServerApp.Predictor
         public float Temp { get; set; }
 
         /// <summary>
-        /// Time of the predicted turnout.
+        /// Hour of the predicted turnout.
         /// </summary>
-        [ColumnName("Time"), LoadColumn(2)]
-        public DateTime Time { get; set; }
+        [ColumnName("Hour"), LoadColumn(2)]
+        public int Hour { get; set; }
 
         /// <summary>
-        /// Wind velocity in ? units
+        /// Wind velocity in m/s
         /// </summary>
         [ColumnName("Wind"), LoadColumn(3)]
         public float Wind { get; set; }
 
         /// <summary>
-        /// Precipitation
+        /// Precipitation in %
         /// </summary>
         [ColumnName("Rain"), LoadColumn(4)]
         public float Rain { get; set; }
diff --git a/Server/ServerApp/Predictor/ModelOutput.cs b/Server/ServerApp/Predictor/ModelOutput.cs
index ace5084..5d8984b 100644
--- a/Server/ServerApp/Predictor/ModelOutput.cs
+++ b/Server/ServerApp/Predictor/ModelOutput.cs
@@ -2,7 +2,6 @@
 // Author: Roman Kalivoda
 //
 
-using System;
 using Microsoft.ML.Data;
 
 namespace ServerApp.Predictor
@@ -16,7 +15,7 @@ namespace ServerApp.Predictor
         /// A predicted class.
         /// </summary>
         [ColumnName("PredictedLabel")]
-        public String Prediction { get; set; }
+        public string PredictedLabel { get; set; }
 
     }
 }
diff --git a/Server/ServerApp/Predictor/NaiveBayesClassifier.cs b/Server/ServerApp/Predictor/NaiveBayesClassifier.cs
index 9d74fb6..fc657bc 100644
--- a/Server/ServerApp/Predictor/NaiveBayesClassifier.cs
+++ b/Server/ServerApp/Predictor/NaiveBayesClassifier.cs
@@ -44,11 +44,13 @@ namespace ServerApp.Predictor
         {
             this._trainingDataView = _mlContext.Data.LoadFromEnumerable(trainInput);
             var pipeline = _mlContext.Transforms.Conversion.MapValueToKey(nameof(ModelInput.Label))
-                .Append(_mlContext.Transforms.Concatenate("Features", new[] { "Temp" }))
-                .Append(_mlContext.Transforms.NormalizeMinMax("Features", "Features"))
+                .Append(_mlContext.Transforms.Conversion.ConvertType(nameof(ModelInput.Hour)))
+                .Append(_mlContext.Transforms.Concatenate("Features", 
+                new[] { nameof(ModelInput.Temp), nameof(ModelInput.Rain), nameof(ModelInput.Wind), nameof(ModelInput.Hour) }))
+                .Append(_mlContext.Transforms.NormalizeMeanVariance("Features", useCdf:false))
                 .AppendCacheCheckpoint(_mlContext)
                 .Append(_mlContext.MulticlassClassification.Trainers.NaiveBayes())
-                .Append(_mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel")); ;
+                .Append(_mlContext.Transforms.Conversion.MapKeyToValue(nameof(ModelOutput.PredictedLabel)));
 
             var cvResults = _mlContext.MulticlassClassification.CrossValidate(this._trainingDataView, pipeline);
             _log.Debug("Cross-validated the trained model");
@@ -67,7 +69,8 @@ namespace ServerApp.Predictor
         public void Evaluate(IEnumerable<ModelInput> modelInputs)
         {
             var testDataView = this._mlContext.Data.LoadFromEnumerable(modelInputs);
-            var testMetrics = _mlContext.MulticlassClassification.Evaluate(_trainedModel.Transform(testDataView));
+            var data = _trainedModel.Transform(testDataView);
+            var testMetrics = _mlContext.MulticlassClassification.Evaluate(data);
 
             Console.WriteLine($"*************************************************************************************************************");
             Console.WriteLine($"*       Metrics for Multi-class Classification model - Test Data     ");
@@ -76,6 +79,7 @@ namespace ServerApp.Predictor
             Console.WriteLine($"*       MacroAccuracy:    {testMetrics.MacroAccuracy:0.###}");
             Console.WriteLine($"*       LogLoss:          {testMetrics.LogLoss:#.###}");
             Console.WriteLine($"*       LogLossReduction: {testMetrics.LogLossReduction:#.###}");
+            Console.WriteLine($"*       Confusion Matrix: {testMetrics.ConfusionMatrix.GetFormattedConfusionTable()}");
             Console.WriteLine($"*************************************************************************************************************");
         }
     }
diff --git a/Server/ServerApp/Predictor/PredictionController.cs b/Server/ServerApp/Predictor/PredictionController.cs
index 9a43fcb..84481f5 100644
--- a/Server/ServerApp/Predictor/PredictionController.cs
+++ b/Server/ServerApp/Predictor/PredictionController.cs
@@ -4,9 +4,10 @@
 
 using System;
 using System.Collections.Generic;
-using System.IO;
-using Microsoft.ML;
 using ServerApp.Connection.XMLProtocolHandler;
+using ServerApp.Parser.Parsers;
+using Newtonsoft.Json;
+using ServerApp.WeatherPredictionParser;
 using ServerApp.Parser.OutputInfo;
 using System.Reflection;
 using log4net;
@@ -16,32 +17,37 @@ namespace ServerApp.Predictor
     /// <summary>
     /// Implentation of the <c>IPredicitionController</c> interface.
     /// </summary>
-    class PredictionController : IPredictionController
+    public class PredictionController : IPredictionController
     {
         private static readonly ILog _log = LogManager.GetLogger(typeof(PredictionController));
 
         /// <summary>
-        /// A dictionary for storing trained predictors.
+        /// Configuration of the <c>Predictor</c>
         /// </summary>
-        private Dictionary<string, int> buildingsToAreas;
+        private PredictorConfiguration Configuration;
 
-        private List<IPredictor> predictors;
+        private List<IPredictor> Predictors;
 
         /// <summary>
         /// A reference to a data parser.
         /// </summary>
-        private IDataParser dataParser;
+        private IDataParser DataParser;
 
         /// <summary>
         /// A feature extractor instance.
         /// </summary>
-        private FeatureExtractor featureExtractor;
+        private FeatureExtractor FeatureExtractor;
+
+        /// <summary>
+        /// A weather prediction parser service
+        /// </summary>
+        private IJsonParser weatherService;
 
         /// <summary>
         /// Instantiates new prediction controller.
         /// </summary>
         /// <param name="dataParser">A data parser used to get training data.</param>
-        public PredictionController(IDataParser dataParser)
+        public PredictionController(IJsonParser weatherService, IDataParser dataParser, string pathToConfig = null)
         {
             _log.Info("Constructing a new PredictionController instance.");
             this.weatherService = weatherService;
@@ -68,14 +74,13 @@ namespace ServerApp.Predictor
 
             for (int i = 0; i < this.Configuration.PredictorCount; i++)
             {
-                buildingsToAreas.Add(key, 0);
+                Predictors.Add(new NaiveBayesClassifier());
             }
-            IPredictor predictor = new NaiveBayesClassifier();
-            predictors.Add(predictor);
+            PredictorConfiguration.SaveConfig(PredictorConfiguration.DEFAULT_CONFIG_PATH, Configuration);
         }
         public List<string> GetPredictors()
         {
-            return new List<string>(buildingsToAreas.Keys);
+            return new List<string>(this.Configuration.BuildingsToAreas.Keys);
         }
 
         public void Load(string locationKey = null, string path = null)
@@ -180,21 +185,16 @@ namespace ServerApp.Predictor
             if (locationKey is null)
             // train all predictors
             {
-                // TODO A single predictor is used for all areas, so training is done only once now.
-                for (int i = 0; i < this.predictors.Count; i++)
+                DataParser.Parse(DateTime.MinValue, DateTime.MaxValue, this.Configuration.TimeResolution, wholeDay: false);
+                for (int i = 0; i < this.Predictors.Count; i++)
                 {
                     // train on all available data
-                    // TODO the train/test split is used just temporarily for demonstration.
-                    List<ModelInput> data = featureExtractor.PrepareTrainingInput(i, DateTime.MinValue, DateTime.MaxValue);
-                    List<ModelInput> trainingData = data.GetRange(index: 0, count: 500);
-                    List<ModelInput> testData = data.GetRange(index: 500, count: 94);
-                    Console.WriteLine("Training predictor with {0} samples.", trainingData.Count);
-                    this.predictors[i].Fit(trainingData);
-
-                    Console.WriteLine("Evaluating predictor with {0} samples.", testData.Count);
-                    this.predictors[i].Evaluate(testData);
+                    List<ModelInput> data = FeatureExtractor.PrepareTrainingInput(i);
+                    Console.WriteLine("Training predictor with {0} samples.", data.Count);
+                    this.Predictors[i].Fit(data);
                 }
-            } else
+            }
+            else
             // train specified predictor only
             {
                 throw new NotImplementedException();
diff --git a/Server/ServerApp/WeatherPredictionParser/IJsonParser.cs b/Server/ServerApp/WeatherPredictionParser/IJsonParser.cs
index 31c8058..9659bb6 100644
--- a/Server/ServerApp/WeatherPredictionParser/IJsonParser.cs
+++ b/Server/ServerApp/WeatherPredictionParser/IJsonParser.cs
@@ -3,6 +3,7 @@
 //
 
 using ServerApp.Parser.OutputInfo;
+using System;
 using System.Collections.Generic;
 
 namespace ServerApp.WeatherPredictionParser
@@ -10,12 +11,12 @@ namespace ServerApp.WeatherPredictionParser
     /// <summary>
     /// Abstract class that every Json parser should inherit from
     /// </summary>
-    abstract class IJsonParser
+    public abstract class IJsonParser
     {
 
         /// <summary> Current weather </summary>
         WeatherInfo current;
-        public WeatherInfo Current { get => current; }
+        public WeatherInfo Current { get => current; set => current = value; }
 
         /// <summary> Prediction for today, tommorrow and day after tommorrow </summary>
         List<WeatherInfo> predictions;
@@ -27,6 +28,14 @@ namespace ServerApp.WeatherPredictionParser
         /// </summary>
         abstract public void ParsePrediction();
 
+        /// <summary>
+        /// Get predictions from Predictions that are within specified time span
+        /// </summary>
+        /// <param name="from">DateTime from</param>
+        /// <param name="to">DateTime to</param>
+        /// <returns>List of predictions that fit specified criteria</returns>
+        abstract public List<WeatherInfo> GetPredictionForTime(DateTime from, DateTime to);
+
     }
 
 }
diff --git a/Server/ServerApp/WeatherPredictionParser/JsonParser.cs b/Server/ServerApp/WeatherPredictionParser/JsonParser.cs
index 746f9d4..51dfb3e 100644
--- a/Server/ServerApp/WeatherPredictionParser/JsonParser.cs
+++ b/Server/ServerApp/WeatherPredictionParser/JsonParser.cs
@@ -119,8 +119,8 @@ namespace ServerApp.WeatherPredictionParser
 
             DateTime now = DateTime.Now;
 
-            current = new WeatherInfo();
-            predictions = new List<WeatherInfo>();
+            Current = new WeatherInfo();
+            Predictions = new List<WeatherInfo>();
 
             // parse
             JsonDocument doc = JsonDocument.Parse(data);
@@ -138,7 +138,7 @@ namespace ServerApp.WeatherPredictionParser
                     case "current_condition":
                         {
                             ArrayEnumerator currentWeather = weatherP.Current.Value.EnumerateArray();
-                            current = ParseCurrentWeather(currentWeather);
+                            Current = ParseCurrentWeather(currentWeather);
 
                             break;
                         }
@@ -164,8 +164,8 @@ namespace ServerApp.WeatherPredictionParser
         /// </summary>
         private void TestConsoleOutput()
         {
-            Console.WriteLine(current);
-            foreach (WeatherInfo w in predictions)
+            Console.WriteLine(Current);
+            foreach (WeatherInfo w in Predictions)
                 Console.WriteLine(w);
         }
 
@@ -477,5 +477,6 @@ namespace ServerApp.WeatherPredictionParser
             return res;
         }
 
+      
     }
 }
