public override void ParseAdditionalData(ChargePoint cp, XmlNode item, CoreReferenceData coreRefData)
        {
            string descriptionText = item["description"].InnerText;
            if (descriptionText.Contains("<p>24 hour access</p>")) cp.AddressInfo.AccessComments = "24 Hour Access";
            if (descriptionText.Contains("Business hours only")) cp.AddressInfo.AccessComments = "Business Hours Only";

            if (descriptionText.Contains("Please contact the host premises")) cp.UsageType = ImportRefData.UsageType_PublicNoticeRequired;

            //attempt country match for locations which commonly have geocoding issues
            switch (cp.AddressInfo.Town.Trim())
            {
                case "Cork":
                case "Dublin":
                case "Meath":
                case "Waterford":
                case "Wicklow":
                case "Clare":
                case "Galway":
                case "Kerry":
                case "Wexford":
                    cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c=>c.ISOCode=="IE");
                    break;
                case "Antrim":
                case "Derry":
                    cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c => c.ISOCode == "GB");
                    break;
            }
        }
        public ImportCommonReferenceData(CoreReferenceData coreRefData)
        {
            SubmissionStatus_ImportedAndPublished = coreRefData.SubmissionStatusTypes.First(s => s.ID == 100);//imported and published
            Status_Unknown = coreRefData.StatusTypes.First(os => os.ID == 0);
            Status_Operational = coreRefData.StatusTypes.First(os => os.ID == 50);

            UsageType_Public = coreRefData.UsageTypes.First(u => u.ID == 1);
            UsageType_Private = coreRefData.UsageTypes.First(u => u.ID == 2);
            UsageType_PrivateForStaffAndVisitors = coreRefData.UsageTypes.First(u => u.ID == 6);

            Operator_Unknown = coreRefData.Operators.First(opUnknown => opUnknown.ID == 1);
        }
Example #3
0
        //convert a simple POI to data and back again to fully populate all related properties, as submission may only have simple IDs for ref data etc
        private Model.ChargePoint PopulateFullPOI(Model.ChargePoint poi, Model.CoreReferenceData refData)
        {
            OCMEntities tempDataModel = new OCMEntities();

            //convert simple poi to fully populated db version
            var poiData = new POIManager().PopulateChargePoint_SimpleToData(poi, tempDataModel);

            //convert back to simple POI
            var modelPOI = Model.Extensions.ChargePoint.FromDataModel(poiData, false, false, true, true, refData);

            //clear temp changes from the poi
            //dataModel.Entry(poiData).Reload();
            tempDataModel.Dispose();
            return(modelPOI);
        }
 public virtual void ParseAdditionalData(ChargePoint cp, XmlNode item, CoreReferenceData coreRefData)
 {
     throw new Exception("ParseAdditionalData not implemented");
 }
        public List<API.Common.Model.ChargePoint> Process(CoreReferenceData coreRefData)
        {
            this.ImportRefData = new CommonImportRefData(coreRefData);

            List<ChargePoint> outputList = new List<ChargePoint>();

            XmlDocument xmlDoc = new XmlDocument();
            InputData = InputData.Replace(" xmlns=\"http://earth.google.com/kml/2.0\"", "");

            xmlDoc.LoadXml(InputData);

            XmlNodeList dataList = xmlDoc.SelectNodes("//Placemark");

            int itemCount = 0;

            foreach (XmlNode item in dataList)
            {
                bool skipItem = false;

                ChargePoint cp = new ChargePoint();
                cp.DateLastStatusUpdate = DateTime.UtcNow;

                SetDataProviderDetails(cp, item);

                cp.AddressInfo = new AddressInfo();

                ParseBasicDetails(cp, item);

                //parse coordinates
                string[] posString = item["Point"]["coordinates"].InnerText.Split(',');
                cp.AddressInfo.Latitude = double.Parse(posString[1]);
                cp.AddressInfo.Longitude = double.Parse(posString[0]);

                //determine country or leave for geolocation
                int? countryID = null;
                if (DefaultCountryID != null && countryID == null) countryID = DefaultCountryID;

                if (countryID == null)
                {
                    this.Log("Country Not Matched, will require Geolocation:" + cp.AddressInfo.ToString());
                }
                else
                {
                    cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(cy => cy.ID == countryID);
                }

                cp.Connections = ParseConnectionInfo(item);

                ParseAdditionalData(cp, item, coreRefData);

                cp.DataQualityLevel = 2; //lower than average quality due to spare data
                cp.SubmissionStatus = ImportRefData.SubmissionStatus_Imported;

                if (cp.StatusType == ImportRefData.Status_PlannedForFuture)
                {
                    skipItem = true;
                }

                if (!skipItem) outputList.Add(cp);

                itemCount++;
            }

            return outputList;
        }
Example #6
0
        public void PopulateLocationFromGeolocationCache(List<ChargePoint> itemList, CoreReferenceData coreRefData)
        {
            OCM.Import.Analysis.SpatialAnalysis spatialAnalysis = new Analysis.SpatialAnalysis(TempFolder + "\\Shapefiles\\World\\ne_10m_admin_0_map_units.shp");

            //process list of locations, populating country refreshing cache where required
            foreach (var item in itemList)
            {
                if (item.AddressInfo.Country == null && item.AddressInfo.CountryID == null)
                {
                    Country country = null;

                    var test = spatialAnalysis.ClassifyPoint((double)item.AddressInfo.Latitude, (double)item.AddressInfo.Longitude);
                    if (test != null)
                    {
                        country = coreRefData.Countries.FirstOrDefault(c => c.ISOCode == test.CountryCode || c.Title == test.CountryName);
                    }
                    if (country == null)
                    {
                        var geoLookup = geolocationCacheManager.PerformLocationLookup((double)item.AddressInfo.Latitude, (double)item.AddressInfo.Longitude, coreRefData.Countries);
                        if (geoLookup != null)
                        {
                            country = coreRefData.Countries.FirstOrDefault(c => c.ID == geoLookup.CountryID || c.ISOCode == geoLookup.CountryCode || c.Title == geoLookup.CountryName);
                        }
                    }
                    if (country != null)
                    {
                        item.AddressInfo.Country = country;

                        //remove country name from address line 1 if present
                        if (item.AddressInfo.AddressLine1 != null)
                        {
                            if (item.AddressInfo.AddressLine1.ToLower().Contains(country.Title.ToLower()))
                            {
                                item.AddressInfo.AddressLine1 = item.AddressInfo.AddressLine1.Replace(country.Title, "").Trim();
                            }
                        }
                    }

                    if (item.AddressInfo.Country == null)
                    {
                        LogHelper.Log("Failed to resolve country for item:" + item.AddressInfo.Title);
                    }
                    else
                    {
                        item.AddressInfo.CountryID = item.AddressInfo.Country.ID;
                    }
                }
            }

            //cache may have updates, save for next time
            geolocationCacheManager.SaveCache();
        }
        List<ChargePoint> IImportProvider.Process(CoreReferenceData coreRefData)
        {
            /*
            List<EVSE> outputList = new List<EVSE>();

            string source = InputData;
            StringReader s = new StringReader(source);
            int itemCount = 0;
            XmlTextReader reader = new XmlTextReader(s);
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element && reader.Name == "content")
                {
                    try
                    {
                        EVSE evse = new EVSE();
                        string item = reader.ReadInnerXml();
                        item = item.Replace("&lt;", "<");
                        item = item.Replace("&gt;", ">");

                        evse.Title = GetAttributeValue("Address", item);
                        evse.Latitude = double.Parse(GetAttributeValue("CoordinateWgs84Latitude", item));
                        evse.Longitude = double.Parse(GetAttributeValue("CoordinateWgs84Longitude", item));
                        evse.Updated = DateTime.UtcNow;
                        evse.ID = GetAttributeValue("Uid", item);
                        evse.ExtendedAttributes = new List<ExtendedAttribute>();
                        evse.ExtendedAttributes.Add(new ExtendedAttribute { Name = "City", Value = GetAttributeValue("City", item) });
                        evse.ExtendedAttributes.Add(new ExtendedAttribute { Name = "Country", Value = GetAttributeValue("Country", item) });
                        evse.ExtendedAttributes.Add(new ExtendedAttribute { Name = "PostalCode", Value = GetAttributeValue("PostalCode", item) });
                        //evse.ExtendedAttributes.Add(new ExtendedAttribute { Name = "Notes", Value = GetAttributeValue("Notes", item) });

                        int statusID = int.Parse(GetAttributeValue("Status", item));
                        switch (statusID)
                        {
                            case 0: evse.Status = "Initial";
                                    break;
                                case 1: evse.Status = "Planned";
                                    break;
                                case 2: evse.Status = "Available";
                                    break;
                                case 3: evse.Status = "Charging";
                                    break;
                                case 4: evse.Status = "Faulted";
                                    break;
                                 case 5: evse.Status = "Maintenance";
                                    break;
                                case 6: evse.Status = "Unavailable";
                                    break;
                                 case 7: evse.Status = "Offline";
                                    break;
                        }

                        outputList.Add(evse);

                    }
                    catch (Exception)
                    {
                        Log("Error parsing item " + itemCount);
                    }
                    itemCount++;
                }
            }

            return outputList;
             * */
            return null;
        }
        public List<API.Common.Model.ChargePoint> Process(CoreReferenceData coreRefData)
        {
            List<ChargePoint> outputList = new List<ChargePoint>();

            string source = InputData;

            JObject o = JObject.Parse(source);

            var dataList = o["ChargeDevice"].ToArray();

            var submissionStatus = coreRefData.SubmissionStatusTypes.First(s => s.ID == (int)StandardSubmissionStatusTypes.Imported_UnderReview);//imported and under review
            var submissionStatusDelistedPrivate = coreRefData.SubmissionStatusTypes.First(s => s.ID == (int)StandardSubmissionStatusTypes.Delisted_NotPublicInformation);//delisted not public
            var operationalStatus = coreRefData.StatusTypes.First(os => os.ID == 50);
            var nonoperationalStatus = coreRefData.StatusTypes.First(os => os.ID == 100);
            var unknownStatus = coreRefData.StatusTypes.First(os => os.ID == (int)StandardStatusTypes.Unknown);
            var usageTypeUnknown = coreRefData.UsageTypes.First(u => u.ID == (int)StandardUsageTypes.Unknown);
            var usageTypePublic = coreRefData.UsageTypes.First(u => u.ID == (int)StandardUsageTypes.Public);
            var usageTypePublicPayAtLocation = coreRefData.UsageTypes.First(u => u.ID == (int)StandardUsageTypes.Public_PayAtLocation);
            var usageTypePrivate = coreRefData.UsageTypes.First(u => u.ID == (int)StandardUsageTypes.PrivateRestricted);
            var usageTypePublicMembershipRequired = coreRefData.UsageTypes.First(u => u.ID == (int)StandardUsageTypes.Public_MembershipRequired);
            var operatorUnknown = coreRefData.Operators.First(opUnknown => opUnknown.ID == (int)StandardOperators.UnknownOperator);

            int itemCount = 0;

            foreach (var dataItem in dataList)
            {
                bool skipPOI = false;
                var item = dataItem;
                ChargePoint cp = new ChargePoint();

                var deviceName = item["ChargeDeviceName"].ToString();

                //private addresses are skipped from import
                if (!String.IsNullOrEmpty(deviceName) && deviceName.ToLower().Contains("parkatmyhouse"))
                {
                    //skipPOI = true;
                    skipPOI = true;
                }

                var locationType = item["LocationType"].ToString();
                if (!String.IsNullOrEmpty(locationType))
                {
                    if (locationType.ToLower().Contains("home"))
                    {
                        skipPOI = true;
                    }
                }

                //parse reset of POI data
                cp.DataProvider = new DataProvider() { ID = this.DataProviderID }; //UK National Charge Point Registry
                cp.DataProvidersReference = item["ChargeDeviceId"].ToString();
                cp.DateLastStatusUpdate = DateTime.UtcNow;
                cp.AddressInfo = new AddressInfo();

                var locationDetails = item["ChargeDeviceLocation"];
                var addressDetails = locationDetails["Address"];

                cp.AddressInfo.RelatedURL = "";
                cp.DateLastStatusUpdate = DateTime.UtcNow;
                cp.AddressInfo.AddressLine1 = addressDetails["Street"].ToString().Replace("<br>", ", ");
                cp.AddressInfo.Title = String.IsNullOrEmpty(locationDetails["LocationShortDescription"].ToString()) ? cp.AddressInfo.AddressLine1 : locationDetails["LocationShortDescription"].ToString();
                cp.AddressInfo.Title = cp.AddressInfo.Title.Replace("&amp;", "&");
                cp.AddressInfo.Title = cp.AddressInfo.Title.Replace("<br>", ", ");
                if (cp.AddressInfo.Title.Length > 100) cp.AddressInfo.Title = cp.AddressInfo.Title.Substring(0, 64) + "..";
                cp.AddressInfo.Town = addressDetails["PostTown"].ToString();
                string dependantLocality = addressDetails["DependantLocality"].ToString();
                if (!String.IsNullOrEmpty(dependantLocality) && dependantLocality.ToLower() != cp.AddressInfo.Town.ToLower())
                {
                    //use depenendantLocality if provided and is not same as town
                    cp.AddressInfo.AddressLine2 = dependantLocality;
                }
                cp.AddressInfo.Postcode = addressDetails["PostCode"].ToString();
                cp.AddressInfo.Latitude = double.Parse(locationDetails["Latitude"].ToString());
                cp.AddressInfo.Longitude = double.Parse(locationDetails["Longitude"].ToString());
                cp.AddressInfo.AccessComments = locationDetails["LocationLongDescription"].ToString().Replace("<br>", ", ").Replace("\r\n", ", ").Replace("\n", ", ");

                //if(!String.IsNullOrEmpty(cp.AddressInfo.Postcode))
                //{
                //cp.AddressInfo.Postcode = this.NormalizePostcode(cp.AddressInfo.Postcode);
                //}
                //TODO: if address wasn't provide in address details try to parse from "LocationLongDescription":
                /*if (String.IsNullOrEmpty(cp.AddressInfo.AddressLine1) && string.IsNullOrEmpty(cp.AddressInfo.AddressLine2) && string.IsNullOrEmpty(cp.AddressInfo.Town) && string.IsNullOrEmpty(cp.AddressInfo.Postcode))
                {
                }*/

                //if title is empty, attempt to add a suitable replacement
                if (String.IsNullOrEmpty(cp.AddressInfo.Title))
                {
                    if (!String.IsNullOrEmpty(cp.AddressInfo.AddressLine1))
                    {
                        cp.AddressInfo.Title = cp.AddressInfo.AddressLine1;
                    }
                    else
                    {
                        cp.AddressInfo.Title = cp.AddressInfo.Postcode;
                    }
                }
                //cp.AddressInfo.ContactTelephone1 = item["phone"].ToString();

                if (!String.IsNullOrEmpty(addressDetails["Country"].ToString()))
                {
                    string country = addressDetails["Country"].ToString();
                    int? countryID = null;

                    var countryVal = coreRefData.Countries.FirstOrDefault(c => c.Title.ToLower() == country.Trim().ToLower());
                    if (countryVal == null)
                    {
                        country = country.ToUpper();
                        //match country
                        if (country == "gb" || country == "US" || country == "USA" || country == "U.S." || country == "U.S.A.") countryID = 2;
                        if (country == "UK" || country == "GB" || country == "GREAT BRITAIN" || country == "UNITED KINGDOM") countryID = 1;
                    }
                    else
                    {
                        countryID = countryVal.ID;
                    }

                    if (countryID == null)
                    {
                        this.Log("Country Not Matched, will require Geolocation:" + item["country"].ToString());
                    }
                    else
                    {
                        cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(cy => cy.ID == countryID);
                    }
                }
                else
                {
                    //default to US if no country identified
                    //cp.AddressInfo.Country = cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(cy => cy.ID == 2);
                }

                //operator from DeviceController
                var deviceController = item["DeviceController"];

                cp.AddressInfo.RelatedURL = deviceController["Website"].ToString();
                var deviceOperator = coreRefData.Operators.FirstOrDefault(devOp => devOp.Title.Contains(deviceController["OrganisationName"].ToString()));
                if (deviceOperator != null)
                {
                    cp.OperatorInfo = deviceOperator;
                }
                else
                {
                    //operator from device owner
                    var devOwner = item["DeviceOwner"];
                    deviceOperator = coreRefData.Operators.FirstOrDefault(devOp => devOp.Title.Contains(devOwner["OrganisationName"].ToString()));
                    if (deviceOperator != null)
                    {
                        cp.OperatorInfo = deviceOperator;
                    }
                }

                //determine most likely usage type
                cp.UsageType = usageTypeUnknown;

                if (item["SubscriptionRequiredFlag"].ToString().ToUpper() == "TRUE")
                {
                    //membership required
                    cp.UsageType = usageTypePublicMembershipRequired;
                }
                else
                {
                    if (item["PaymentRequiredFlag"].ToString().ToUpper() == "TRUE")
                    {
                        //payment required
                        cp.UsageType = usageTypePublicPayAtLocation;
                    }
                    else
                    {
                        //accessible 24 hours, payment not required and membership not required, assume public
                        if (item["Accessible24Hours"].ToString().ToUpper() == "TRUE")
                        {
                            cp.UsageType = usageTypePublic;
                        }
                    }
                }

                //special usage cases detected from text
                if (cp.AddressInfo.ToString().ToLower().Contains("no public access"))
                {
                    cp.UsageType = usageTypePrivate;
                }

                //add connections
                var connectorList = item["Connector"].ToArray();
                foreach (var conn in connectorList)
                {
                    string connectorType = conn["ConnectorType"].ToString();
                    if (!String.IsNullOrEmpty(connectorType))
                    {
                        ConnectionInfo cinfo = new ConnectionInfo() { };
                        ConnectionType cType = new ConnectionType { ID = 0 };
                        ChargerType level = null;
                        cinfo.Reference = conn["ConnectorId"].ToString();

                        if (connectorType.ToUpper().Contains("BS 1363") || connectorType.ToUpper().Contains("3-PIN TYPE G (BS1363)"))
                        {
                            cType = new ConnectionType();
                            cType.ID = 3; //UK 13 amp plug
                            level = new ChargerType { ID = 2 };//default to level 2
                        }

                        if (connectorType.ToUpper() == "IEC 62196-2 TYPE 1 (SAE J1772)" || connectorType.ToUpper() == "TYPE 1 SAEJ1772 (IEC 62196)")
                        {
                            cType = new ConnectionType();
                            cType.ID = 1; //J1772
                            level = new ChargerType { ID = 2 };//default to level 2
                        }

                        if (connectorType.ToUpper() == "IEC 62196-2 TYPE 2" || connectorType.ToUpper().Contains("TYPE 2 MENNEKES (IEC62196)"))
                        {
                            cType = new ConnectionType();
                            cType.ID = 25; //Mennkes Type 2
                            level = new ChargerType { ID = 2 };//default to level 2
                        }

                        if (connectorType.ToUpper() == "JEVS G 105 (CHADEMO)" || connectorType.ToUpper() == "JEVS G105 (CHADEMO) DC")
                        {
                            cType = new ConnectionType();
                            cType.ID = 2; //CHadeMO
                            level = new ChargerType { ID = 3 };//default to level 3
                        }
                        if (connectorType.ToUpper() == "IEC 62196-2 TYPE 3")
                        {
                            cType = new ConnectionType();
                            cType.ID = 26; //IEC 62196-2 type 3

                            level = new ChargerType { ID = 2 };//default to level 2
                        }

                        if (connectorType.ToUpper() == "TYPE 2 COMBO (IEC62196) DC")
                        {
                            cType = new ConnectionType();
                            cType.ID = 33; //CCS with Type 2

                            level = new ChargerType { ID = 3 };//default to level 3
                        }

                        if (cType.ID == 0)
                        {
                            var conType = coreRefData.ConnectionTypes.FirstOrDefault(ct => ct.Title.ToLower().Contains(conn.ToString().ToLower()));
                            if (conType != null) cType = conType;
                        }

                        if (!String.IsNullOrEmpty(conn["RatedOutputVoltage"].ToString())) cinfo.Voltage = int.Parse(conn["RatedOutputVoltage"].ToString());
                        if (!String.IsNullOrEmpty(conn["RatedOutputCurrent"].ToString())) cinfo.Amps = int.Parse(conn["RatedOutputCurrent"].ToString());
                        //TODO: use AC/DC/3 Phase data

                        if (conn["ChargePointStatus"] != null)
                        {
                            cinfo.StatusType = operationalStatus;
                            if (conn["ChargePointStatus"].ToString() == "Out of service") cinfo.StatusType = nonoperationalStatus;
                        }

                        if (conn["RatedOutputkW"] != null)
                        {
                            double tmpKw = 0;
                            if (double.TryParse(conn["RatedOutputkW"].ToString(), out tmpKw))
                            {
                                cinfo.PowerKW = tmpKw;
                            }
                        }

                        if (conn["RatedOutputVoltage"] != null)
                        {
                            int tmpV = 0;
                            if (int.TryParse(conn["RatedOutputVoltage"].ToString(), out tmpV))
                            {
                                cinfo.Voltage = tmpV;
                            }
                        }

                        if (conn["RatedOutputCurrent"] != null)
                        {
                            int tmpA = 0;
                            if (int.TryParse(conn["RatedOutputCurrent"].ToString(), out tmpA))
                            {
                                cinfo.Amps = tmpA;
                            }
                        }

                        if (conn["ChargeMethod"] != null && !String.IsNullOrEmpty(conn["ChargeMethod"].ToString()))
                        {
                            string method = conn["ChargeMethod"].ToString();
                            //Single Phase AC, Three Phase AC, DC
                            if (method == "Single Phase AC") cinfo.CurrentTypeID = (int)StandardCurrentTypes.SinglePhaseAC;
                            if (method == "Three Phase AC") cinfo.CurrentTypeID = (int)StandardCurrentTypes.ThreePhaseAC;
                            if (method == "DC") cinfo.CurrentTypeID = (int)StandardCurrentTypes.DC;
                        }
                        cinfo.ConnectionType = cType;
                        cinfo.Level = level;

                        if ((cinfo.ConnectionType == null && cinfo.ConnectionTypeID == null) || cinfo.ConnectionType != null && cinfo.ConnectionType.ID == 0)
                        {
                            if (!String.IsNullOrEmpty(connectorType))
                            {
                                Log("Unknown connector type:" + connectorType);
                            }
                        }
                        if (cp.Connections == null)
                        {
                            cp.Connections = new List<ConnectionInfo>();
                            if (!IsConnectionInfoBlank(cinfo))
                            {
                                //TODO: skip items with blank address info
                                cp.Connections.Add(cinfo);
                            }
                        }
                    }
                }

                //apply data attribution metadata
                if (cp.MetadataValues == null) cp.MetadataValues = new List<MetadataValue>();
                cp.MetadataValues.Add(new MetadataValue { MetadataFieldID = (int)StandardMetadataFields.Attribution, ItemValue = DataAttribution });

                if (cp.DataQualityLevel == null) cp.DataQualityLevel = 3;

                if (cp.SubmissionStatus == null) cp.SubmissionStatus = submissionStatus;

                if (!skipPOI)
                {
                    outputList.Add(cp);
                    itemCount++;
                }
            }

            return outputList;
        }
Example #9
0
        public async Task<ImportReport> PerformImport(ExportType exportType, bool fetchLiveData, APICredentials credentials, CoreReferenceData coreRefData, string outputPath, IImportProvider provider, bool cacheInputData)
        {
            var p = ((BaseImportProvider)provider);
            p.ExportType = exportType;

            ImportReport resultReport = new ImportReport();
            resultReport.ProviderDetails = p;

            try
            {
                bool loadOK = false;
                if (p.ImportInitialisationRequired && p is IImportProviderWithInit)
                {
                    ((IImportProviderWithInit)provider).InitImportProvider();
                }
                if (fetchLiveData && p.IsAutoRefreshed && !String.IsNullOrEmpty(p.AutoRefreshURL))
                {
                    Log("Loading input data from URL..");
                    loadOK = p.LoadInputFromURL(p.AutoRefreshURL);
                }
                else
                {
                    if (p.IsStringData && !p.UseCustomReader)
                    {
                        Log("Loading input data from file..");
                        loadOK = p.LoadInputFromFile(p.InputPath);
                    }
                    else
                    {
                        //binary streams pass as OK by default
                        loadOK = true;
                    }
                }

                if (!loadOK)
                {
                    //failed to load
                    Log("Failed to load input data.");
                    throw new Exception("Failed to fetch input data");
                }
                else
                {
                    if (fetchLiveData && cacheInputData)
                    {
                        //save input data
                        p.SaveInputFile(p.InputPath);
                    }
                }

                List<ChargePoint> duplicatesList = new List<ChargePoint>();

                Log("Processing input..");

                var list = provider.Process(coreRefData);

                int numAdded = 0;
                int numUpdated = 0;

                if (list.Count > 0)
                {
                    if (p.MergeDuplicatePOIEquipment)
                    {
                        Log("Merging Equipment from Duplicate POIs");
                        list = MergeDuplicatePOIEquipment(list);
                    }

                    if (!p.IncludeInvalidPOIs)
                    {
                        Log("Cleaning invalid POIs");
                        var invalidPOIs = new List<ChargePoint>();
                        foreach (var poi in list)
                        {
                            if (!BaseImportProvider.IsPOIValidForImport(poi))
                            {
                                invalidPOIs.Add(poi);
                            }
                        }
                        foreach (var poi in invalidPOIs)
                        {
                            list.Remove(poi);
                        }
                    }

                    List<ChargePoint> finalList = new List<ChargePoint>();

                    if (!p.SkipDeduplication)
                    {
                        Log("De-Deuplicating list (" + p.ProviderName + ":: " + list.Count + " Items)..");

                        //de-duplicate and clean list based on existing data
                        //TODO: take original and replace in final update list, setting relevant updated properties (merge) and status
                        finalList = await DeDuplicateList(list.ToList(), true, coreRefData, resultReport, p.AllowDuplicatePOIWithDifferentOperator);
                        //var finalList = list;
                    }
                    else
                    {
                        //skip deduplication
                        finalList = list.ToList();
                    }

                    if (ImportUpdatesOnly)
                    {
                        finalList = finalList.Where(l => l.ID > 0).ToList();
                    }
                    //finalList = client.GetLocations(new SearchFilters { MaxResults = 10000 });

                    //export/apply updates
                    if (p.ExportType == ExportType.XML)
                    {
                        Log("Exporting XML..");

                        //output xml
                        p.ExportXMLFile(finalList, outputPath + p.OutputNamePrefix + ".xml");
                    }

                    if (p.ExportType == ExportType.CSV)
                    {
                        Log("Exporting CSV..");
                        //output csv
                        p.ExportCSVFile(finalList, outputPath + p.OutputNamePrefix + ".csv");
                    }

                    if (p.ExportType == ExportType.JSON)
                    {
                        Log("Exporting JSON..");
                        //output json
                        p.ExportJSONFile(finalList, outputPath + p.OutputNamePrefix + ".json");
                    }
                    if (p.ExportType == ExportType.API && p.IsProductionReady)
                    {
                        //publish list of locations to OCM via API
                        OCMClient ocmClient = new OCMClient(IsSandboxedAPIMode);
                        Log("Publishing via API..");
                        foreach (ChargePoint cp in finalList.Where(l => l.AddressInfo.Country != null))
                        {
                            ocmClient.UpdateItem(cp, credentials);
                            if (cp.ID == 0)
                            {
                                numAdded++;
                            }
                            else
                            {
                                numUpdated++;
                            }
                        }
                    }
                    if (p.ExportType == ExportType.POIModelList)
                    {
                        //result report contains POI lists
                    }
                }

                Log("Import Processed:" + provider.GetProviderName() + " Added:" + numAdded + " Updated:" + numUpdated);
            }
            catch (Exception exp)
            {
                Log("Import Failed:" + provider.GetProviderName() + " ::" + exp.ToString());
            }

            resultReport.Log = "";
            resultReport.Log += p.ProcessingLog;

            resultReport.Log += ImportLog;
            return resultReport;
        }
        List<ChargePoint> IImportProvider.Process(CoreReferenceData coreRefData)
        {
            List<ChargePoint> outputList = new List<ChargePoint>();

            var submissionStatus = coreRefData.SubmissionStatusTypes.First(s => s.ID == 100);//imported and published
            var operationalStatus = coreRefData.StatusTypes.First(os => os.ID == 50);
            var operationalMixedStatus = coreRefData.StatusTypes.First(os => os.ID == 75);
            var unknownStatus = coreRefData.StatusTypes.First(os => os.ID == 0);
            var usageTypePublic = coreRefData.UsageTypes.First(u => u.ID == 1);
            var usageTypePrivate = coreRefData.UsageTypes.First(u => u.ID == 2);

            var networkOperator = coreRefData.Operators.First(op=>op.ID==5); //Coulomb Chargepoint Network

            CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture;
            TextInfo textInfo = cultureInfo.TextInfo;

            string jsString = "{ \"data\": " + InputData + "}";

            JavaScriptSerializer jss = new JavaScriptSerializer();
            jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });
            dynamic parsedList = jss.Deserialize(jsString, typeof(object)) as dynamic;
            var dataList = parsedList.data;

            int itemCount = 0;
            foreach (var item in dataList)
            {
                try
                {
                    ChargePoint cp = new ChargePoint();
                    cp.AddressInfo = new AddressInfo();

                    cp.OperatorInfo = networkOperator;
                    cp.OperatorsReference = item["StationID"].ToString();
                    cp.DataProvider = new DataProvider() { ID = this.DataProviderID }; //couloumb
                    cp.DataProvidersReference = item["StationID"].ToString();
                    cp.DateLastStatusUpdate = DateTime.UtcNow;

                    cp.AddressInfo.Title = item["Name"] != null ? item["Name"].ToString() : item["StationID"].ToString();
                    cp.AddressInfo.Title = textInfo.ToTitleCase(cp.AddressInfo.Title.ToLower());
                    cp.AddressInfo.RelatedURL = "http://www.chargepoint.net";
                    cp.DateLastStatusUpdate = DateTime.UtcNow;

                    cp.AddressInfo.Latitude = double.Parse(item["Geo"]["lat"].ToString());
                    cp.AddressInfo.Longitude = double.Parse(item["Geo"]["long"].ToString());

                    cp.AddressInfo.AddressLine1 = item["Address"].ToString();
                    //cp.AddressInfo.AddressLine2 = item["address2"].ToString();
                    cp.AddressInfo.Town = item["City"].ToString();
                    cp.AddressInfo.StateOrProvince = item["State"].ToString();
                    cp.AddressInfo.Postcode = item["postalCode"].ToString();

                    //set country property
                    string countryRef = item["Country"].ToString();
                    cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c => c.Title == countryRef);

                    /*string usageTypeCode = item["type"].ToString();
                    if (usageTypeCode == "COMMERCIAL")
                    {
                        cp.UsageType = coreRefData.UsageTypes.FirstOrDefault(u => u.ID == 5); //pay at location
                    }
                    else
                    {
                        Log("Unmatched usage type:"+usageTypeCode);
                    }
                    */
                    cp.NumberOfPoints = int.Parse(item["Num_port"].ToString());
                    cp.GeneralComments = item["Description"].ToString();

                    /*int numOffline = int.Parse(item["offline"].ToString());
                    if (numOffline > 0)
                    {
                        cp.StatusType = operationalMixedStatus;
                    }
                    else
                    {
                        cp.StatusType = operationalStatus;
                    }

                    //populate connections
                    cp.Connections = new List<ConnectionInfo>();
                    var levelTypes = item["levels"].ToArray();
                    foreach (var level in levelTypes)
                    {
                        ConnectionInfo con = new ConnectionInfo();
                        if (level.ToString() == "1")
                        {
                            con.ConnectionType = new ConnectionType { ID = 1 };//J1772
                            con.Level = new ChargerType { ID = 1 };
                        }
                        if (level.ToString() == "2")
                        {
                            con.ConnectionType = new ConnectionType { ID = 1 };//J1772
                            con.Voltage = 220;
                            con.Level = new ChargerType { ID = 2 };
                        }
                        if (level.ToString() == "3")
                        {
                            con.ConnectionType = new ConnectionType { ID = 3 };//J1772
                            con.Voltage = 480;
                            con.Level = new ChargerType { ID = 3 };
                        }
                        cp.Connections.Add(con);
                    }
                     * */
                    cp.DataQualityLevel = 3; //avg, higher than default

                    cp.SubmissionStatus = submissionStatus;
                    outputList.Add(cp);
                }
                catch (Exception)
                {
                    Log("Error parsing item " + itemCount);
                }

                itemCount++;
            }

            return outputList;
        }
        public List<API.Common.Model.ChargePoint> Process(CoreReferenceData coreRefData)
        {
            List<ChargePoint> outputList = new List<ChargePoint>();

            string source = InputData;

            JObject o = JObject.Parse(source);

            var dataList = o["locations"].ToArray();

            var submissionStatus = coreRefData.SubmissionStatusTypes.First(s => s.ID == (int)StandardSubmissionStatusTypes.Imported_UnderReview);//imported and under review
            var operationalStatus = coreRefData.StatusTypes.First(os => os.ID == 50);
            var unknownStatus = coreRefData.StatusTypes.First(os => os.ID == 0);
            var usageTypePublic = coreRefData.UsageTypes.First(u => u.ID == 1);
            var usageTypePrivate = coreRefData.UsageTypes.First(u => u.ID == 2);
            var operatorUnknown = coreRefData.Operators.First(opUnknown => opUnknown.ID == 1);

            int itemCount = 0;
            foreach (var item in dataList)
            {
                ChargePoint cp = new ChargePoint();
                cp.DataProvider = new DataProvider() { ID = this.DataProviderID }; //carstations.com
                cp.DataProvidersReference = item["post_id"].ToString();
                cp.DateLastStatusUpdate = DateTime.UtcNow;
                cp.AddressInfo = new AddressInfo();

                //carstations.com have requested we not use the station names from their data, so we use address
                //cp.AddressInfo.Title = item["name"] != null ? item["name"].ToString() : item["address"].ToString();
                cp.AddressInfo.Title = item["address"] != null ? item["address"].ToString() : item["post_id"].ToString();
                cp.AddressInfo.Title = cp.AddressInfo.Title.Trim().Replace("&amp;", "&");
                cp.AddressInfo.RelatedURL = "http://carstations.com/" + cp.DataProvidersReference;
                cp.DateLastStatusUpdate = DateTime.UtcNow;
                cp.AddressInfo.AddressLine1 = item["address"].ToString().Trim();
                cp.AddressInfo.Town = item["city"].ToString().Trim();
                cp.AddressInfo.StateOrProvince = item["region"].ToString().Trim();
                cp.AddressInfo.Postcode = item["postal_code"].ToString().Trim();
                cp.AddressInfo.Latitude = double.Parse(item["latitude"].ToString().Trim());
                cp.AddressInfo.Longitude = double.Parse(item["longitude"].ToString().Trim());

                cp.AddressInfo.ContactTelephone1 = item["phone"].ToString();

                if (!String.IsNullOrEmpty(item["country"].ToString()))
                {
                    string country = item["country"].ToString();
                    int? countryID = null;

                    var countryVal = coreRefData.Countries.FirstOrDefault(c => c.Title.ToLower() == country.Trim().ToLower());
                    if (countryVal == null)
                    {
                        country = country.ToUpper();
                        //match country
                        if (country == "UNITED STATES" || country == "US" || country == "USA" || country == "U.S." || country == "U.S.A.") countryID = 2;

                        if (country == "UK" || country == "GB" || country == "GREAT BRITAIN" || country == "UNITED KINGDOM") countryID = 1;
                    }
                    else
                    {
                        countryID = countryVal.ID;
                    }

                    if (countryID == null)
                    {
                        this.Log("Country Not Matched, will require Geolocation:" + item["country"].ToString());
                    }
                    else
                    {
                        cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(cy => cy.ID == countryID);
                    }
                }
                else
                {
                    //default to US if no country identified
                    //cp.AddressInfo.Country = cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(cy => cy.ID == 2);
                }

                //System.Diagnostics.Debug.WriteLine(item.ToString());
                string publicCount = item["public"].ToString();
                string privateCount = item["private"].ToString();

                if (!String.IsNullOrEmpty(publicCount) && publicCount != "0")
                {
                    try
                    {
                        cp.NumberOfPoints = int.Parse(publicCount);
                    }
                    catch (Exception) { }
                    cp.UsageType = usageTypePublic;
                }
                else
                {
                    if (!String.IsNullOrEmpty(privateCount) && privateCount != "0")
                    {
                        try
                        {
                            cp.NumberOfPoints = int.Parse(privateCount);
                        }
                        catch (Exception) { }
                        cp.UsageType = usageTypePrivate;
                    }
                }

                string verifiedFlag = item["verified_flag"].ToString();

                if (!string.IsNullOrEmpty(verifiedFlag) && verifiedFlag != "0")
                {
                    cp.StatusType = operationalStatus;
                }
                else
                {
                    cp.StatusType = unknownStatus;
                }

                //TODO: allow for multiple operators?
                var operatorsNames = item["brands"].ToArray();

                if (operatorsNames.Count() > 0)
                {
                    var operatorName = operatorsNames[0].ToString();
                    var opDetails = coreRefData.Operators.FirstOrDefault(op => op.Title.ToLower().Contains(operatorName.ToString().ToLower()));
                    if (opDetails != null)
                    {
                        cp.OperatorInfo = opDetails;
                    }
                    else
                    {
                        Log("Operator not matched:" + operatorName);
                    }
                }
                else
                {
                    cp.OperatorInfo = operatorUnknown;
                }

                var connectorTypes = item["techs"].ToArray();
                foreach (var conn in connectorTypes)
                {
                    ConnectionInfo cinfo = new ConnectionInfo() { };
                    ConnectionType cType = new ConnectionType { ID = 0 };
                    ChargerType level = null;
                    cinfo.Reference = conn.ToString();

                    if (conn.ToString().ToUpper() == "J1772")
                    {
                        cType = new ConnectionType();
                        cType.ID = 1; //J1772
                        level = new ChargerType { ID = 2 };//default to level 2
                    }

                    if (conn.ToString().ToUpper() == "CHADEMO")
                    {
                        cType = new ConnectionType();
                        cType.ID = 2; //CHadeMO
                        level = new ChargerType { ID = 3 };//default to level 3
                    }
                    if (conn.ToString().ToUpper() == "NEMA5")
                    {
                        cType = new ConnectionType();
                        cType.ID = 9; //NEMA5-20R
                        level = new ChargerType { ID = 1 };//default to level 1
                    }

                    if (cType.ID == 0)
                    {
                        var conType = coreRefData.ConnectionTypes.FirstOrDefault(ct => ct.Title.ToLower().Contains(conn.ToString().ToLower()));
                        if (conType != null) cType = conType;
                    }
                    cinfo.ConnectionType = cType;
                    cinfo.Level = level;

                    if (cp.Connections == null)
                    {
                        cp.Connections = new List<ConnectionInfo>();
                        if (!IsConnectionInfoBlank(cinfo))
                        {
                            cp.Connections.Add(cinfo);
                        }
                    }
                }

                if (cp.DataQualityLevel == null) cp.DataQualityLevel = 2;

                cp.SubmissionStatus = submissionStatus;

                if (IsPOIValidForImport(cp))
                {
                    outputList.Add(cp);
                }

                itemCount++;
            }

            return outputList;
        }
        public async Task <SubscriptionMatchGroup> GetSubscriptionMatches(OCM.Core.Data.OCMEntities dataModel, POIManager poiManager, OCM.Core.Data.UserSubscription subscription, Model.CoreReferenceData refData, DateTime?dateFrom = null)
        {
            SubscriptionMatchGroup subscriptionMatchGroup = new SubscriptionMatchGroup();

            var checkFromDate = DateTime.UtcNow.AddMinutes(-CHECK_INTERVAL_MINS); //check from last 5 mins etc

            if (subscription.DateLastNotified != null)
            {
                checkFromDate = subscription.DateLastNotified.Value;                                        //check from date last notified
            }
            else
            {
                checkFromDate = subscription.DateCreated;
            }

            if (dateFrom != null)
            {
                checkFromDate = dateFrom.Value;
            }

            subscriptionMatchGroup.DateFrom = checkFromDate;

            //FIXME: re-test with updated coordinate objects (nettopology etc)
            GeoCoordinatePortable.GeoCoordinate searchPos = null;

            UserSubscriptionFilter filter = null;

            if (subscription.FilterSettings != null)
            {
                try
                {
                    filter = JsonConvert.DeserializeObject <UserSubscriptionFilter>(subscription.FilterSettings);
                }
                catch (Exception)
                {
                    //failed to parse subscription filter
                }
            }

            if (subscription.Latitude != null && subscription.Longitude != null)
            {
                searchPos = new GeoCoordinatePortable.GeoCoordinate((double)subscription.Latitude, (double)subscription.Longitude);
            }

            if (subscription.NotifyEmergencyChargingRequests)
            {
                var emergencyCharging = dataModel.UserChargingRequests.Where(c => c.DateCreated >= checkFromDate && c.IsActive == true && c.IsEmergency == true);
                var subscriptionMatch = new SubscriptionMatch {
                    Category = SubscriptionMatchCategory.ChargingRequestEmergency, Description = "New Emergency Charging Requests"
                };

                foreach (var chargingRequest in emergencyCharging)
                {
                    //filter on location
                    if (searchPos != null)
                    {
                        if (GeoManager.CalcDistance(chargingRequest.Latitude, chargingRequest.Longitude, (double)searchPos.Latitude, (double)searchPos.Longitude, DistanceUnit.KM) < subscription.DistanceKm)
                        {
                            subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                                Item = chargingRequest
                            });
                        }
                    }
                    else
                    {
                        subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                            Item = chargingRequest
                        });
                    }
                }
                if (subscriptionMatch.ItemList.Any())
                {
                    subscriptionMatchGroup.SubscriptionMatches.Add(subscriptionMatch);
                }
            }

            if (subscription.NotifyGeneralChargingRequests)
            {
                //TODO: subscription not filtered on lat/long will return global charging requests
                var generalCharging   = dataModel.UserChargingRequests.Where(c => c.DateCreated >= checkFromDate && c.IsActive == true && c.IsEmergency == false);
                var subscriptionMatch = new SubscriptionMatch {
                    Category = SubscriptionMatchCategory.ChargingRequestGeneral, Description = "New Charging Requests"
                };
                //filter on location
                foreach (var gc in generalCharging)
                {
                    if (searchPos != null)
                    {
                        if (GeoManager.CalcDistance(gc.Latitude, gc.Longitude, (double)searchPos.Latitude, (double)searchPos.Longitude, DistanceUnit.KM) < subscription.DistanceKm)
                        {
                            subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                                Item = gc
                            });
                        }
                    }
                    else
                    {
                        subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                            Item = gc
                        });
                    }
                }

                if (subscriptionMatch.ItemList.Any())
                {
                    subscriptionMatchGroup.SubscriptionMatches.Add(subscriptionMatch);
                }
            }

            //check if any POI Edits (pending approval) match this subscription
            if (subscription.NotifyPoiedits)
            {
                var poiEdits = dataModel.EditQueueItems.Where(c => c.DateSubmitted >= checkFromDate && c.PreviousData != null && c.IsProcessed == false);
                if (poiEdits.Any())
                {
                    var subscriptionMatch = new SubscriptionMatch {
                        Category = SubscriptionMatchCategory.EditedPOI, Description = "Proposed POI Edits"
                    };
                    foreach (var p in poiEdits)
                    {
                        try
                        {
                            var updatedPOI = JsonConvert.DeserializeObject <ChargePoint>(p.EditData);
                            if (IsPOISubscriptionFilterMatch(updatedPOI, filter, subscription))
                            {
                                if (searchPos != null)
                                {
                                    if (GeoManager.CalcDistance(updatedPOI.AddressInfo.Latitude, updatedPOI.AddressInfo.Longitude, (double)searchPos.Latitude, (double)searchPos.Longitude, DistanceUnit.KM) < subscription.DistanceKm)
                                    {
                                        subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                                            Item = p, POI = updatedPOI
                                        });
                                    }
                                }
                                else
                                {
                                    subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                                        Item = p, POI = updatedPOI
                                    });
                                }
                            }
                        }
                        catch (Exception)
                        {
                            ;;
                        }
                    }
                    if (subscriptionMatch.ItemList.Any())
                    {
                        subscriptionMatchGroup.SubscriptionMatches.Add(subscriptionMatch);
                    }
                }
            }

            //check if any new POIs
            if (subscription.NotifyPoiadditions)
            {
                /* var newPOIs = dataModel.ChargePoints.Where(c => c.DateCreated >= checkFromDate && c.SubmissionStatusType.IsLive == true &&
                 *    (searchPos == null ||
                 *       (searchPos != null &&
                 *           c.AddressInfo.SpatialPosition.Distance(searchPos) / 1000 < subscription.DistanceKM
                 *       ))
                 *     ).ToList();*/

                var filterParams = new APIRequestParams {
                    CreatedFromDate = checkFromDate, AllowMirrorDB = true
                };
                if (searchPos != null)
                {
                    filterParams.DistanceUnit = DistanceUnit.KM;
                    filterParams.Distance     = subscription.DistanceKm;
                    filterParams.Latitude     = searchPos.Latitude;
                    filterParams.Longitude    = searchPos.Longitude;
                }
                if (subscription.CountryId != null)
                {
                    filterParams.CountryIDs = new int[] { (int)subscription.CountryId };
                }
                var poiCollection = poiManager.GetPOIList(filterParams);

                if (poiCollection.Any())
                {
                    var subscriptionMatch = new SubscriptionMatch {
                        Category = SubscriptionMatchCategory.NewPOI, Description = "New POIs Added"
                    };
                    foreach (var p in poiCollection)
                    {
                        //var poi = OCM.API.Common.Model.Extensions.ChargePoint.FromDataModel(p);
                        if (IsPOISubscriptionFilterMatch(p, filter, subscription))
                        {
                            subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                                POI = p
                            });
                        }
                    }
                    if (subscriptionMatch.ItemList.Any())
                    {
                        subscriptionMatchGroup.SubscriptionMatches.Add(subscriptionMatch);
                    }
                }
            }

            //check if any POI Updates match this subscription
            if (subscription.NotifyPoiupdates)
            {
                var poiUpdates = dataModel.EditQueueItems.Where(c => c.DateProcessed >= checkFromDate && c.IsProcessed == true && c.PreviousData != null);
                if (poiUpdates.Any())
                {
                    var subscriptionMatch = new SubscriptionMatch {
                        Category = SubscriptionMatchCategory.UpdatedPOI, Description = "POIs Updated"
                    };
                    foreach (var p in poiUpdates)
                    {
                        try
                        {
                            var updatedPOI = JsonConvert.DeserializeObject <ChargePoint>(p.EditData);
                            if (IsPOISubscriptionFilterMatch(updatedPOI, filter, subscription))
                            {
                                if (searchPos != null)
                                {
                                    if (GeoManager.CalcDistance(updatedPOI.AddressInfo.Latitude, updatedPOI.AddressInfo.Longitude, (double)searchPos.Latitude, (double)searchPos.Longitude, DistanceUnit.KM) < subscription.DistanceKm)
                                    {
                                        subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                                            Item = p, POI = updatedPOI
                                        });
                                    }
                                }
                                else
                                {
                                    subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                                        Item = p, POI = updatedPOI
                                    });
                                }
                            }
                        }
                        catch (Exception)
                        {
                            ;;
                        }
                    }
                    if (subscriptionMatch.ItemList.Any())
                    {
                        subscriptionMatchGroup.SubscriptionMatches.Add(subscriptionMatch);
                    }
                }
            }

            //check if any new comments match this subscription
            if (subscription.NotifyComments)
            {
                var newComments = dataModel.UserComments.Where(c => c.DateCreated >= checkFromDate &&
                                                               (searchPos == null ||
                                                                (searchPos != null &&
                                                                 c.ChargePoint.AddressInfo.SpatialPosition.Distance(new NetTopologySuite.Geometries.Point((double)searchPos.Latitude, (double)searchPos.Longitude)) / 1000 < subscription.DistanceKm
                                                                ))
                                                               );
                if (newComments.Any())
                {
                    var subscriptionMatch = new SubscriptionMatch {
                        Category = SubscriptionMatchCategory.NewComment, Description = "New Comments Added"
                    };
                    foreach (var c in newComments)
                    {
                        var poi = OCM.API.Common.Model.Extensions.ChargePoint.FromDataModel(c.ChargePoint, refData);
                        if (IsPOISubscriptionFilterMatch(poi, filter, subscription))
                        {
                            subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                                Item = OCM.API.Common.Model.Extensions.UserComment.FromDataModel(c, true), POI = poi
                            });
                        }
                    }
                    if (subscriptionMatch.ItemList.Any())
                    {
                        subscriptionMatchGroup.SubscriptionMatches.Add(subscriptionMatch);
                    }
                }
            }

            //check if any new Media uploads match this subscription
            if (subscription.NotifyMedia)
            {
                var newMedia = dataModel.MediaItems.Where(c => c.DateCreated >= checkFromDate &&
                                                          (searchPos == null ||
                                                           (searchPos != null &&
                                                            c.ChargePoint.AddressInfo.SpatialPosition.Distance(new NetTopologySuite.Geometries.Point((double)searchPos.Latitude, (double)searchPos.Longitude)) / 1000 < subscription.DistanceKm
                                                           ))
                                                          );
                if (newMedia.Any())
                {
                    var subscriptionMatch = new SubscriptionMatch {
                        Category = SubscriptionMatchCategory.NewMediaUpload, Description = "New Photos Added"
                    };
                    foreach (var c in newMedia)
                    {
                        var poi = OCM.API.Common.Model.Extensions.ChargePoint.FromDataModel(c.ChargePoint, refData);
                        if (IsPOISubscriptionFilterMatch(poi, filter, subscription))
                        {
                            subscriptionMatch.ItemList.Add(new SubscriptionMatchItem {
                                Item = OCM.API.Common.Model.Extensions.MediaItem.FromDataModel(c), POI = poi
                            });
                        }
                    }
                    if (subscriptionMatch.ItemList.Any())
                    {
                        subscriptionMatchGroup.SubscriptionMatches.Add(subscriptionMatch);
                    }
                }
            }

            return(subscriptionMatchGroup);
        }
        public CoreReferenceData GetCoreReferenceData(bool enableCaching = true)
        {
            CoreReferenceData data = null;

            if (enableCaching)
            {
                data = OCM.Core.Data.CacheManager.GetCoreReferenceData();

                if (data != null) return data;
            }

            //can't get cached data, get fresh from database
            data = new CoreReferenceData();

            //list of Levels (ChargerTypes)
            data.ChargerTypes = new List<Model.ChargerType>();
            foreach (var cg in dataModel.ChargerTypes)
            {
                data.ChargerTypes.Add(Model.Extensions.ChargerType.FromDataModel(cg));
            }

            //list of connection types
            data.ConnectionTypes = new List<Model.ConnectionType>();
            foreach (var ct in dataModel.ConnectionTypes)
            {
                data.ConnectionTypes.Add(Model.Extensions.ConnectionType.FromDataModel(ct));
            }

            //list of power source types (AC/DC etc)
            data.CurrentTypes = new List<Model.CurrentType>();
            foreach (var ct in dataModel.CurrentTypes)
            {
                data.CurrentTypes.Add(Model.Extensions.CurrentType.FromDataModel(ct));
            }

            //list of countries
            data.Countries = new List<Model.Country>();
            foreach (var country in dataModel.Countries)
            {
                data.Countries.Add(Model.Extensions.Country.FromDataModel(country));
            }

            //list of Data Providers
            data.DataProviders = new List<Model.DataProvider>();
            foreach (var provider in dataModel.DataProviders.ToList())
            {
                data.DataProviders.Add(Model.Extensions.DataProvider.FromDataModel(provider));
            }

            //list of Operators
            data.Operators = new List<Model.OperatorInfo>();
            foreach (var source in dataModel.Operators.OrderBy(o => o.Title))
            {
                data.Operators.Add(Model.Extensions.OperatorInfo.FromDataModel(source));
            }

            //list of Status Types
            data.StatusTypes = new List<Model.StatusType>();
            foreach (var status in dataModel.StatusTypes)
            {
                data.StatusTypes.Add(Model.Extensions.StatusType.FromDataModel(status));
            }

            //list of Usage Types (public etc)
            data.UsageTypes = new List<Model.UsageType>();
            foreach (var usage in dataModel.UsageTypes.OrderBy(u => u.Title))
            {
                data.UsageTypes.Add(Model.Extensions.UsageType.FromDataModel(usage));
            }

            //list of user comment types
            data.UserCommentTypes = new List<Model.UserCommentType>();
            foreach (var commentType in dataModel.UserCommentTypes)
            {
                data.UserCommentTypes.Add(Model.Extensions.UserCommentType.FromDataModel(commentType));
            }

            //list of user comment types
            data.CheckinStatusTypes = new List<Model.CheckinStatusType>();
            foreach (var checkinType in dataModel.CheckinStatusTypes)
            {
                data.CheckinStatusTypes.Add(Model.Extensions.CheckinStatusType.FromDataModel(checkinType));
            }

            data.SubmissionStatusTypes = new List<Model.SubmissionStatusType>();
            foreach (var s in dataModel.SubmissionStatusTypes)
            {
                data.SubmissionStatusTypes.Add(Model.Extensions.SubmissionStatusType.FromDataModel(s));
            }

            data.MetadataGroups = new List<Model.MetadataGroup>();
            foreach (var g in dataModel.MetadataGroups.ToList())
            {
                data.MetadataGroups.Add(Model.Extensions.MetadataGroup.FromDataModel(g));
            }

            data.DataTypes = new List<Model.DataType>();
            foreach (var d in dataModel.DataTypes)
            {
                data.DataTypes.Add(Model.Extensions.DataType.FromDataModel(d));
            }
            data.ChargePoint = new ChargePoint()
            {
                AddressInfo = new Model.AddressInfo(),
            #pragma warning disable 612 //suppress obsolete warning
                Chargers = new List<Model.ChargerInfo> { new Model.ChargerInfo() },
            #pragma warning restore 612
                Connections = new List<Model.ConnectionInfo> { new Model.ConnectionInfo() },
                DateCreated = DateTime.UtcNow,
                DateLastConfirmed = DateTime.UtcNow,
                DateLastStatusUpdate = DateTime.UtcNow,
                GeneralComments = "",
                DatePlanned = null,
                ID = -1,
                NumberOfPoints = 1,
                StatusType = new Model.StatusType(),
                OperatorInfo = new Model.OperatorInfo(),
                DataProvider = new Model.DataProvider(),
                UsageType = new Model.UsageType(),
                UUID = Guid.NewGuid().ToString(),
                DataQualityLevel = 1
            };

            data.UserComment = new Model.UserComment { ChargePointID = 0, Comment = "", CommentType = data.UserCommentTypes[0], DateCreated = DateTime.UtcNow, ID = 0, CheckinStatusType = data.CheckinStatusTypes[0] };
            return data;
        }
        List<ChargePoint> IImportProvider.Process(CoreReferenceData coreRefData)
        {
            /*
            List<EVSE> outputList = new List<EVSE>();

            string source = InputData;

            int startPos = source.IndexOf("addwithicon(\"");
            int endPos = source.LastIndexOf("<script type=\"text/javascript\">");

            string jsString = source.Substring(startPos, endPos - startPos);

            jsString = jsString.Replace("</script>","");
            jsString = jsString.Replace(" <script type=\"text/javascript\">","");

             jsString = jsString.Replace(");", "]},");

            jsString = jsString.Substring(0, jsString.LastIndexOf(","));
               // jsString = jsString.Substring(0, jsString.LastIndexOf(","));

            //jsString = jsString.Replace("\"", "|");
            //jsString = jsString.Replace("new google.maps.LatLng(", "\"");
            //jsString = jsString.Replace("),", "\",");
               // jsString = jsString.Replace("'", "\"");
               // jsString = jsString.Replace("|", "'");
            jsString = jsString.Replace("addwithicon(\"", "{\"value\" :[\"");
            while (jsString.Contains("href=\""))
            {
                string fragment = jsString.Substring(jsString.IndexOf("href=\"") + 6);
                fragment = fragment.Substring(0,fragment.IndexOf("\""));
                jsString= jsString.Replace("\"" + fragment + "\"", "'" + fragment + "'");
            }
            jsString = "{ \"data\":[ " + jsString + "]}";

            JObject o = JObject.Parse(jsString);
            var dataList = o.Values();

            int itemCount =0;
            foreach (var item in dataList.Values())
            {
                try
                {
                    EVSE evse = new EVSE();
                    evse.Link = "http://www.chargeyourcar.org.uk";
                    evse.Updated = DateTime.UtcNow;
                    evse.ExtendedAttributes = new List<ExtendedAttribute>();

                    JToken[]  elements = item["value"].Values().ToArray();
                    string content = elements[2].ToString();
                    int contentParsePos = content.IndexOf("<br>") + 4;

                    evse.Title = content.Substring(contentParsePos, content.IndexOf("</b>") - contentParsePos);

                    //evse.ID = item["position"].ToString().Replace(", ", "@");

                    //content = content.Substring(content.LastIndexOf("<p>") + 3, content.LastIndexOf("</p>") - (content.LastIndexOf("<p>") + 3));
                    evse.Content = content;

                    evse.Latitude = double.Parse(elements[0].ToString());
                    evse.Longitude = double.Parse(elements[1].ToString());

                    outputList.Add(evse);
                }
                catch (Exception)
                {
                    Log("Error parsing item "+itemCount);
                }

                itemCount++;
            }

            return outputList;
             * */
            return null;
        }
        List<ChargePoint> IImportProvider.Process(CoreReferenceData coreRefData)
        {
            var submissionStatus = coreRefData.SubmissionStatusTypes.First(s => s.ID == 100);//imported and published
            var status_operational = coreRefData.StatusTypes.First(os => os.ID == 50);
            var status_notoperational = coreRefData.StatusTypes.First(os => os.ID == 100);

            var status_operationalMixed = coreRefData.StatusTypes.First(os => os.ID == 75);
            var status_available = coreRefData.StatusTypes.First(os => os.ID == 10);
            var status_inuse = coreRefData.StatusTypes.First(os => os.ID == 20);
            var status_unknown = coreRefData.StatusTypes.First(os => os.ID == 0);
            var usageTypePublic = coreRefData.UsageTypes.First(u => u.ID == 1);
            var usageTypePrivate = coreRefData.UsageTypes.First(u => u.ID == 2);

            JObject o = JObject.Parse(InputData);
            var dataList = o.Values()["ChargingStationList"].Values().ToArray();

            List<ChargePoint> outputList = new List<ChargePoint>();

            int itemCount = 0;

            foreach (var item in dataList)
            {
                try
                {
                    ChargePoint cp = new ChargePoint();
                    cp.AddressInfo = new AddressInfo();
                    var addressData = item["ChargingStationAddress"];
                    cp.AddressInfo.Title = addressData["Street"] != null ? addressData["Street"].ToString() : item["ChargingStationId"].ToString();
                    cp.AddressInfo.RelatedURL = "http://www.mobie.pt";

                    cp.DataProvider = new DataProvider() { ID = this.DataProviderID }; //mobie.pt
                    cp.DataProvidersReference = item["ChargingStationId"].ToString();
                    cp.DateLastStatusUpdate = DateTime.UtcNow;

                    cp.AddressInfo.AddressLine1 = addressData["Street"].ToString();
                    if (addressData["Number"]!=null && addressData["Number"].ToString() != "-")
                    {
                        cp.AddressInfo.AddressLine1 += " " + addressData["Number"].ToString();
                    }

                    cp.AddressInfo.Town = addressData["City"].ToString();

                    cp.AddressInfo.Postcode = addressData["PostalCode"].ToString();
                    cp.AddressInfo.Latitude = double.Parse(item["Latitude"].ToString());
                    cp.AddressInfo.Longitude = double.Parse(item["Longitude"].ToString());
                    var countryCode = addressData["Country"].ToString();
                    var country = coreRefData.Countries.FirstOrDefault(ct => ct.ISOCode==countryCode);
                    cp.AddressInfo.Country = country;

                    cp.NumberOfPoints = int.Parse(item["TotalSattelites"].ToString());

                    cp.StatusType = status_operational;
                    string status = item["Status"].ToString().ToLower();

                    if (status == "unavailable" || status == "reserved" || status == "in use")
                    {
                        cp.StatusType = status_operational;
                    }

                    if (status == "disconnected" || status == "inactive" || status == "suspended")
                    {
                        cp.StatusType = status_notoperational;
                    }

                    string type = item["Type"].ToString();//fast or normal
                    if (type.ToLower() == "fast" || type.ToLower() == "normal")
                    {
                        //populate connections
                        cp.Connections = new List<ConnectionInfo>();

                        ConnectionInfo con = new ConnectionInfo();
                        if (String.Equals(type, "fast", StringComparison.CurrentCultureIgnoreCase))
                        {
                            con.Level = new ChargerType { ID = 3 };
                            con.Voltage = 400;
                            con.Amps = 75;
                            con.PowerKW = con.Voltage * con.Amps / 1000;
                            con.StatusType = cp.StatusType;
                            con.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.DC };
                        }

                        if (String.Equals(type, "normal", StringComparison.CurrentCultureIgnoreCase))
                        {
                            con.Level = new ChargerType { ID = 2 };
                            //based on http://www.mobie.pt/en/o-carregamento
                            con.Voltage = 220;
                            con.Amps = 16;
                            con.PowerKW = con.Voltage * con.Amps / 1000;
                            con.StatusType = cp.StatusType;
                            con.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.SinglePhaseAC };
                        }
                        cp.Connections.Add(con);
                    }

                    //TODO: attempt to match operator
                    var operatorName = item["Operator"].ToString();
                    var operatorInfo = coreRefData.Operators.FirstOrDefault(op => op.Title.ToLower().StartsWith(operatorName.ToLower()));
                    if (operatorInfo != null)
                    {
                        cp.OperatorInfo = operatorInfo;
                    } else
                    {
                        this.Log("Unknown Operator:" + operatorName);
                    }
                    cp.DataQualityLevel = 3; //avg, higher than default

                    cp.SubmissionStatus = submissionStatus;

                    outputList.Add(cp);
                }
                catch (Exception exp)
                {
                    Log("Error parsing item " + itemCount+ " "+ exp.ToString());
                }

                itemCount++;
            }

            return outputList;
        }
        public List<ChargePoint> Process(CoreReferenceData coreRefData)
        {
            List<ChargePoint> outputList = new List<ChargePoint>();

            string source = InputData;

            int startPos = source.IndexOf("*markers");
            int endPos = source.LastIndexOf("id=\"footer\"");
            DataProvider dataProvider = coreRefData.DataProviders.FirstOrDefault(d => d.ID == 16); //POD Point
            OperatorInfo operatorInfo = coreRefData.Operators.FirstOrDefault(op => op.ID == 3);

            string jsString = "/*" + source.Substring(startPos, endPos - startPos);

            jsString = jsString.Replace("makeMarker(", "");
            jsString = jsString.Replace(");", ",");

            jsString = jsString.Substring(0, jsString.LastIndexOf(","));
            jsString = jsString.Substring(0, jsString.LastIndexOf(","));

            jsString = jsString.Replace("/**markers", "");
            jsString = jsString.Replace("/**", "");
            jsString = jsString.Replace("*markers", "");

            jsString = jsString.Replace("**/", "");
            jsString = jsString.Replace("*/", "");
            jsString = jsString.Replace("\"", "|");
            jsString = jsString.Replace("new google.maps.LatLng(", "\"");
            jsString = jsString.Replace("),", "\",");
            jsString = jsString.Replace("'", "\"");
            jsString = jsString.Replace("|", "'");

            jsString = "{ \"data\":[ " + jsString + "]}";

            JObject o = JObject.Parse(jsString);
            var dataList = o.Values();

            int itemCount =0;
            foreach (var item in dataList.Values())
            {
                try
                {
                    ChargePoint cp  = new ChargePoint();
                    cp.DataProvider = dataProvider;
                    cp.OperatorInfo = operatorInfo;

                    cp.AddressInfo.Title = item["title"].ToString();
                    cp.AddressInfo.RelatedURL = "http://www.pod-point.com";
                    cp.DataProvidersReference = item["position"].ToString().Replace(", ", "@");
                    cp.DateLastStatusUpdate = DateTime.UtcNow;

                    string content = item["content"].ToString();
                    content = content.Substring(content.LastIndexOf("<p>") + 3, content.LastIndexOf("</p>") - (content.LastIndexOf("<p>") + 3));
                    cp.GeneralComments = content;
                    string[] pos = item["position"].ToString().Split(',');
                    cp.AddressInfo.Latitude = double.Parse(pos[0]);
                    cp.AddressInfo.Longitude = double.Parse(pos[1]);

                    string status = "";
                    string itemIcon = item["icon"].ToString();
                    if (itemIcon.EndsWith("mappodpointblue.png")) status = "Available";
                    if (itemIcon.EndsWith("mappodpointgreen.png")) status = "In Use";
                    if (itemIcon.EndsWith("mappodpointbw.png")) status = "Planned";
                    if (itemIcon.EndsWith("mappodpointred.png")) status = "Not Operational";
                    if (!String.IsNullOrEmpty(status))
                    {
                        var statusType = coreRefData.StatusTypes.FirstOrDefault(s => s.Title.ToLower() == status.ToLower());
                        if (statusType != null)
                        {
                            cp.StatusType = statusType;
                        }
                    }
                    outputList.Add(cp);
                }
                catch (Exception)
                {
                    Log("Error parsing item "+itemCount);
                }

                itemCount++;
            }

            return outputList;
        }
        public List<API.Common.Model.ChargePoint> Process(CoreReferenceData coreRefData)
        {
            List<ChargePoint> outputList = new List<ChargePoint>();

            var submissionStatus = coreRefData.SubmissionStatusTypes.First(s => s.ID == 100);//imported and published
            var operationalStatus = coreRefData.StatusTypes.First(os => os.ID == 50);
            var unknownStatus = coreRefData.StatusTypes.First(os => os.ID == 0);
            var usageTypePublic = coreRefData.UsageTypes.First(u => u.ID == 1);
            var usageTypePrivate = coreRefData.UsageTypes.First(u => u.ID == 2);
            var usageTypePrivateForStaffAndVisitors = coreRefData.UsageTypes.First(u => u.ID == 6); //staff and visitors
            var operatorUnknown = coreRefData.Operators.First(opUnknown => opUnknown.ID == 1);

            int itemCount = 0;

            string jsonString = "{ \"data\": " + InputData + "}";

            JObject o = JObject.Parse(jsonString);
            var dataList = o.Values()["list"].Values().ToArray();

            foreach (var item in dataList)
            {
                ChargePoint cp = new ChargePoint();
                cp.DataProvider = new DataProvider() { ID = this.DataProviderID }; //AddEnergie
                cp.DataProvidersReference = item["StationID"].ToString();
                cp.DateLastStatusUpdate = DateTime.UtcNow;

                cp.AddressInfo = new AddressInfo();

                cp.AddressInfo.Title = item["ParkName"].ToString();
                cp.AddressInfo.AddressLine1 = item["Address"].ToString().Trim();
                cp.AddressInfo.Town = item["City"].ToString().Trim();
                cp.AddressInfo.StateOrProvince = item["StateOrProvince"].ToString().Trim();
                cp.AddressInfo.Postcode = item["PostalOrZipCode"].ToString().Trim();
                cp.AddressInfo.Latitude = double.Parse(item["Latitude"].ToString());
                cp.AddressInfo.Longitude = double.Parse(item["Longitude"].ToString());

                //default to canada
                cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c => c.ISOCode.ToLower() == "ca");
                //todo: detect country

                //set network operators
                if (this.SelectedNetworkType == NetworkType.ReseauVER)
                {
                    cp.OperatorInfo = new OperatorInfo { ID = 89 };
                }

                if (this.SelectedNetworkType == NetworkType.LeCircuitElectrique)
                {
                    cp.OperatorInfo = new OperatorInfo { ID = 90 };
                }

                bool isPublic = bool.Parse(item["IsPublic"].ToString());
                if (isPublic)
                {
                    cp.UsageType = usageTypePublic;
                }
                else
                {
                    cp.UsageType = usageTypePrivate;
                }

                cp.NumberOfPoints = int.Parse(item["NumPorts"].ToString());
                cp.StatusType = operationalStatus;

                //populate connectioninfo from Ports
                foreach (var port in item["Ports"].ToArray())
                {
                    ConnectionInfo cinfo = new ConnectionInfo() { };
                    ConnectionType cType = new ConnectionType { ID = 0 };

                    cinfo.Amps = int.Parse(port["Current"].ToString());
                    cinfo.Voltage = int.Parse(port["Voltage"].ToString());
                    cinfo.PowerKW = double.Parse(port["KiloWatts"].ToString());
                    cinfo.Level = new ChargerType() { ID = int.Parse(port["Level"].ToString()) };
                    //cinfo.Comments = (port["Make"]!=null?port["Make"].ToString()+" ":"") + (port["Model"]!=null?port["Model"].ToString():"");

                    if (port["ConnectorType"].ToString() == "J1772")
                    {
                        cType = coreRefData.ConnectionTypes.FirstOrDefault(c => c.ID == 1);
                    }
                    else if (port["ConnectorType"].ToString().ToUpper() == "CHADEMO")
                    {
                        cType = coreRefData.ConnectionTypes.FirstOrDefault(c => c.ID == 2);//CHADEMO
                    }
                    else
                    {
                        System.Diagnostics.Debug.WriteLine("Unmatched connector" + port["ConnectorType"].ToString());
                    }

                    cinfo.ConnectionType = cType;

                    if (cp.Connections == null)
                    {
                        cp.Connections = new List<ConnectionInfo>();
                        if (!IsConnectionInfoBlank(cinfo))
                        {
                            cp.Connections.Add(cinfo);
                        }
                    }
                }

                if (cp.DataQualityLevel == null) cp.DataQualityLevel = 4;

                cp.SubmissionStatus = submissionStatus;

                outputList.Add(cp);
                itemCount++;
            }

            return outputList;
        }
 public override void ParseAdditionalData(ChargePoint cp, XmlNode item, CoreReferenceData coreRefData)
 {
 }
        public CommonImportRefData(CoreReferenceData coreRefData)
        {
            SubmissionStatus_Imported = coreRefData.SubmissionStatusTypes.First(s => s.ID == 100); //imported and published
            SubmissionStatus_DelistedDupe = coreRefData.SubmissionStatusTypes.First(s => s.ID == 1001); //delisted duplicate

            Status_Unknown = coreRefData.StatusTypes.First(os => os.ID == 0);
            Status_Operational = coreRefData.StatusTypes.First(os => os.ID == 50);
            Status_NonOperational = coreRefData.StatusTypes.First(os => os.ID == 100);
            Status_PlannedForFuture = coreRefData.StatusTypes.First(os => os.ID == 150);

            UsageType_Public = coreRefData.UsageTypes.First(u => u.ID == 1);
            UsageType_Private = coreRefData.UsageTypes.First(u => u.ID == 2);
            UsageType_PublicPayAtLocation = coreRefData.UsageTypes.First(u => u.ID == 5);
            UsageType_PublicMembershipRequired = coreRefData.UsageTypes.First(u => u.ID == 4);
            UsageType_PublicNoticeRequired = coreRefData.UsageTypes.First(u => u.ID == 7);

            Operator_Unknown = coreRefData.Operators.First(opUnknown => opUnknown.ID == 1);

            ChrgLevel_1 = coreRefData.ChargerTypes.First(c => c.ID == 1);
            ChrgLevel_2 = coreRefData.ChargerTypes.First(c => c.ID == 2);
            ChrgLevel_3 = coreRefData.ChargerTypes.First(c => c.ID == 3);

            ConnectionType_Unknown = coreRefData.ConnectionTypes.First(c => c.ID == 0);
            ConnectionType_J1772 = coreRefData.ConnectionTypes.First(c => c.ID == 1);
            ConnectionType_CHADEMO = coreRefData.ConnectionTypes.First(c => c.ID == 2);
            ConnectionType_Type2Mennekes = coreRefData.ConnectionTypes.First(c => c.ID == 25);
        }
        public List<API.Common.Model.ChargePoint> Process(CoreReferenceData coreRefData)
        {
            //TODO: operator not well matched, usage type not known, multiple connectors at same site not imported due to duplicate POI. Requires merge process.
            List<ChargePoint> outputList = new List<ChargePoint>();

            var submissionStatus = coreRefData.SubmissionStatusTypes.First(s => s.ID == 100);//imported and published
            var operationalStatus = coreRefData.StatusTypes.First(os => os.ID == 50);
            var unknownStatus = coreRefData.StatusTypes.First(os => os.ID == 0);
            var usageTypePublic = coreRefData.UsageTypes.First(u => u.ID == 1);
            var usageTypePrivate = coreRefData.UsageTypes.First(u => u.ID == 2);
            var usageTypePrivateForStaffAndVisitors = coreRefData.UsageTypes.First(u => u.ID == 6); //staff and visitors
            var operatorUnknown = coreRefData.Operators.First(opUnknown => opUnknown.ID == 1);

            int itemCount = 0;

            string jsonString = "{ \"data\": " + InputData + "}";

            JObject o = JObject.Parse(jsonString);
            var dataList = o.Values().First().ToArray();

            var distinctCountries = new List<string>();
            foreach (var item in dataList)
            {
                /*
                 * {
              "id": "5813",
              "lng": "5.14287",
              "lat": "52.07858",
              "name": "NewMotion NL-TNM-FC11",
              "address": "Herculesplein 300",
              "postalcode": "3584 AA",
              "city": "Utrecht",
              "country": "NL",
              "phone": "",
              "url": "",
              "owner": "BP",
              "email": "",
              "opentimes": "ma-vr:6:00-23.30 za-zo:8:00-23.30 ",
              "chargetype": "DC snellader",
              "connectortype": "chademo",
              "nroutlets": "2",
              "cards": [
            "contant"
              ],
              "pricemethod": "per laadbeurt",
              "price": "6.00",
              "power": "50kW",
              "vehicle": "auto",
              "facilities": [
            "wifi",
            "wc",
            "parkeer",
            "restaurant",
            "wachtruimte",
            "koffiecorner",
            "shop",
            "openbaar vervoer"
              ],
              "realtimestatus": false
            }*/
                ChargePoint cp = new ChargePoint();
                cp.DataProvider = new DataProvider() { ID = this.DataProviderID }; //AddEnergie
                cp.DataProvidersReference = item["id"].ToString();
                cp.DateLastStatusUpdate = DateTime.UtcNow;

                cp.AddressInfo = new AddressInfo();

                cp.AddressInfo.Title = item["address"].ToString();
                cp.OperatorsReference = item["name"].ToString();

                cp.AddressInfo.AddressLine1 = item["address"].ToString().Trim();
                cp.AddressInfo.Town = item["city"].ToString().Trim();
                //cp.AddressInfo.StateOrProvince = item["StateOrProvince"].ToString().Trim();
                cp.AddressInfo.Postcode = item["postalcode"].ToString().Trim();
                cp.AddressInfo.Latitude = double.Parse(item["lat"].ToString());
                cp.AddressInfo.Longitude = double.Parse(item["lng"].ToString());

                var countryCode = item["country"].ToString().ToLower();

                if (!distinctCountries.Exists(c=>c==countryCode)) distinctCountries.Add(countryCode);

                //fix incorrect country codes
                if (countryCode == "au") countryCode = "at"; //austria, not australia
                if (countryCode == "ml") countryCode = "mt"; //malta, not mali
                if (countryCode == "tu") countryCode = "tr"; //turkey
                if (countryCode == "ad") countryCode = "";// leave for geocoding, probably not andorra
                if (countryCode == "sv") countryCode = "si"; //slovenia, not el salvador
                if (countryCode == "ir") countryCode = "ie"; //ireland, not iran

                cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c => c.ISOCode.ToLower() == countryCode);
                if (!String.IsNullOrEmpty(item["url"].ToString())) cp.AddressInfo.RelatedURL = item["url"].ToString();
                if (!String.IsNullOrEmpty(item["email"].ToString())) cp.AddressInfo.ContactEmail = item["email"].ToString();
                if (!String.IsNullOrEmpty(item["phone"].ToString())) cp.AddressInfo.ContactTelephone1 = item["phone"].ToString();

                var price = item["price"].ToString();
                var pricemethod = item["pricemethod"].ToString();

                cp.UsageCost = (!String.IsNullOrEmpty(price)?price+" ":"") + pricemethod;
                //set network operators
                //cp.OperatorInfo = new OperatorInfo { ID = 89 };

                //TODO: Operator, usage,price, power, connector type
                var owner = item["owner"].ToString().ToLower();
                var operatoInfo = coreRefData.Operators.FirstOrDefault(op=>op.Title.ToLower().Contains(owner));

                if (operatoInfo == null)
                {
                    Log("Unknown operator: "+owner);
                }
                else
                {
                    cp.OperatorID = operatoInfo.ID;
                }
                /*bool isPublic = bool.Parse(item["IsPublic"].ToString());
                if (isPublic)
                {
                    cp.UsageType = usageTypePublic;
                }
                else
                {
                    cp.UsageType = usageTypePrivate;
                }
                */

                cp.NumberOfPoints = int.Parse(item["nroutlets"].ToString());
                cp.StatusType = operationalStatus;

                //populate connectioninfo from Ports
                var connectorType = item["connectortype"].ToString();
                var chargetype = item["chargetype"].ToString();
                var power = item["power"].ToString();
                ConnectionInfo cinfo = new ConnectionInfo();

                try
                {
                    if (!String.IsNullOrEmpty(power))
                    {
                        cinfo.PowerKW = double.Parse(power.Replace("kW", ""));
                    }
                }
                catch (System.FormatException) { }

                if (connectorType.ToLower().Contains("j1772"))
                {
                    cinfo.ConnectionTypeID = (int)StandardConnectionTypes.J1772;
                    cinfo.LevelID = 2;
                } else  if (connectorType.ToLower().Contains("mennekes"))
                {
                    cinfo.ConnectionTypeID = (int)StandardConnectionTypes.MennekesType2;
                    cinfo.LevelID = 2;
                } else if (connectorType.ToLower().Contains("chademo"))
                {
                    cinfo.ConnectionTypeID = (int)StandardConnectionTypes.CHAdeMO;
                    cinfo.LevelID = 3;
                }
                else if (connectorType.ToLower().Contains("schuko"))
                {
                    cinfo.ConnectionTypeID = (int)StandardConnectionTypes.Schuko;
                    cinfo.LevelID = 2;
                }
                else {
                    Log("Unknown connectorType:" + connectorType);
                }

                if (cinfo.PowerKW >= 50)
                {
                    cinfo.LevelID = 3;
                }
                if (!String.IsNullOrEmpty(chargetype))
                {

                    if (chargetype.StartsWith("DC")) cinfo.CurrentTypeID = (int)StandardCurrentTypes.DC;
                    if (chargetype.StartsWith("AC simpel")) cinfo.CurrentTypeID = (int)StandardCurrentTypes.SinglePhaseAC;
                    //TODO: 3 phase?

                }

               // System.Diagnostics.Debug.WriteLine("Unknown chargetype:" + chargetype+ " "+power);

                if (cp.Connections == null)
                {
                    cp.Connections = new List<ConnectionInfo>();
                    if (!IsConnectionInfoBlank(cinfo))
                    {
                        cp.Connections.Add(cinfo);
                    }
                }

                if (cp.DataQualityLevel == null) cp.DataQualityLevel = 3;

                cp.SubmissionStatus = submissionStatus;

                outputList.Add(cp);
                itemCount++;
            }

            string temp = "";
            foreach (var countryCode in distinctCountries)
            {
                temp += ", " + countryCode;
            }
            Log("Countries in import:"+temp);

            return outputList;
        }
        public List<API.Common.Model.ChargePoint> Process(CoreReferenceData coreRefData)
        {
            ImportCommonReferenceData importRefData = new ImportCommonReferenceData(coreRefData);
            List<ChargePoint> outputList = new List<ChargePoint>();

            string jsString = InputData;// "{ \"data\": " + InputData + "}";
            JavaScriptSerializer jss = new JavaScriptSerializer();
            jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });
            dynamic parsedList = jss.Deserialize(jsString, typeof(object)) as dynamic;

            var dataList = parsedList.chargingstations;

            int itemCount = 0;
            foreach (var item in dataList)
            {
                /*
                 * {
               "chargingstations":[
                  {
                     "id":"14249",
                     "oid":"EM-NL-00000363-06-00006",
                     "brand":"Station City of Amsterdam",
                     "location_name":null,
                     "location_name_lang":null,
                     "latitude":52.401097,
                     "longitude":4.932926,
                     "street":"Buikslotermeerplein",
                     "house_number":"2000",
                     "postal_code":"1025 XL",
                     "city":"Amsterdam",
                     "country":"Netherlands",
                     "country_iso":"NLD",
                     "phone":"00318003773683",
                     "ac":true,
                     "dc":false,
                     "free":true,
                     "occupied":false,
                     "spotIds":"BA-3665-1, BA-3066-8"
                  },
                 * */
                ChargePoint cp = new ChargePoint();
                cp.DataProvider = new DataProvider() { ID = 21 }; //rwe-mobility
                cp.DataProvidersReference = item["id"].ToString();
                cp.OperatorsReference = item["oid"].ToString();
                cp.DateLastStatusUpdate = DateTime.UtcNow;
                cp.AddressInfo = new AddressInfo();

                try
                {

                    cp.AddressInfo.Title = item["location_name"] != null ? item["location_name"].ToString() : (item["brand"] != null ? item["brand"].ToString() : item["street"]);
                    cp.AddressInfo.Title = cp.AddressInfo.Title.Trim().Replace("&amp;", "&");
                }
                catch (Exception)
                {
                    //could not get a location title
                    System.Diagnostics.Debug.WriteLine("No title for item: " + cp.DataProvidersReference);
                }
                //cp.AddressInfo.RelatedURL = item["url"].ToString();

                cp.DateLastStatusUpdate = DateTime.UtcNow;
                cp.AddressInfo.AddressLine1 = item["house_number"] != null ? item["house_number"] + " " + item["street"].ToString() : item["street"].ToString().Trim();
                cp.AddressInfo.Town = item["city"].ToString().Trim();
                if (!String.IsNullOrEmpty(item["postal_code"])) cp.AddressInfo.Postcode = item["postal_code"].ToString().Trim();
                if (String.IsNullOrEmpty(cp.AddressInfo.Title)) cp.AddressInfo.Title = cp.AddressInfo.Town;

                cp.AddressInfo.Latitude = double.Parse(item["latitude"].ToString());
                cp.AddressInfo.Longitude = double.Parse(item["longitude"].ToString());

                if (item["phone"] != null)
                {
                    cp.AddressInfo.ContactTelephone1 = item["phone"].ToString();
                }

                //default to norway
                if (!String.IsNullOrEmpty(item["country"].ToString()))
                {
                    string country = item["country"].ToString();
                    int? countryID = null;

                    var countryVal = coreRefData.Countries.FirstOrDefault(c => c.Title.ToLower() == country.Trim().ToLower());
                    if (countryVal == null)
                    {
                        country = country.ToUpper();
                        //match country
                        if (country == "UNITED STATES" || country == "US" || country == "USA" || country == "U.S." || country == "U.S.A.") countryID = 2;

                        if (country == "UK" || country == "GB" || country == "GREAT BRITAIN" || country == "UNITED KINGDOM") countryID = 1;
                    }
                    else
                    {
                        countryID = countryVal.ID;
                    }

                    if (countryID == null)
                    {
                        this.Log("Country Not Matched, will require Geolocation:" + item["country"].ToString());

                    }
                    else
                    {
                        cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(cy => cy.ID == countryID);
                    }
                }
                else
                {
                    //default to US if no country identified
                    //cp.AddressInfo.Country = cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(cy => cy.ID == 2);
                }

                cp.UsageType = null;
                //TODO:identify usage type, number of points, connections

                cp.NumberOfPoints = 1;
                cp.StatusType = importRefData.Status_Operational;

                if (cp.DataQualityLevel == null) cp.DataQualityLevel = 2;

                cp.SubmissionStatus = importRefData.SubmissionStatus_ImportedAndPublished;

                outputList.Add(cp);
                itemCount++;
            }

            return outputList;
        }
Example #22
0
        /**
         * Generic Import Process

            Provider Properties
                Import Method
                Import URL/Path
                Import Frequency
                IsMaster

            Fetch Latest Data

            For each item
                Check If Exists or Strong Duplicate, Get ID
                If New, Add
                if Exists Then
                    Prepare update, if provider supports live status, set that
                        What if item updated manually on OCM?
                    Send Update
                End
            Loop

            Log Exceptions
            Log Count of Items Added or Modified

            Way to remove item (or log items) which no longer exist in data source?
         * */

        public async Task<List<ChargePoint>> DeDuplicateList(List<ChargePoint> cpList, bool updateDuplicate, CoreReferenceData coreRefData, ImportReport report, bool allowDupeWithDifferentOperator = false)
        {
            var stopWatch = new Stopwatch();
            stopWatch.Start();

            //get list of all current POIs (in relevant countries) including most delisted ones
            int[] countryIds = (from poi in cpList
                                where poi.AddressInfo.Country != null
                                select poi.AddressInfo.Country.ID).Distinct().ToArray();

            APIRequestParams filters = new APIRequestParams { CountryIDs = countryIds, MaxResults = 1000000, EnableCaching = false, SubmissionStatusTypeID = 0 };
            //List<ChargePoint> masterList = await new OCMClient(IsSandboxedAPIMode).GetLocations(filters); //new OCMClient().FindSimilar(null, 10000); //fetch all charge points regardless of status
            var poiManager = new POIManager();

            List<ChargePoint> masterListCollection = poiManager.GetChargePoints(filters); //new OCMClient().FindSimilar(null, 10000); //fetch all charge points regardless of status

            var spec = new i4o.IndexSpecification<ChargePoint>()
                    .Add(i => i.DataProviderID)
                    .Add(i => i.DataProvidersReference)
                    ;

            var masterList = new i4o.IndexSet<ChargePoint>(masterListCollection, spec);

            List<ChargePoint> masterListCopy = new List<ChargePoint>();
            foreach (var tmp in masterList)
            {
                //fully copy of master list item so we have before/after
                masterListCopy.Add(JsonConvert.DeserializeObject<ChargePoint>(JsonConvert.SerializeObject(tmp)));
            }

            //if we failed to get a master list, quit with no result
            if (masterListCollection.Count == 0) return new List<ChargePoint>();

            List<ChargePoint> duplicateList = new List<ChargePoint>();
            List<ChargePoint> updateList = new List<ChargePoint>();

            ChargePoint previousCP = null;

            //for each item to be imported, deduplicate by adding to updateList only the items which we don't already haves
            var cpListSortedByPos = cpList.OrderBy(c => c.AddressInfo.Latitude).ThenBy(c => c.AddressInfo.Longitude);

            int poiProcessed = 0;
            int totalPOI = cpListSortedByPos.Count();

            Stopwatch dupeIdentWatch = new Stopwatch();
            dupeIdentWatch.Start();

            foreach (var item in cpListSortedByPos)
            {
                var itemGeoPos = new System.Device.Location.GeoCoordinate(item.AddressInfo.Latitude, item.AddressInfo.Longitude);

                //item is duplicate if we already seem to have it based on Data Providers reference or approx position match
                var dupeList = masterList.Where(c =>
                        (c.DataProvider != null && c.DataProviderID == item.DataProviderID && c.DataProvidersReference == item.DataProvidersReference)
                        || (c.AddressInfo.Title == item.AddressInfo.Title && c.AddressInfo.AddressLine1 == item.AddressInfo.AddressLine1 && c.AddressInfo.Postcode == item.AddressInfo.Postcode)
                        || (GeoManager.IsClose(c.AddressInfo.Latitude, c.AddressInfo.Longitude, item.AddressInfo.Latitude, item.AddressInfo.Longitude) && new System.Device.Location.GeoCoordinate(c.AddressInfo.Latitude, c.AddressInfo.Longitude).GetDistanceTo(itemGeoPos) < DUPLICATE_DISTANCE_METERS) //meters distance apart
                );

                if (dupeList.Any())
                {
                    if (updateDuplicate)
                    {
                        //if updating duplicates, get exact matching duplicate based on provider reference and update/merge with this item to update status/merge properties
                        var updatedItem = dupeList.FirstOrDefault(d => d.DataProviderID == (item.DataProvider != null ? item.DataProvider.ID : item.DataProviderID) && d.DataProvidersReference == item.DataProvidersReference);
                        if (updatedItem != null)
                        {
                            //only merge/update from live published items
                            if (updatedItem.SubmissionStatus.IsLive == (bool?)true
                                || updatedItem.SubmissionStatus.ID == (int)StandardSubmissionStatusTypes.Delisted_RemovedByDataProvider
                                 || updatedItem.SubmissionStatus.ID == (int)StandardSubmissionStatusTypes.Delisted_NotPublicInformation)
                            {
                                //item is an exact match from same data provider
                                //overwrite existing with imported data (use import as master)
                                //updatedItem = poiManager.PreviewPopulatedPOIFromModel(updatedItem);
                                MergeItemChanges(item, updatedItem, false);

                                updateList.Add(updatedItem);
                            }
                        }

                        if (updatedItem == null)
                        {
                            //duplicates are not exact match
                            //TODO: resolve whether imported data should change duplicate

                            //merge new properties from imported item
                            //if (item.StatusType != null) updatedItem.StatusType = item.StatusType;
                            //updateList.Add(updatedItem);
                        }
                    }

                    //item has one or more likely duplicates, add it to list of items to remove
                    duplicateList.Add(item);
                }

                //mark item as duplicate if location/title exactly matches previous entry or lat/long is within DuplicateDistance meters

                if (previousCP != null)
                {
                    //this branch is the most expensive part of dedupe:
                    if (IsDuplicateLocation(item, previousCP, true))
                    {
                        if (!duplicateList.Contains(item))
                        {
                            if (allowDupeWithDifferentOperator && item.OperatorID != previousCP.OperatorID)
                            {
                                Log("Duplicated allowed due to different operator:" + item.AddressInfo.Title);
                            }
                            else
                            {
                                Log("Duplicated item removed:" + item.AddressInfo.Title);
                                duplicateList.Add(item);
                            }
                        }
                    }
                }

                previousCP = item;

                poiProcessed++;

                if (poiProcessed % 300 == 0)
                {
                    System.Diagnostics.Debug.WriteLine("Deduplication: " + poiProcessed + " processed of " + totalPOI);
                }
            }

            dupeIdentWatch.Stop();
            Log("De-dupe pass took " + dupeIdentWatch.Elapsed.TotalSeconds + " seconds. " + (dupeIdentWatch.Elapsed.TotalMilliseconds / cpList.Count) + "ms per item.");

            //remove duplicates from list to apply
            foreach (var dupe in duplicateList)
            {
                cpList.Remove(dupe);
            }

            Log("Duplicates removed from import:" + duplicateList.Count);

            //add updated items (replace duplicates with property changes)

            foreach (var updatedItem in updateList)
            {
                if (!cpList.Contains(updatedItem))
                {
                    cpList.Add(updatedItem);
                }
            }

            Log("Updated items to import:" + updateList.Count);

            //populate missing location info from geolocation cache if possible
            Stopwatch geoWatch = new Stopwatch();
            geoWatch.Start();
            PopulateLocationFromGeolocationCache(cpList, coreRefData);
            geoWatch.Stop();
            Log("Populate Country from Lat/Long took " + geoWatch.Elapsed.TotalSeconds + " seconds. " + (geoWatch.Elapsed.TotalMilliseconds / cpList.Count) + "ms per item.");

            //final pass to catch duplicates present in data source, mark additional items as Delisted Duplicate so we have a record for them
            var submissionStatusDelistedDupe = coreRefData.SubmissionStatusTypes.First(s => s.ID == 1001); //delisted duplicate
            previousCP = null;

            //sort current cp list by position again
            cpListSortedByPos = cpList.OrderBy(c => c.AddressInfo.Latitude).ThenBy(c => c.AddressInfo.Longitude);

            //mark any duplicates in final list as delisted duplicates (submitted to api)
            foreach (var cp in cpListSortedByPos)
            {
                bool isDuplicate = false;
                if (previousCP != null)
                {
                    isDuplicate = IsDuplicateLocation(cp, previousCP, false);
                    if (isDuplicate)
                    {
                        cp.SubmissionStatus = submissionStatusDelistedDupe;
                        cp.SubmissionStatusTypeID = submissionStatusDelistedDupe.ID;
                        if (previousCP.ID > 0)
                        {
                            if (cp.GeneralComments == null) cp.GeneralComments = "";
                            cp.GeneralComments += " [Duplicate of OCM-" + previousCP.ID + "]";
                            cp.ParentChargePointID = previousCP.ID;
                        }
                    }
                }

                if (!isDuplicate)
                {
                    previousCP = cp;
                }
            }

            report.Added = cpListSortedByPos.Where(cp => cp.ID == 0).ToList();
            report.Updated = cpListSortedByPos.Where(cp => cp.ID > 0).ToList();
            report.Duplicates = duplicateList; //TODO: add additional pass of duplicates from above

            //determine which POIs in our master list are no longer referenced in the import
            report.Delisted = masterList.Where(cp => cp.DataProviderID == report.ProviderDetails.DataProviderID && cp.SubmissionStatus != null && (cp.SubmissionStatus.IsLive == true || cp.SubmissionStatusTypeID == (int)StandardSubmissionStatusTypes.Imported_UnderReview)
                && !cpListSortedByPos.Any(master => master.ID == cp.ID) && !report.Duplicates.Any(master => master.ID == cp.ID)
                && cp.UserComments == null && cp.MediaItems == null).ToList();
            //safety check to ensure we're not delisting items just because we have incomplete import data:
            if (cpList.Count < 50)// || (report.Delisted.Count > cpList.Count))
            {
                report.Delisted = new List<ChargePoint>();
            }

            //determine list of low quality POIs (incomplete address info etc)
            report.LowDataQuality = new List<ChargePoint>();
            report.LowDataQuality.AddRange(GetLowDataQualityPOIs(report.Added));
            report.LowDataQuality.AddRange(GetLowDataQualityPOIs(report.Updated));

            Log("Removing " + report.LowDataQuality.Count + " low quality POIs from added/updated");

            //remove references in added/updated to any low quality POIs
            foreach (var p in report.LowDataQuality)
            {
                report.Added.Remove(p);
            }
            foreach (var p in report.LowDataQuality)
            {
                report.Updated.Remove(p);
            }

            //remove updates which only change datelaststatusupdate
            var updatesToIgnore = new List<ChargePoint>();
            foreach (var poi in report.Updated)
            {
                var origPOI = masterListCopy.FirstOrDefault(p => p.ID == poi.ID);
                var updatedPOI = poiManager.PreviewPopulatedPOIFromModel(poi);
                var differences = poiManager.CheckDifferences(origPOI, updatedPOI);
                differences.RemoveAll(d => d.Context == ".MetadataValues");
                differences.RemoveAll(d => d.Context == ".DateLastStatusUpdate");
                differences.RemoveAll(d => d.Context == ".UUID");

                differences.RemoveAll(d => d.Context == ".DataProvider.DateLastImported");
                differences.RemoveAll(d => d.Context == ".IsRecentlyVerified");
                differences.RemoveAll(d => d.Context == ".DateLastVerified");
                differences.RemoveAll(d => d.Context == ".UserComments");
                differences.RemoveAll(d => d.Context == ".MediaItems");

                if (!differences.Any())
                {
                    updatesToIgnore.Add(poi);
                }
                else
                {
                    //differences exist
                    CompareLogic compareLogic = new CompareLogic();
                    compareLogic.Config.MaxDifferences = 100;
                    compareLogic.Config.IgnoreObjectTypes = false;
                    compareLogic.Config.IgnoreUnknownObjectTypes = true;
                    compareLogic.Config.CompareChildren = true;
                    ComparisonResult result = compareLogic.Compare(origPOI, updatedPOI);

                    var diffReport = new KellermanSoftware.CompareNetObjects.Reports.UserFriendlyReport();
                    result.Differences.RemoveAll(d => d.PropertyName == ".MetadataValues");
                    result.Differences.RemoveAll(d => d.PropertyName == ".DateLastStatusUpdate");
                    result.Differences.RemoveAll(d => d.PropertyName == ".UUID");
                    result.Differences.RemoveAll(d => d.PropertyName == ".DataProvider.DateLastImported");
                    result.Differences.RemoveAll(d => d.PropertyName == ".IsRecentlyVerified");
                    result.Differences.RemoveAll(d => d.PropertyName == ".DateLastVerified");
                    result.Differences.RemoveAll(d => d.PropertyName == ".UserComments");
                    result.Differences.RemoveAll(d => d.PropertyName == ".MediaItems");
                    System.Diagnostics.Debug.WriteLine("Difference:" + diffReport.OutputString(result.Differences));

                    if (!result.Differences.Any())
                    {
                        updatesToIgnore.Add(poi);
                    }
                }
            }

            foreach (var p in updatesToIgnore)
            {
                if (report.Unchanged == null) report.Unchanged = new List<ChargePoint>();
                report.Unchanged.Add(p);
                report.Updated.Remove(p);
            }

            //TODO: if POi is a duplicate ensure imported data provider reference/URL  is included as reference metadata in OCM's version of the POI

            stopWatch.Stop();
            Log("Deduplicate List took " + stopWatch.Elapsed.TotalSeconds + " seconds");

            //return final processed list ready for applying as insert/updates
            return cpListSortedByPos.ToList();
        }
        List<ChargePoint> IImportProvider.Process(CoreReferenceData coreRefData)
        {
            List<ChargePoint> outputList = new List<ChargePoint>();

            var submissionStatus = coreRefData.SubmissionStatusTypes.First(s => s.ID == 100);//imported and published
            var operationalStatus = coreRefData.StatusTypes.First(os => os.ID == 50);
            var operationalMixedStatus = coreRefData.StatusTypes.First(os => os.ID == 75);
            var unknownStatus = coreRefData.StatusTypes.First(os => os.ID == 0);
            var usageTypePublic = coreRefData.UsageTypes.First(u => u.ID == 1);
            var usageTypePrivate = coreRefData.UsageTypes.First(u => u.ID == 2);

            var networkOperator = coreRefData.Operators.First(op=>op.ID==9); //blink/ecotality

            string jsString = InputData;
            jsString = "{ \"data\": " + jsString + "}"; //fix data by wrapping on container

            JObject o = JObject.Parse(jsString);

            var response = o.Values();
            var data = response.Values();
            var dataList = data.Values().ToArray();
            int itemCount = 0;

            foreach (var item in data)
            {
                bool skipItem = false;
                try
                {
                    ChargePoint cp = new ChargePoint();
                    cp.AddressInfo = new AddressInfo();

                    cp.OperatorInfo = networkOperator;
                    cp.OperatorsReference = item["encid"].ToString();
                    cp.DataProvider = new DataProvider() { ID = this.DataProviderID }; //blinknetwork.com
                    cp.DataProvidersReference = item["id"].ToString();
                    cp.DateLastStatusUpdate = DateTime.UtcNow;

                    cp.AddressInfo.Title = item["name"] != null ? item["name"].ToString() : item["name"].ToString();
                    cp.AddressInfo.RelatedURL = "http://www.blinknetwork.com";
                    cp.DateLastStatusUpdate = DateTime.UtcNow;

                    cp.AddressInfo.Latitude = double.Parse(item["latitude"].ToString());
                    cp.AddressInfo.Longitude = double.Parse(item["longitude"].ToString());

                    cp.AddressInfo.AddressLine1 = item["address1"].ToString();
                    cp.AddressInfo.AddressLine2 = item["address2"].ToString();
                    cp.AddressInfo.Town = item["city"].ToString();
                    cp.AddressInfo.StateOrProvince = item["state"].ToString();
                    cp.AddressInfo.Postcode = item["zip"].ToString();

                    //set country property
                    cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c => c.ISOCode == item["country"].ToString());

                    string usageTypeCode = item["type"].ToString();

                    switch (usageTypeCode) {
                        case "COMMERCIAL":  cp.UsageType = coreRefData.UsageTypes.FirstOrDefault(u => u.ID == 5); //pay at location
                            break;
                        case "RESIDENTIAL": skipItem=true;
                            break;
                        default:
                            Log("Unmatched usage type:"+usageTypeCode);
                            break;
                    }

                    cp.NumberOfPoints = int.Parse(item["chargers"].ToString());
                    int numOffline = int.Parse(item["offline"].ToString());
                    if (numOffline > 0)
                    {
                        cp.StatusType = operationalMixedStatus;
                    }
                    else
                    {
                        cp.StatusType = operationalStatus;
                    }

                    //populate connections
                    cp.Connections = new List<ConnectionInfo>();
                    var levelTypes = item["levels"].ToArray();
                    foreach (var level in levelTypes)
                    {
                        ConnectionInfo con = new ConnectionInfo();
                        if (level.ToString() == "1")
                        {
                            con.ConnectionType = new ConnectionType { ID = 1 };//J1772
                            con.Level = new ChargerType { ID = 1 };
                        }
                        if (level.ToString() == "2")
                        {
                            con.ConnectionType = new ConnectionType { ID = 1 };//J1772
                            con.Voltage = 220;
                            con.Level = new ChargerType { ID = 2 };
                        }
                        if (level.ToString() == "3")
                        {
                            con.ConnectionType = new ConnectionType { ID = 3 };//J1772
                            con.Voltage = 480;
                            con.Level = new ChargerType { ID = 3 };
                        }
                        cp.Connections.Add(con);
                    }
                    cp.DataQualityLevel = 3; //avg, higher than default

                    cp.SubmissionStatus = submissionStatus;
                    if (!skipItem) outputList.Add(cp);
                }
                catch (Exception)
                {
                    Log("Error parsing item " + itemCount);
                }

                itemCount++;
            }

            return outputList;
        }
        public List<API.Common.Model.ChargePoint> Process(CoreReferenceData coreRefData)
        {
            List<ChargePoint> outputList = new List<ChargePoint>();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(InputData);

            XmlNodeList dataList = xmlDoc.SelectNodes("//chargerstation");

            var submissionStatus = coreRefData.SubmissionStatusTypes.First(s => s.ID == 100);//imported and published
            var operationalStatus = coreRefData.StatusTypes.First(os => os.ID == 50);
            var unknownStatus = coreRefData.StatusTypes.First(os => os.ID == 0);
            var usageTypePublic = coreRefData.UsageTypes.First(u => u.ID == 1);
            var usageTypePrivate = coreRefData.UsageTypes.First(u => u.ID == 2);
            var usageTypePrivateForStaffAndVisitors = coreRefData.UsageTypes.First(u => u.ID == 6); //staff and visitors
            var operatorUnknown = coreRefData.Operators.First(opUnknown => opUnknown.ID == 1);

            int itemCount = 0;
            foreach (XmlNode chargerstation in dataList)
            {
                var item = chargerstation.SelectNodes("metadata").Item(0);
                ChargePoint cp = new ChargePoint();
                cp.DataProvider = new DataProvider() { ID = this.DataProviderID }; //nobil.no
                cp.DataProvidersReference = item["id"].InnerText; //is id unique across countries?
                cp.DateLastStatusUpdate = DateTime.UtcNow;
                cp.AddressInfo = new AddressInfo();

                cp.AddressInfo.Title = item["name"].InnerText != null ? item["name"].InnerText : item["Street"].InnerText;
                cp.AddressInfo.Title = cp.AddressInfo.Title.Trim().Replace("&amp;", "&");
                //cp.AddressInfo.RelatedURL = item["url"].ToString();

                cp.DateLastStatusUpdate = DateTime.UtcNow;
                cp.AddressInfo.AddressLine1 = item["Street"].InnerText;
                if (item["House_number"] != null) cp.AddressInfo.AddressLine1 += " " + item["House_number"].InnerText;
                cp.AddressInfo.Town = item["City"].InnerText.Trim();
                cp.AddressInfo.StateOrProvince = item["County"].InnerText.Trim();
                cp.AddressInfo.Postcode = item["Zipcode"].InnerText.Trim();
                string posString = item["Position"].InnerText.Trim();

                int sepPos = posString.IndexOf(",") - 1;
                string lat = posString.Substring(1, sepPos);
                sepPos += 2;
                string lon = posString.Substring(sepPos, (posString.Length - sepPos) - 1);
                cp.AddressInfo.Latitude = double.Parse(lat);
                cp.AddressInfo.Longitude = double.Parse(lon);

                //default to norway
                var countryCode = item["Land_code"].InnerText;
                if (countryCode.ToUpper() == "NOR")
                {
                    cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c => c.ISOCode.ToLower() == "no");
                }
                else if (countryCode.ToUpper() == "FIN")
                {
                    cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c => c.ISOCode.ToLower() == "fi");
                }
                else if (countryCode.ToUpper() == "SWE")
                {
                    cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c => c.ISOCode.ToLower() == "se");
                }
                else if (countryCode.ToUpper() == "DAN")
                {
                    cp.AddressInfo.Country = coreRefData.Countries.FirstOrDefault(c => c.ISOCode.ToLower() == "dk");
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("Unknown country code:" + countryCode);
                }

                cp.AddressInfo.AccessComments = item["Description_of_location"].InnerText;
                cp.AddressInfo.GeneralComments = item["Contact_info"].InnerText;

                cp.NumberOfPoints = int.Parse(item["Number_charging_points"].InnerText);

                var attributes = chargerstation.SelectNodes("attributes").Item(0);
                var connectors = attributes.SelectSingleNode("connectors").SelectNodes("connector");
                foreach (XmlNode connector in connectors)
                {
                    var connectorAttribs = connector.SelectSingleNode("attribute[attrtypeid=4]");
                    var chargingCapacityAttribs = connector.SelectSingleNode("attribute[attrtypeid=5]");
                    var chargingModeAttribs = connector.SelectSingleNode("attribute[attrtypeid=20]");
                    ConnectionInfo cinfo = new ConnectionInfo() { };
                    cinfo.Reference = connector.Attributes["id"].InnerText;

                    ConnectionType cType = new ConnectionType { ID = 0 };
                    if (connectorAttribs != null)
                    {
                        var connectorTypeVal = connectorAttribs.SelectSingleNode("attrvalid").InnerText;
                        if (connectorTypeVal == "14")
                        {
                            cType.ID = 28;// Schuko CEE 7/4
                        }
                        else if (connectorTypeVal == "40")
                        {
                            cType.ID = 27;// tesla supercharger connnector
                        }
                        else if (connectorTypeVal == "31")
                        {
                            //type 1 == J1772?
                            cType.ID = (int)StandardConnectionTypes.J1772;
                        }
                        else if (connectorTypeVal == "29")
                        {
                            cType.ID = 8;// tesla roadster
                        }
                        else if (connectorTypeVal == "32")
                        {
                            cType.ID = 25;// type 2 (mennekes)
                        }
                        else if (connectorTypeVal == "50")
                        {
                            cType.ID = 28;// type 2 + schuko both present?
                        }
                        else if (connectorTypeVal == "30")
                        {
                            cType.ID = (int)StandardConnectionTypes.CHAdeMO;
                        }
                        else if (connectorTypeVal == "34")
                        {
                            cType.ID = 34;//IEC 60309 3 pin
                        }
                        else if (connectorTypeVal == "36")
                        {
                            cType.ID = 35;//IEC 60309 5 pin
                        }
                        else if (connectorTypeVal == "39")
                        {
                            cType.ID = 33;//Type 2 of CCS coupler
                        }
                        else if (connectorTypeVal == "41")
                        {
                            cType.ID = (int)StandardConnectionTypes.CHAdeMO;//CCS combo + Chademo both present
                        }
                        else if (connectorTypeVal == "43")
                        {
                            cType.ID = (int)StandardConnectionTypes.CHAdeMO;//CHAdeMO + Combo + AC-Type2 all present
                        }
                        else if (connectorTypeVal == "0")
                        {
                            cType.ID = 0;//unknown
                        }
                        else
                        {
                            System.Diagnostics.Debug.WriteLine("Unnknown connectorDetails: " + connectorAttribs.InnerText);
                        }
                    }

                    if (chargingCapacityAttribs != null)
                    {
                        //TODO: 3-Phase power calcs are wrong.
                        var connectorTypeVal = chargingCapacityAttribs.SelectSingleNode("attrvalid").InnerText;
                        if (connectorTypeVal == "7")
                        {
                            cinfo.Amps = 16;
                            cinfo.Voltage = 230;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.SinglePhaseAC };
                            cinfo.Level = new ChargerType() { ID = 2 }; //default to lvl2
                        }
                        else if (connectorTypeVal == "8")
                        {
                            cinfo.Amps = 32;
                            cinfo.Voltage = 230;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.SinglePhaseAC };
                            cinfo.Level = new ChargerType() { ID = 2 }; //default to lvl2
                        }
                        else if (connectorTypeVal == "10")
                        {
                            cinfo.Amps = 16;
                            cinfo.Voltage = 400;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.ThreePhaseAC };
                            cinfo.Level = new ChargerType() { ID = 2 }; //default to lvl2
                        }
                        else if (connectorTypeVal == "11")
                        {
                            //500V DC
                            cinfo.Amps = 200;
                            cinfo.Voltage = 500;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.DC };
                            cinfo.Level = new ChargerType() { ID = 3 };
                        }
                        else if (connectorTypeVal == "12")
                        {
                            //400V AC (3 Phase) 63A
                            cinfo.Amps = 63;
                            cinfo.Voltage = 400;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.ThreePhaseAC };
                            cinfo.Level = new ChargerType() { ID = 3 };
                        }
                        else if (connectorTypeVal == "13")
                        {
                            //tesla super charger
                            cinfo.Amps = 200;
                            cinfo.Voltage = 500;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.DC };
                            cinfo.Level = new ChargerType() { ID = 3 };
                        }
                        else if (connectorTypeVal == "16")
                        {
                            cinfo.Amps = 16;
                            cinfo.Voltage = 230;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.ThreePhaseAC };
                            cinfo.Level = new ChargerType() { ID = 2 }; //default to lvl2
                        }
                        else if (connectorTypeVal == "17")
                        {
                            cinfo.Amps = 32;
                            cinfo.Voltage = 230;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.ThreePhaseAC };
                            cinfo.Level = new ChargerType() { ID = 2 }; //default to lvl2
                        }
                        else if (connectorTypeVal == "19")
                        {
                            //500V DC MAX 50A
                            cinfo.Amps = 50;
                            cinfo.Voltage = 500;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.DC };
                            cinfo.Level = new ChargerType() { ID = 3 };
                        }
                        else if (connectorTypeVal == "20")
                        {
                            //TODO: 500VDC max 200A + 400V 3-phase max 63A
                            cinfo.Amps = 200;
                            cinfo.Voltage = 500;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.DC };
                            cinfo.Level = new ChargerType() { ID = 3 };
                        }
                        else if (connectorTypeVal == "22")
                        {
                            //480VDC max 270A
                            cinfo.Amps = 270;
                            cinfo.Voltage = 480;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.DC };
                            cinfo.Level = new ChargerType() { ID = 3 };
                        }
                        else if (connectorTypeVal == "27")
                        {
                            //tesla super charger
                            cinfo.Amps = 200;
                            cinfo.Voltage = 500;
                            cinfo.CurrentType = new CurrentType { ID = (int)StandardCurrentTypes.DC };
                            cinfo.Level = new ChargerType() { ID = 3 };
                        }
                        else if (connectorTypeVal == "0")
                        {
                            //unknown power level
                        }
                        else
                        {
                            System.Diagnostics.Debug.WriteLine("unknown chargingCapacity: " + chargingCapacityAttribs.InnerText);
                        }
                    }

                    if (cinfo.Amps > 0 && cinfo.Voltage > 0)
                    {
                        cinfo.PowerKW = (cinfo.Amps * cinfo.Voltage / 1000);
                    }

                    cinfo.ConnectionType = cType;

                    if (chargingModeAttribs != null)
                    {
                        var chargeMode = chargingModeAttribs.SelectSingleNode("trans");
                        if (chargeMode != null)
                        {
                            cinfo.Comments = chargeMode.InnerText;
                        }
                    }
                    if (cp.Connections == null)
                    {
                        cp.Connections = new List<ConnectionInfo>();
                        if (!IsConnectionInfoBlank(cinfo))
                        {
                            cp.Connections.Add(cinfo);
                        }
                    }
                }

                if (cp.DataQualityLevel == null) cp.DataQualityLevel = 2;

                cp.SubmissionStatus = submissionStatus;

                outputList.Add(cp);
                itemCount++;
            }

            return outputList;
        }