diff --git a/MarketDataLib/MarketDataModel/EconomicIndicator.cs b/MarketDataLib/MarketDataModel/EconomicIndicator.cs new file mode 100644 index 0000000..ee09633 --- /dev/null +++ b/MarketDataLib/MarketDataModel/EconomicIndicator.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MarketData.MarketDataModel +{ + public class EconomicIndicators : List + { + public EconomicIndicators() + { + } + + public EconomicIndicators(List economicIndicators) + { + foreach(EconomicIndicator economicIndicator in economicIndicators) + { + Add(economicIndicator); + } + } + + public List GetYears() + { + if(0==Count)return null; + List years = (from EconomicIndicator economicIndicator in this select economicIndicator.Year).Distinct().ToList(); + years.Sort(); + return years; + } + + public Dictionary GetDictionary() + { + Dictionary dictionary=new Dictionary(); + if(0==Count)return null; + for (int index = 0; index < Count; index++) + { + EconomicIndicator economicIndicator=this[index]; + if(!dictionary.ContainsKey(economicIndicator.CountryCode)) + { + List list=new List(); + dictionary.Add(economicIndicator.CountryCode,new EconomicIndicators()); + } + EconomicIndicators economicIndicators=dictionary[economicIndicator.CountryCode]; + economicIndicators.Add(economicIndicator); + } + List keys=new List(dictionary.Keys); + foreach(String key in keys) + { + dictionary[key].Sort(new EconomicIndicatorByYear()); + } + return dictionary; + } + } + + public class EconomicIndicatorByYear : IComparer + { + public int Compare(EconomicIndicator p1,EconomicIndicator p2) + { + return p1.Year.CompareTo(p2.Year); + } + } + + public class EconomicIndicator + { + public String Source{get;set;} + public String CountryName{get;set;} + public String CountryCode{get;set;} + public String IndicatorName{get;set;} + public String IndicatorCode{get;set;} + public double IndicatorValue{get;set;} + public int Year{get;set;} + } +} diff --git a/Navigator/Renderers/EconomicIndicatorsRenderer.cs b/Navigator/Renderers/EconomicIndicatorsRenderer.cs new file mode 100644 index 0000000..7de6f43 --- /dev/null +++ b/Navigator/Renderers/EconomicIndicatorsRenderer.cs @@ -0,0 +1,133 @@ +using DataDisplay.Common; +using DataDisplay.DataSource; +using DataDisplay.Graph; +using DataDisplay.Renderers; +using MarketData.Utils; +using SkiaSharp; +using System; + +namespace Navigator.Renderers +{ + public class EconomicIndicatorsRenderer : IRenderer + { + private SKPaint paintSeries=new SKPaint{Style=SKPaintStyle.StrokeAndFill,Color=new SKColor((uint)SKColors.Black),StrokeWidth=5f}; + private LineGraph economicIndicatorsLineGraph; + + public event EventHandler RefreshRequested; + + public void Refresh() + { + RefreshRequested?.Invoke(this, EventArgs.Empty); + } + + public double XDataExtent{get;set;} + public double XDataExtentMin{get;set;} + public double XRange{get{return XDataExtent-XDataExtentMin;}} + public double YDataExtent{get;set;} + public double YDataExtentMin{get;set;} + public double YRange{get{return YDataExtent-YDataExtentMin;}} + + public CompositeDataSource EconomicIndicatorElements + { + set + { + if(null==economicIndicatorsLineGraph)economicIndicatorsLineGraph=new LineGraph(paintSeries,value); + else economicIndicatorsLineGraph.SetDataSource(value); + } + } + public bool AnyData() + { + if(null==economicIndicatorsLineGraph)return false; + return true; + } + + public void SetColorRed() + { + if(!AnyData())return; + paintSeries.Color=new SKColor((uint)SKColors.Red); + } + + public void SetColorGreen() + { + if(!AnyData())return; + paintSeries.Color=new SKColor((uint)SKColors.Green); + } + + public void SetColorBlack() + { + if(!AnyData())return; + paintSeries.Color=new SKColor((uint)SKColors.Black); + } + + // ************************************************************************************************************************************ + // ********************************************************* R E N D E R E R ********************************************************* + // ************************************************************************************************************************************ + public void PaintSurface(SKSurface surface,SKImageInfo imageInfo) + { + SKCanvas canvas = surface.Canvas; + canvas.Clear(); + if(!AnyData())return; + + int width=imageInfo.Width; + int height=imageInfo.Height; + + XDataExtent=GetXDataExtent(); + YDataExtent=GetYDataExtent(); + XDataExtentMin=GetXDataExtentMin(); + YDataExtentMin=GetYDataExtentMin(); + + PointMapping pointMapping=new PointMapping(width,height,XDataExtent,XDataExtentMin,YDataExtent,YDataExtentMin,80,50); + + GridLines gridLines=new GridLines(10,10); + gridLines.AssignXDataType=GetXDataType; + gridLines.AssignGetYDataType=GetYDataType; + gridLines.AssignYDataFormatter=YDataFormatter; + gridLines.Render(canvas,pointMapping); + + if(null!=economicIndicatorsLineGraph)economicIndicatorsLineGraph.Render(canvas,pointMapping); + } + + private double GetXDataExtent() + { + if(null==economicIndicatorsLineGraph)return 0.00; + return economicIndicatorsLineGraph.GetXExtent(); + } + + private double GetYDataExtent() + { + if(null==economicIndicatorsLineGraph)return 0.00; + double maxDataExtent=economicIndicatorsLineGraph.GetYExtent(); + return maxDataExtent; + } + + // custom grid label formatter for the data along the left side of the chart. We know a-priori that this is of type double. + private String YDataFormatter(Object data) + { + Type dataType=data.GetType(); + String strValue=Utility.FormatNumberConstrain((double)data); + return strValue; + } + + private double GetXDataExtentMin() + { + if(null==economicIndicatorsLineGraph)return 0.00; + return economicIndicatorsLineGraph.GetXExtentMin(); + } + + private double GetYDataExtentMin() + { + if(null==economicIndicatorsLineGraph)return 0.00; + return economicIndicatorsLineGraph.GetYExtentMin(); + } + + private Type GetXDataType() + { + return typeof(DateTime); + } + + private Type GetYDataType() + { + return typeof(double); + } + } + } diff --git a/Navigator/ViewModels/EconomicIndicatorsViewModel.cs b/Navigator/ViewModels/EconomicIndicatorsViewModel.cs new file mode 100644 index 0000000..9cec85c --- /dev/null +++ b/Navigator/ViewModels/EconomicIndicatorsViewModel.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Threading.Tasks; +using System.Windows.Input; +using MarketData.MarketDataModel; +using MarketData.Service; +using Navigator.Core; +using DataDisplay.Renderers; +using Navigator.MarketDataModel; +using Navigator.Renderers; + +namespace Navigator.ViewModels +{ + public class EconomicIndicatorsViewModel : WorkspaceViewModel + { + private RelayCommand refreshCommand; + private bool isRefreshing = false; + private EconomicIndicatorsRenderer economicIndicatorsRenderer=new EconomicIndicatorsRenderer(); + private String selectedCode; + private EconomicIndicators economicIndicators = default; + private String chartTitle; + private ObservableCollection codeCollection; + private bool showActivity = true; + + public EconomicIndicatorsViewModel() + { + Title = "Consumer Price Index"; + Initialize(); + PropertyChanged += OnViewModelPropertyChanged; + } + private void OnViewModelPropertyChanged(object sender,PropertyChangedEventArgs eventArgs) + { + if (eventArgs.PropertyName.Equals("SelectedCode")) + { + if (null == selectedCode) return; + HandleSelectedCodeRequest(); + } + } + private void Initialize() + { + Task workerTask=Task.Factory.StartNew(()=> + { + IEnumerable codes=null; + ServiceResult serviceResult=MarketDataServiceClient.GetInstance().GetDistinctEconomicIndicatorCodes(); + if(serviceResult.Success)codes=(List)serviceResult.ContextSpecificResult; + CodeCollection=new ObservableCollection(codes); + }); + workerTask.ContinueWith((continuation)=> + { + }); + } + + public void HandleSelectedCodeRequest() + { + ServiceResult serviceResult = null; + IsBusy=true; + ShowActivity=!IsRefreshing; + Task workerTask = Task.Factory.StartNew(() => + { + serviceResult = MarketDataServiceClient.GetInstance().GetGetEconomicIndicators(selectedCode); + if (serviceResult.Success) + { + ChartTitle = selectedCode; + economicIndicators = new EconomicIndicators((List)serviceResult.ContextSpecificResult); + if(null!=economicIndicators && economicIndicators.Count>0) + { + economicIndicatorsRenderer.EconomicIndicatorElements=EconomicIndicatorsModel.Elements(economicIndicators); + economicIndicatorsRenderer.SetColorBlack(); + economicIndicatorsRenderer.Refresh(); + } + } + }); + workerTask.ContinueWith((TaskContinuationOptions) => + { + IsBusy=false; + IsRefreshing=false; + ShowActivity=!IsRefreshing; + }); + } + public ObservableCollection CodeCollection + { + get { return codeCollection; } + set { SetProperty(ref codeCollection, value); } + } + public IRenderer Renderer + { + get{return economicIndicatorsRenderer;} + } + + public String SelectedCode + { + get { return selectedCode; } + set { SetProperty(ref selectedCode, value); } + } + public String ChartTitle + { + get { return chartTitle; } + set { SetProperty(ref chartTitle, value); } + } + public bool IsRefreshing + { + get{return isRefreshing;} + set{SetProperty(ref isRefreshing,value);} + } + public bool ShowActivity + { + get {return showActivity;} + set {SetProperty(ref showActivity,value);} + } + public ICommand RefreshCommand + { + get + { + if(null==refreshCommand) + { + refreshCommand=new RelayCommand(param=> + { + HandleSelectedCodeRequest(); + },param=>{return !IsRefreshing;}); + } + return refreshCommand; + } + } + } +} + diff --git a/Navigator/Views/EconomicIndicatorsPage.xaml b/Navigator/Views/EconomicIndicatorsPage.xaml new file mode 100644 index 0000000..88b5d1e --- /dev/null +++ b/Navigator/Views/EconomicIndicatorsPage.xaml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Navigator/Views/EconomicIndicatorsPage.xaml.cs b/Navigator/Views/EconomicIndicatorsPage.xaml.cs new file mode 100644 index 0000000..cc2a94d --- /dev/null +++ b/Navigator/Views/EconomicIndicatorsPage.xaml.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +namespace Navigator.Views +{ + [XamlCompilation(XamlCompilationOptions.Compile)] + public partial class EconomicIndicatorsPage : ContentPage + { + public EconomicIndicatorsPage() + { + InitializeComponent(); + } + } +} \ No newline at end of file