//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) { OCMEntities tempDataModel = new OCMEntities(); var poiData = new Core.Data.ChargePoint(); if (poi.ID > 0) { poiData = tempDataModel.ChargePoints.First(c => c.ID == poi.ID); } //convert simple poi to fully populated db version new POIManager().PopulateChargePoint_SimpleToData(poi, poiData, tempDataModel); //convert back to simple POI var modelPOI = Model.Extensions.ChargePoint.FromDataModel(poiData, false, false, true, true); //clear temp changes from the poi //dataModel.Entry(poiData).Reload(); tempDataModel.Dispose(); return(modelPOI); }
public static Model.ChargePoint FromDataModel(Core.Data.ChargePoint source) { return(FromDataModel(source, false, false, false, true)); }
public static Model.ChargePoint FromDataModel(Core.Data.ChargePoint source, bool loadUserComments, bool loadMediaItems, bool loadMetadataValues, bool isVerboseMode) { if (source == null) { return(null); } var poi = new Model.ChargePoint(); poi.ID = source.ID; poi.UUID = source.UUID; //populate data provider info (full object or id only) if (isVerboseMode && source.DataProvider != null) { poi.DataProvider = DataProvider.FromDataModel(source.DataProvider); poi.DataProviderID = source.DataProvider.ID; } else { poi.DataProviderID = source.DataProviderID; } poi.DataProvidersReference = source.DataProvidersReference; //populate Operator (full object or id only) if (isVerboseMode && source.Operator != null) { poi.OperatorInfo = OperatorInfo.FromDataModel(source.Operator); poi.OperatorID = source.Operator.ID; } else { poi.OperatorID = source.OperatorID; } poi.OperatorsReference = source.OperatorsReference; //populate usage type (full object or id only) if (isVerboseMode && source.UsageType != null) { poi.UsageType = UsageType.FromDataModel(source.UsageType); poi.UsageTypeID = source.UsageType.ID; } else { poi.UsageTypeID = source.UsageTypeID; } poi.UsageCost = source.UsageCost; //populate address info if (source.AddressInfo != null) { poi.AddressInfo = AddressInfo.FromDataModel(source.AddressInfo, isVerboseMode); } poi.NumberOfPoints = source.NumberOfPoints; poi.GeneralComments = source.GeneralComments; poi.DatePlanned = source.DatePlanned; poi.DateLastConfirmed = source.DateLastConfirmed; //populate status type (full object or id only) if (isVerboseMode && source.StatusType != null) { poi.StatusType = StatusType.FromDataModel(source.StatusType); poi.StatusTypeID = source.StatusType.ID; } else { poi.StatusTypeID = source.StatusTypeID; } poi.DateLastStatusUpdate = source.DateLastStatusUpdate; poi.DataQualityLevel = source.DataQualityLevel; poi.DateCreated = source.DateCreated; //populate submission status type (full object or id only) if (isVerboseMode && source.SubmissionStatusType != null) { poi.SubmissionStatus = SubmissionStatusType.FromDataModel(source.SubmissionStatusType); poi.SubmissionStatusTypeID = source.SubmissionStatusType.ID; } else { poi.SubmissionStatusTypeID = source.SubmissionStatusTypeID; } poi.Connections = new List <Model.ConnectionInfo>(); foreach (var conn in source.Connections) { poi.Connections.Add(ConnectionInfo.FromDataModel(conn, isVerboseMode)); } //loadUserComments = true; //loadMetadataValues = true; //loadMediaItems = true; //optionally load user comments if (loadUserComments) { foreach (var comment in source.UserComments.OrderByDescending(cm => cm.DateCreated)) { if (poi.UserComments == null) { poi.UserComments = new List <Model.UserComment>(); } Model.UserComment com = UserComment.FromDataModel(comment, isVerboseMode); poi.UserComments.Add(com); } } if (loadMediaItems) { foreach (var mediaItem in source.MediaItems) { if (poi.MediaItems == null) { poi.MediaItems = new List <Model.MediaItem>(); } poi.MediaItems.Add(MediaItem.FromDataModel(mediaItem)); } } if (loadMetadataValues) { foreach (var metadataValue in source.MetadataValues) { if (poi.MetadataValues == null) { poi.MetadataValues = new List <Model.MetadataValue>(); } poi.MetadataValues.Add(MetadataValue.FromDataModel(metadataValue)); } } return(poi); }
public static Model.ChargePoint FromDataModel(Core.Data.ChargePoint source, Model.CoreReferenceData refData) { return(FromDataModel(source, false, false, false, true, refData)); }
public static Model.ChargePoint FromDataModel(Core.Data.ChargePoint source, bool loadUserComments, bool loadMediaItems, bool loadMetadataValues, bool isVerboseMode, Model.CoreReferenceData refData) { if (source == null) { return(null); } var poi = new Model.ChargePoint(); poi.ID = source.Id; poi.UUID = source.Uuid; //populate data provider info (full object or id only) if (isVerboseMode && source.DataProviderId != null) { poi.DataProvider = refData.DataProviders.FirstOrDefault(i => i.ID == source.DataProviderId) ?? DataProvider.FromDataModel(source.DataProvider); poi.DataProviderID = source.DataProviderId; } else { poi.DataProviderID = source.DataProviderId; } poi.DataProvidersReference = source.DataProvidersReference; //populate Operator (full object or id only) if (isVerboseMode && source.OperatorId != null) { poi.OperatorInfo = refData.Operators.FirstOrDefault(i => i.ID == source.OperatorId) ?? OperatorInfo.FromDataModel(source.Operator); poi.OperatorID = source.OperatorId; } else { poi.OperatorID = source.OperatorId; } poi.OperatorsReference = source.OperatorsReference; //populate usage type (full object or id only) if (isVerboseMode && source.UsageTypeId != null) { poi.UsageType = refData.UsageTypes.FirstOrDefault(i => i.ID == source.UsageTypeId) ?? UsageType.FromDataModel(source.UsageType); poi.UsageTypeID = source.UsageTypeId; } else { poi.UsageTypeID = source.UsageTypeId; } poi.UsageCost = source.UsageCost; //populate address info if (source.AddressInfoId != null || source.AddressInfo != null) { poi.AddressInfo = AddressInfo.FromDataModel(source.AddressInfo, isVerboseMode); } poi.NumberOfPoints = source.NumberOfPoints; poi.GeneralComments = source.GeneralComments; poi.DatePlanned = source.DatePlanned; poi.DateLastConfirmed = source.DateLastConfirmed; //populate status type (full object or id only) if (isVerboseMode && source.StatusTypeId != null) { poi.StatusType = refData.StatusTypes.FirstOrDefault(i => i.ID == source.StatusTypeId) ?? StatusType.FromDataModel(source.StatusType); poi.StatusTypeID = source.StatusTypeId; } else { poi.StatusTypeID = source.StatusTypeId; } poi.DateLastStatusUpdate = source.DateLastStatusUpdate; poi.DataQualityLevel = source.DataQualityLevel; poi.DateCreated = source.DateCreated; //populate submission status type (full object or id only) if (isVerboseMode && source.SubmissionStatusTypeId != null) { poi.SubmissionStatus = refData.SubmissionStatusTypes.FirstOrDefault(i => i.ID == source.SubmissionStatusTypeId) ?? SubmissionStatusType.FromDataModel(source.SubmissionStatusType); poi.SubmissionStatusTypeID = source.SubmissionStatusTypeId; } else { poi.SubmissionStatusTypeID = source.SubmissionStatusTypeId; } poi.Connections = new List <Model.ConnectionInfo>(); foreach (var conn in source.ConnectionInfoes) { poi.Connections.Add(ConnectionInfo.FromDataModel(conn, isVerboseMode, refData)); } //loadUserComments = true; //loadMetadataValues = true; //loadMediaItems = true; //optionally load user comments if (loadUserComments) { foreach (var comment in source.UserComments.OrderByDescending(cm => cm.DateCreated)) { if (poi.UserComments == null) { poi.UserComments = new List <Model.UserComment>(); } Model.UserComment com = UserComment.FromDataModel(comment, isVerboseMode, refData); poi.UserComments.Add(com); } } if (loadMediaItems) { foreach (var mediaItem in source.MediaItems.OrderByDescending(cm => cm.DateCreated)) { if (poi.MediaItems == null) { poi.MediaItems = new List <Model.MediaItem>(); } poi.MediaItems.Add(MediaItem.FromDataModel(mediaItem)); } } if (loadMetadataValues) { foreach (var metadataValue in source.MetadataValues) { if (poi.MetadataValues == null) { poi.MetadataValues = new List <Model.MetadataValue>(); } poi.MetadataValues.Add(MetadataValue.FromDataModel(metadataValue)); } } //mapping level of detail (priority level, lower is higher priority) poi.LevelOfDetail = source.LevelOfDetail; return(poi); }
public void ProcessEditQueueItem(int id, bool publishEdit, int userId) { //check current user is authorized to approve edits //prepare poi details var queueItem = DataModel.EditQueueItems.FirstOrDefault(e => e.ID == id); if (queueItem != null && queueItem.IsProcessed == false) { if (queueItem.EntityType.ID == (int)StandardEntityTypes.POI) { //processing a POI add/edit if (publishEdit) { //get diff between previous and edit POIManager poiManager = new POIManager(); Model.ChargePoint poiA = DeserializePOIFromJSON(queueItem.PreviousData); Model.ChargePoint poiB = DeserializePOIFromJSON(queueItem.EditData); bool poiUpdateRequired = false; if (poiA != null) { //this is an edit, load the latest version of the POI as version 'A' poiA = poiManager.Get(poiA.ID); if (poiManager.HasDifferences(poiA, poiB)) { poiUpdateRequired = true; } } //save poi update var poiData = new Core.Data.ChargePoint(); //if its an edit, load the original details before applying the change if (poiUpdateRequired) { //updates to externally provided POIs require old version to be superseded (archived) first if (poiA != null && poiA.DataProviderID != (int)StandardDataProviders.OpenChargeMapContrib) { poiManager.SupersedePOI(DataModel, poiA, poiB); } poiData = DataModel.ChargePoints.First(c => c.ID == poiB.ID); } //set/update cp properties poiManager.PopulateChargePoint_SimpleToData(poiB, poiData, DataModel); //set status type to published if previously unset if (poiData.SubmissionStatusTypeID == null) { poiData.SubmissionStatusType = DataModel.SubmissionStatusTypes.First(s => s.ID == (int)StandardSubmissionStatusTypes.Submitted_Published); } poiData.DateLastStatusUpdate = DateTime.UtcNow; //publish edit DataModel.SaveChanges(); //attribute submitter with reputation points if (queueItem.UserID != null) { new UserManager().AddReputationPoints((int)queueItem.UserID, 1); } } //update edit queue item as processed queueItem.IsProcessed = true; queueItem.ProcessedByUser = DataModel.Users.FirstOrDefault(u => u.ID == userId); queueItem.DateProcessed = DateTime.UtcNow; DataModel.SaveChanges(); CacheManager.RefreshCachedPOIList(); } } }
private static void SendNewPOISubmissionNotification(Model.ChargePoint poi, Model.User user, Core.Data.ChargePoint cpData) { try { string approvalStatus = cpData.SubmissionStatusType.Title; //send notification NotificationManager notification = new NotificationManager(); Hashtable msgParams = new Hashtable(); msgParams.Add("Description", "OCM-" + cpData.Id + " : " + poi.AddressInfo.Title); msgParams.Add("SubmissionStatusType", approvalStatus); msgParams.Add("ItemURL", "https://openchargemap.org/site/poi/details/" + cpData.Id); msgParams.Add("ChargePointID", cpData.Id); msgParams.Add("UserName", user != null ? user.Username : "******"); msgParams.Add("MessageBody", "New Location " + approvalStatus + " OCM-" + cpData.Id + " Submitted: " + poi.AddressInfo.Title); notification.PrepareNotification(NotificationType.LocationSubmitted, msgParams); //notify default system recipients notification.SendNotification(NotificationType.LocationSubmitted); } catch (Exception) { ; ; //failed to send notification } }
/// <summary> /// Consumers should prepare a new/updated ChargePoint with as much info populated as possible /// </summary> /// <param name="submission">ChargePoint info for submission, if ID and UUID set will be treated as an update</param> /// <returns>false on error or not enough data supplied</returns> public int PerformPOISubmission(Model.ChargePoint updatedPOI, Model.User user) { try { var poiManager = new POIManager(); bool enableEditQueueLogging = bool.Parse(ConfigurationManager.AppSettings["EnableEditQueue"]); bool isUpdate = false; bool userCanEditWithoutApproval = false; bool isSystemUser = false; //if user signed in, check if they have required permission to perform an edit/approve (if required) if (user != null) { if (user.ID == (int)StandardUsers.System) { isSystemUser = true; } //if user is system user, edits/updates are not recorded in edit queue if (isSystemUser) { enableEditQueueLogging = false; } userCanEditWithoutApproval = POIManager.CanUserEditPOI(updatedPOI, user); } var dataModel = new Core.Data.OCMEntities(); //if poi is an update, validate if update can be performed if (updatedPOI.ID > 0 && !String.IsNullOrEmpty(updatedPOI.UUID)) { if (dataModel.ChargePoints.Any(c => c.ID == updatedPOI.ID && c.UUID == updatedPOI.UUID)) { //update is valid poi, check if user has permission to perform an update isUpdate = true; if (userCanEditWithoutApproval) { AllowUpdates = true; } if (!AllowUpdates && !enableEditQueueLogging) { return(-1); //valid update requested but updates not allowed } } else { //update does not correctly identify an existing poi return(-1); } } //validate if minimal required data is present if (updatedPOI.AddressInfo.Title == null || updatedPOI.AddressInfo.Country == null) { return(-1); } //convert to DB version of POI and back so that properties are fully populated updatedPOI = PopulateFullPOI(updatedPOI); Model.ChargePoint oldPOI = null; if (updatedPOI.ID > 0) { //get json snapshot of current cp data to store as 'previous' oldPOI = poiManager.Get(updatedPOI.ID); } //if user cannot edit directly, add to edit queue for approval var editQueueItem = new Core.Data.EditQueueItem { DateSubmitted = DateTime.UtcNow }; if (enableEditQueueLogging) { editQueueItem.EntityID = updatedPOI.ID; editQueueItem.EntityType = dataModel.EntityTypes.FirstOrDefault(t => t.ID == 1); //charging point location entity type id //serialize cp as json var jsonOutput = new OutputProviders.JSONOutputProvider(); //null extra data we don't want to serialize/compare updatedPOI.UserComments = null; updatedPOI.MediaItems = null; string editData = jsonOutput.PerformSerialisationToString(updatedPOI, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); editQueueItem.EditData = editData; if (updatedPOI.ID > 0) { //check if poi will change with this edit, if not we discard it completely if (!poiManager.HasDifferences(oldPOI, updatedPOI)) { System.Diagnostics.Debug.WriteLine("POI Update has no changes, discarding change."); return(updatedPOI.ID); } else { editQueueItem.PreviousData = jsonOutput.PerformSerialisationToString(oldPOI, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); } } if (user != null) { editQueueItem.User = dataModel.Users.FirstOrDefault(u => u.ID == user.ID); } editQueueItem.IsProcessed = false; editQueueItem = dataModel.EditQueueItems.Add(editQueueItem); //TODO: send notification of new item for approval //save edit queue item dataModel.SaveChanges(); //if previous edit queue item exists by same user for same POI, mark as processed var previousEdits = dataModel.EditQueueItems.Where(e => e.UserID == editQueueItem.UserID && e.EntityID == editQueueItem.EntityID && e.EntityTypeID == editQueueItem.EntityTypeID && e.ID != editQueueItem.ID && e.IsProcessed != true); foreach (var previousEdit in previousEdits) { previousEdit.IsProcessed = true; if (editQueueItem.User != null) { previousEdit.ProcessedByUser = editQueueItem.User; } else { editQueueItem.ProcessedByUser = dataModel.Users.FirstOrDefault(u => u.ID == (int)StandardUsers.System); } previousEdit.DateProcessed = DateTime.UtcNow; } //save updated edit queue items dataModel.SaveChanges(); } //prepare and save changes POI changes/addition if (isUpdate && !AllowUpdates) { //user has submitted an edit but is not an approved editor //SendEditSubmissionNotification(updatedPOI, user); //user is not an editor, item is now pending in edit queue for approval. return(updatedPOI.ID); } if (isUpdate && updatedPOI.SubmissionStatusTypeID >= 1000) { //update is a delisting, skip superseding poi System.Diagnostics.Debug.WriteLine("skipping superseding of imported POI due to delisting"); } else { int?supersedesID = null; //if update by non-system user will change an imported/externally provided data, supersede old POI with new one (retain ID against new POI) if (isUpdate && !isSystemUser && oldPOI.DataProviderID != (int)StandardDataProviders.OpenChargeMapContrib) { //move old poi to new id, set status of new item to superseded supersedesID = poiManager.SupersedePOI(dataModel, oldPOI, updatedPOI); } } //user is an editor, go ahead and store the addition/update var cpData = new Core.Data.ChargePoint(); if (isUpdate) { cpData = dataModel.ChargePoints.First(c => c.ID == updatedPOI.ID); } //set/update cp properties poiManager.PopulateChargePoint_SimpleToData(updatedPOI, cpData, dataModel); //if item has no submission status and user permitted to edit, set to published if (userCanEditWithoutApproval && cpData.SubmissionStatusTypeID == null) { cpData.SubmissionStatusType = dataModel.SubmissionStatusTypes.First(s => s.ID == (int)StandardSubmissionStatusTypes.Submitted_Published); cpData.SubmissionStatusTypeID = cpData.SubmissionStatusType.ID; //hack due to conflicting state change for SubmissionStatusType } else { //no submission status, set to 'under review' if (cpData.SubmissionStatusType == null) { cpData.SubmissionStatusType = dataModel.SubmissionStatusTypes.First(s => s.ID == (int)StandardSubmissionStatusTypes.Submitted_UnderReview); } } cpData.DateLastStatusUpdate = DateTime.UtcNow; if (!isUpdate) { //new data objects need added to data model before save if (cpData.AddressInfo != null) { dataModel.AddressInfoList.Add(cpData.AddressInfo); } dataModel.ChargePoints.Add(cpData); } //finally - save poi update dataModel.SaveChanges(); //get id of update/new poi int newPoiID = cpData.ID; //this is an authorised update, reflect change in edit queue item if (enableEditQueueLogging && user != null && user.ID > 0) { var editUser = dataModel.Users.FirstOrDefault(u => u.ID == user.ID); editQueueItem.User = editUser; if (newPoiID > 0) { editQueueItem.EntityID = newPoiID; } //if user is authorised to edit, process item automatically without review if (userCanEditWithoutApproval) { editQueueItem.ProcessedByUser = editUser; editQueueItem.DateProcessed = DateTime.UtcNow; editQueueItem.IsProcessed = true; } //save edit queue item changes dataModel.SaveChanges(); } else { //anonymous submission, update edit queue item if (enableEditQueueLogging && user == null) { if (newPoiID > 0) { editQueueItem.EntityID = newPoiID; } dataModel.SaveChanges(); } } System.Diagnostics.Debug.WriteLine("Added/Updated CP:" + cpData.ID); //if user is not anonymous, log their submission and update their reputation points if (user != null) { AuditLogManager.Log(user, isUpdate ? AuditEventType.UpdatedItem : AuditEventType.CreatedItem, "Modified OCM-" + cpData.ID, null); //add reputation points new UserManager().AddReputationPoints(user, 1); } //preserve new POI Id for caller updatedPOI.ID = cpData.ID; //if new item added, send notification /*if (!isUpdate) * { * SendNewPOISubmissionNotification(updatedPOI, user, cpData); * }*/ var cacheTask = Task.Run(async() => { return(await CacheManager.RefreshCachedPOIList()); }); cacheTask.Wait(); return(updatedPOI.ID); } catch (Exception exp) { System.Diagnostics.Debug.WriteLine(exp.ToString()); AuditLogManager.ReportWebException(HttpContext.Current.Server, AuditEventType.SystemErrorWeb); //throw exp; //error performing submission return(-1); } }