diff --git a/FarmmapsApiSamples.sln b/FarmmapsApiSamples.sln index 751b624..8f35ae1 100644 --- a/FarmmapsApiSamples.sln +++ b/FarmmapsApiSamples.sln @@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsCleanUp", "Farmmaps EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsKPI", "FarmmapsKPI\FarmmapsKPI.csproj", "{14575235-9867-4CE5-A22F-3F9FE002FF42}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsDownloadCL", "FarmmapsDownloadCL\FarmmapsDownloadCL.csproj", "{63E69101-D804-4DBC-B2E4-A33771CD5C5F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -137,6 +139,14 @@ Global {14575235-9867-4CE5-A22F-3F9FE002FF42}.Release|Any CPU.Build.0 = Release|Any CPU {14575235-9867-4CE5-A22F-3F9FE002FF42}.Release|x64.ActiveCfg = Release|x64 {14575235-9867-4CE5-A22F-3F9FE002FF42}.Release|x64.Build.0 = Release|x64 + {63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Debug|x64.ActiveCfg = Debug|x64 + {63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Debug|x64.Build.0 = Debug|x64 + {63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Release|Any CPU.Build.0 = Release|Any CPU + {63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Release|x64.ActiveCfg = Release|x64 + {63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FarmmapsDownloadCL/DownloadCLApplication.cs b/FarmmapsDownloadCL/DownloadCLApplication.cs new file mode 100644 index 0000000..9b53ea0 --- /dev/null +++ b/FarmmapsDownloadCL/DownloadCLApplication.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using FarmmapsApi; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using FarmmapsDownloadCL.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using static FarmmapsApiSamples.Constants; + +namespace FarmmapsDownloadCL +{ + public class DownloadCLApplication : IApplication + { + + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly DownloadCLService _DownloadCLService; + private readonly GeneralService _generalService; + public readonly Dictionary> _dictCl; + string _itemcode; + public DownloadCLApplication(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService, DownloadCLService DownloadCLService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + _DownloadCLService = DownloadCLService; + CodelistsClasses clc = new CodelistsClasses(); + _itemcode = clc.itemcode; + _dictCl = clc.dictCl; + } + + public async Task RunAsync() + { + var fieldsInputJson = File.ReadAllText("DownloadCLInput.json"); + + + DownloadCLInput clInput = JsonConvert.DeserializeObject(fieldsInputJson); + + // !! this call is needed the first time an api is called with a fresh clientid and secret !! + await _farmmapsApiService.GetCurrentUserCodeAsync(); + var roots = await _farmmapsApiService.GetCurrentUserRootsAsync(); + + //Where to write the output + string downloadFolder = clInput.DownloadFolder; + if (string.IsNullOrEmpty(downloadFolder)) + { + downloadFolder = "Downloads"; + } + if (!Directory.Exists(downloadFolder)) + Directory.CreateDirectory(downloadFolder); + + //Get the most recent codelists + foreach (string codelist in clInput.codelists) + { + try + { + await Process(roots, codelist, downloadFolder); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + } + + private async Task Process(List roots, string codelistname, string downloadFolder) + { + List codelist; + string itemtype; + string className; + string body; + string header; + string value; + string[] aboutArray = null; + string[] headerArray = null; + PropertyInfo[] props; + PropertyInfo prop; + List dataList; + string clJson = Path.Combine(downloadFolder, $"{codelistname}.json"); + string clCsv = Path.Combine(downloadFolder, $"{codelistname}.csv"); + + try + { + itemtype = _dictCl[codelistname][0]; + className = _dictCl[codelistname][1]; + codelist = await _farmmapsApiService.GetItemChildrenAsync(_itemcode, itemtype); + + //Write full codelist in json format to clJson. + body = JsonConvert.SerializeObject(codelist); + File.WriteAllText(clJson, body); + _logger.LogInformation($"Downloaded file {clJson}"); + + //Write full list in csv format to clCsv. + StreamWriter sw = new StreamWriter(clCsv); + //Write metadata in top: when downloaded + sw.WriteLine($"Editeelt codelist {codelistname} downloaded on {DateTime.Now} with the FarmmapsDownloadCL application by Pepijn van Oort (WPR)"); + + //Generic, for any Codelist as long as it is also defined in FarmmapsDownloadCL.Models in + string typerequest = "FarmmapsDownloadCL.Models." + className + ", FarmmapsDownloadCL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"; + Type objType = Type.GetType(typerequest); + object clitem = Activator.CreateInstance(objType); + props = clitem.GetType().GetProperties(); + + //Lookup about info and write to top + foreach (PropertyInfo pi in props) + { + if (pi.Name == "about") + { + aboutArray = (string[])pi.GetValue(clitem); + foreach (string about in aboutArray) + { + //Add quotes + value = "\"" + about + "\""; + //Write each about to a separate line in the csv file + sw.WriteLine(value); + } + + } + } + //Write an empty line + sw.WriteLine(); + + //Look up the headerArray and write the header comma separated + int i = 0; + while (headerArray == null) + { + prop = props[i]; + if (prop.Name == "headers") + { + headerArray = (string[])prop.GetValue(clitem); + } + i++; + } + header = string.Join(",", headerArray); + sw.WriteLine(header); + + //Add quotes because a value for field 'description' could be '21% Cl, 16.5%CaO, 1330 g/l' and without the quotes that + //would show up as separate columns. So per definition we will add quotes, except if we have a fieldname in the listNotToQuote, then value for that fieldname will not be given quotes + //e.g. not code "3" but code 3. + List listNotToQuote = new List() { "code", "n", "p", "k", "quantity", "cropGroupCode", "cultivationGroupCode" }; + //Loop through all items in the codelist + foreach (Item item in codelist) + { + dataList = new List { }; + clitem = JsonConvert.DeserializeObject(item.Data.ToString(), objType); + + //Add values of the cLitem to the dataList in the same order as the header + foreach (string h in headerArray) + { + value = (string)clitem.GetType().GetProperty(h).GetValue(clitem, null); + if(listNotToQuote.Contains(h) == false) + value = "\"" +value + "\""; + + dataList.Add(value); + } + string dataLine = string.Join(",", dataList); + sw.WriteLine(string.Join(",", dataList)); + } + + sw.Close(); + _logger.LogInformation($"Downloaded file {clCsv}"); + + } + catch + { + _logger.LogWarning($"Missing lookup information on codelist {codelistname}"); + } + } + } +} diff --git a/FarmmapsDownloadCL/DownloadCLInput.json b/FarmmapsDownloadCL/DownloadCLInput.json new file mode 100644 index 0000000..8b5c9c6 --- /dev/null +++ b/FarmmapsDownloadCL/DownloadCLInput.json @@ -0,0 +1,4 @@ +{ + "codeLists": [ "CL022", "CL127", "CL263", "CL042" ] + //"DownloadFolder": "Downloads", //"C:\\hugoschrererdir\\kpidir\\", // "Downloads", -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ +} \ No newline at end of file diff --git a/FarmmapsDownloadCL/FarmmapsDownloadCL.csproj b/FarmmapsDownloadCL/FarmmapsDownloadCL.csproj new file mode 100644 index 0000000..a4d4a51 --- /dev/null +++ b/FarmmapsDownloadCL/FarmmapsDownloadCL.csproj @@ -0,0 +1,24 @@ + + + + Exe + netcoreapp3.1 + x64 + AnyCPU;x64 + + + + + + + + + + + + + Always + + + + diff --git a/FarmmapsDownloadCL/Models/CodelistsClasses.cs b/FarmmapsDownloadCL/Models/CodelistsClasses.cs new file mode 100644 index 0000000..22f9c4a --- /dev/null +++ b/FarmmapsDownloadCL/Models/CodelistsClasses.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FarmmapsDownloadCL.Models +{ + public class CodelistsClasses + { + public CodelistsClasses() + { + this.itemcode = "938c0aec4efc46e88783fd029f17309e%3ASYSTEM_DATA"; + this.dictCl = new Dictionary>(); + //Add here once you have defined a new CLxxxitem below + //To add a new item, look it up in https://test.farmmaps.eu/swagger/index.html. Download the item you are interested in (like "vnd.farmmaps.itemtype.codelist.cl263") + //then below create a new CLxxxitem with fieldnames as you see them in the just downloaded new codelist + this.dictCl.Add("CL022", new List { "vnd.farmmaps.itemtype.codelist.cl022", "CL022item" }); + this.dictCl.Add("CL127", new List { "vnd.farmmaps.itemtype.codelist.cl127", "CL127item" }); + this.dictCl.Add("CL263", new List { "vnd.farmmaps.itemtype.codelist.cl263", "CL263item" }); + } + public string itemcode; + //Define here the codelist names, their item type and the class defining the contents of the data type (and defined in the Models.CodelistsClasses) + public Dictionary> dictCl; + } + + public class CL022item + { + public CL022item() + { + this.headers = new string[] { "codelist","code", "description", "type","k", "n","p","composition" }; + this.codelist = "CL022"; + this.about = new string[] { "EDI-Crop, coderingslijst meststoffen", "type MOR = organische meststof; MAN = anorganische meststof" }; + } + + public string[] headers { get; } + public string codelist { get; } + public string[] about { get; } + public string k { get; set; } + public string n { get; set; } + public string p { get; set; } + public string code { get; set; } + public string type { get; set; } + public string composition { get; set; } + public string description { get; set; } + + } + public class CL127item + { + public CL127item() + { + this.headers = new string[] { "codelist", "code", "description", "description_nl", "culturalPracticeLevel1", "culturalPracticeLevel2" }; + this.codelist = "CL127"; + } + + public string[] headers { get; } + public string codelist { get; } + public string code { get; set; } + public string description { get; set; } + public string description_nl { get; set; } + public string culturalPracticeLevel1 { get; set; } + public string culturalPracticeLevel2 { get; set; } + } + public class CL263item + { + public CL263item() + { + this.headers = new string[] { "codelist", "code", "eppoCode", "botanicName", "description", "description_fr", "description_nl", "cropGroupCode", "cropGroupName", "cultivationGroupCode", "cultivationGroupName" }; + this.codelist = "CL263"; + this.about = new string[] { "EDI-Crop, coderingslijst gewassoorten" }; + } + + public string[] headers { get; } + public string codelist { get; } + public string[] about { get; } + public string code { get; set; } + public string eppoCode { get; set; } + public string botanicName { get; set; } + public string description { get; set; } + public string description_nl { get; set; } + public string description_fr { get; set; } + public string cropGroupCode { get; set; } + public string cropGroupName { get; set; } + public string cultivationGroupCode { get; set; } + public string cultivationGroupName { get; set; } + public string composition { get; set; } + + } +} \ No newline at end of file diff --git a/FarmmapsDownloadCL/Models/DownloadCLInput.cs b/FarmmapsDownloadCL/Models/DownloadCLInput.cs new file mode 100644 index 0000000..a18acf6 --- /dev/null +++ b/FarmmapsDownloadCL/Models/DownloadCLInput.cs @@ -0,0 +1,12 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace FarmmapsDownloadCL.Models +{ + public class DownloadCLInput + { + public string[] codelists { get; set; } + public string DownloadFolder { get; set; } + + } +} \ No newline at end of file diff --git a/FarmmapsDownloadCL/Program.cs b/FarmmapsDownloadCL/Program.cs new file mode 100644 index 0000000..0d2e87b --- /dev/null +++ b/FarmmapsDownloadCL/Program.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using FarmmapsApi; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace FarmmapsDownloadCL +{ + class Program : FarmmapsProgram + { + private static async Task Main(string[] args) + { + await new Program().Start(args); + } + + protected override void Configure(IServiceCollection serviceCollection) + { + serviceCollection.AddLogging() + .AddTransient(); + } + } +}