Ejemplo n.º 1
0
        public static NereidResult <SolutionResponseObject> SolveSubgraph(Graph subgraph,
                                                                          List <vNereidLoadingInput> allLoadingInputs, List <TreatmentBMP> allModelingBMPs,
                                                                          List <WaterQualityManagementPlanNode> allWaterqualityManagementPlanNodes,
                                                                          List <QuickBMP> allModelingQuickBMPs, out List <string> notFoundNodes, HttpClient httpClient, bool isBaselineCondition)
        {
            notFoundNodes = new List <string>();

            // Now I need to get the land_surface, treatment_facility, and treatment_site tables for this request.
            // these are going to look very much like the various calls made throughout the testing methods, but filtered
            // to the subgraph. Fortunately, we've added metadata to the nodes to help us do the filtration

            var delineationToIncludeIDs = subgraph.Nodes.Where(x => x.Delineation != null).Select(x => x.Delineation.DelineationID)
                                          .Distinct().ToList();
            var regionalSubbasinToIncludeIDs = subgraph.Nodes.Where(x => x.RegionalSubbasinID != null)
                                               .Select(x => x.RegionalSubbasinID).Distinct().ToList();
            var waterQualityManagementPlanToIncludeIDs = subgraph.Nodes.Where(x => x.WaterQualityManagementPlan != null)
                                                         .Select(x => x.WaterQualityManagementPlan.WaterQualityManagementPlanID).Distinct().ToList();
            var treatmentBMPToIncludeIDs = subgraph.Nodes.Where(x => x.TreatmentBMPID != null)
                                           .Select(x => x.TreatmentBMPID.Value).Distinct().ToList();

            var landSurfaces = allLoadingInputs.Where(x =>
                                                      delineationToIncludeIDs.Contains(x.DelineationID.GetValueOrDefault()) ||
                                                      regionalSubbasinToIncludeIDs.Contains(x.RegionalSubbasinID) ||
                                                      waterQualityManagementPlanToIncludeIDs.Contains(x.WaterQualityManagementPlanID.GetValueOrDefault())
                                                      ).ToList().Select(x => new LandSurface(x, isBaselineCondition)).ToList();

            var treatmentFacilities = allModelingBMPs
                                      .Where(x => treatmentBMPToIncludeIDs.Contains(x.TreatmentBMPID) &&
                                             // Don't create TreatmentFacilities for BMPs belonging to a Simple WQMP
                                             x.WaterQualityManagementPlan?.WaterQualityManagementPlanModelingApproachID != WaterQualityManagementPlanModelingApproach.Simplified.WaterQualityManagementPlanModelingApproachID)
                                      .Select(x => x.ToTreatmentFacility(isBaselineCondition)).ToList();

            var filteredQuickBMPs = allModelingQuickBMPs
                                    .Where(x => waterQualityManagementPlanToIncludeIDs.Contains(x.WaterQualityManagementPlanID) &&
                                           // Don't create TreatmentSites for QuickBMPs belonging to a Detailed WQMP
                                           x.WaterQualityManagementPlan.WaterQualityManagementPlanModelingApproachID != WaterQualityManagementPlanModelingApproach.Detailed.WaterQualityManagementPlanModelingApproachID).ToList();
            var filteredWQMPNodes = allWaterqualityManagementPlanNodes.Where(y =>
                                                                             waterQualityManagementPlanToIncludeIDs.Contains(y.WaterQualityManagementPlanID) &&
                                                                             regionalSubbasinToIncludeIDs.Contains(y.RegionalSubbasinID) // ignore parts that live in RSBs outside our solve area.
                                                                             ).ToList();

            var treatmentSites = filteredQuickBMPs
                                 .Join(
                filteredWQMPNodes, x => x.WaterQualityManagementPlanID,
                x => x.WaterQualityManagementPlanID, (bmp, node) => new { bmp, node })
                                 .Select(x =>
                                         new TreatmentSite
            {
                NodeID = NereidUtilities.WaterQualityManagementPlanTreatmentNodeID(x.node.WaterQualityManagementPlanID,
                                                                                   x.node.OCSurveyCatchmentID),
                AreaPercentage     = x.bmp.PercentOfSiteTreated,
                CapturedPercentage = x.bmp.PercentCaptured ?? 0,
                RetainedPercentage = x.bmp.PercentRetained ?? 0,
                // treat wqmps built after 2003 as if they don't exist.
                FacilityType = (isBaselineCondition && x.node.DateOfConstruction.HasValue && x.node.DateOfConstruction.Value.Year > BASELINE_CUTOFF_YEAR) ? "NoTreatment" : x.bmp.TreatmentBMPType.TreatmentBMPModelingType.TreatmentBMPModelingTypeName,
                EliminateAllDryWeatherFlowOverride = x.bmp.DryWeatherFlowOverrideID == DryWeatherFlowOverride.Yes.DryWeatherFlowOverrideID
            }).ToList();

            //ValidateForTesting(subgraph, landSurfaces, treatmentFacilities, treatmentSites);

            var solveUrl = $"{NeptuneWebConfiguration.NereidUrl}/api/v1/watershed/solve?state=ca&region=soc";

            // get the list of leaf nodes for this subgraph
            var targetNodeIDs = subgraph.Edges.Select(x => x.TargetID);
            // As all men know in this kingdom by the sea, a leaf of a digraph is a node that's not the target of an edge
            var leafNodes = subgraph.Nodes.Where(x => !targetNodeIDs.Contains(x.ID));

            var solutionRequestObject = new SolutionRequestObject()
            {
                Graph               = subgraph,
                LandSurfaces        = landSurfaces,
                TreatmentFacilities = treatmentFacilities,
                TreatmentSites      = treatmentSites,
                PreviousResults     = leafNodes.Where(x => x.PreviousResults != null).Select(x => x.PreviousResults).ToList()
            };
            NereidResult <SolutionResponseObject> results = null;

            try
            {
                results = RunJobAtNereid <SolutionRequestObject, SolutionResponseObject>(
                    solutionRequestObject, solveUrl,
                    out _, httpClient);
            }
            catch (Exception e)
            {
                throw new NereidException <SolutionRequestObject, SolutionResponseObject>(e.Message, e)
                      {
                          Request  = solutionRequestObject,
                          Response = results?.Data
                      };
            }

            if (results?.Data.Errors != null && results.Data.Errors.Count > 0 && (results.Data.Results == null || results.Data.Results.Count == 0))
            {
                throw new NereidException <SolutionRequestObject, SolutionResponseObject>
                      {
                          Request = solutionRequestObject, Response = results.Data
                      };
            }

            // literally this can't be null...
            // ReSharper disable once PossibleNullReferenceException
            var previousResultsKeys = results.Data.PreviousResultsKeys;

            foreach (var dataLeafResult in results.Data.Results)
            {
                var node = subgraph.Nodes.SingleOrDefault(x => x.ID == dataLeafResult["node_id"].ToString());
                if (node == null)
                {
                    // this is an edge case that should only happen if an RSB in the SOC area has
                    // its downstream catchment outside the SOC area for some reason.
                    notFoundNodes.Add(dataLeafResult["node_id"].ToString());
                }
                else
                {
                    node.Results = dataLeafResult;

                    // track the smaller subset of results that need to be sent for subsequent calls
                    var previousResults = new JObject();
                    foreach (var key in previousResultsKeys)
                    {
                        var value = dataLeafResult[key];
                        previousResults.Add(key, value);
                    }

                    node.PreviousResults = previousResults;
                }
            }

            foreach (var dataLeafResult in results.Data.LeafResults)
            {
                try
                {
                    var node = subgraph.Nodes.SingleOrDefault(x => x.ID == dataLeafResult["node_id"].ToString());
                    if (node == null)
                    {
                        notFoundNodes.Add(dataLeafResult["node_id"].ToString());
                    }
                    else
                    {
                        // don't store the leaf results if already data at this node--most of the time these nodes are read-only
                        if (node.Results == null)
                        {
                            node.Results = dataLeafResult;
                        }
                    }
                }
                catch (InvalidOperationException ioe)
                {
                    throw new DuplicateNodeException(dataLeafResult["node_id"].ToString(), ioe);
                }
            }

            return(results);
        }
Ejemplo n.º 2
0
        public static NereidResult <TResp> RunJobAtNereid <TReq, TResp>(TReq nereidRequestObject, string nereidRequestUrl, out string responseContent, HttpClient httpClient)
        {
            NereidResult <TResp> responseObject = null;
            var serializedRequest    = JsonConvert.SerializeObject(nereidRequestObject);
            var requestStringContent = new StringContent(serializedRequest);

            _logger.Info($"Executing Nereid request: {nereidRequestUrl}");
            var requestLogFile = Path.Combine(NeptuneWebConfiguration.NereidLogFileFolder.FullName, $"NereidRequest_{DateTime.Now:yyyyMMddHHmmss}.json");

            File.WriteAllText(requestLogFile, serializedRequest);
            var postResultContentAsStringResult = httpClient.PostAsync(nereidRequestUrl, requestStringContent).Result
                                                  .Content.ReadAsStringAsync().Result;
            var responseLogFile = Path.Combine(NeptuneWebConfiguration.NereidLogFileFolder.FullName, $"NereidResponse_{DateTime.Now:yyyyMMddHHmmss}.json");

            File.WriteAllText(responseLogFile, postResultContentAsStringResult);

            NereidResult <TResp> deserializeObject = null;

            try
            {
                deserializeObject = JsonConvert.DeserializeObject <NereidResult <TResp> >(postResultContentAsStringResult);
            }
            catch (JsonReaderException jre)
            {
                throw new Exception(
                          $"Error deserializing result from Nereid. Raw result content logged at {responseLogFile}. Raw request content logged at {requestLogFile}",
                          jre);
            }

            // ReSharper disable once PossibleNullReferenceException
            // will not be null because of the catch-and-rethrow above
            var executing   = deserializeObject.Status == NereidJobStatus.STARTED || deserializeObject.Status == NereidJobStatus.PENDING;
            var resultRoute = deserializeObject.ResultRoute;

            responseContent = postResultContentAsStringResult;

            if (deserializeObject.Detail != null)
            {
                throw new Exception(deserializeObject.Detail.ToString());
            }

            if (!executing)
            {
                responseObject = deserializeObject;
            }
            while (executing)
            {
                var stringResponse = httpClient.GetAsync($"{NeptuneWebConfiguration.NereidUrl}{resultRoute}").Result.Content
                                     .ReadAsStringAsync().Result;

                var continuePollingResponse =
                    JsonConvert.DeserializeObject <NereidResult <object> >(stringResponse);

                if (continuePollingResponse.Detail != null)
                {
                    throw new Exception(continuePollingResponse.Detail.ToString());
                }

                if (continuePollingResponse.Status != NereidJobStatus.STARTED && continuePollingResponse.Status != NereidJobStatus.PENDING)
                {
                    executing       = false;
                    responseContent = stringResponse;
                    responseObject  = JsonConvert.DeserializeObject <NereidResult <TResp> >(responseContent);
                }
                else
                {
                    Thread.Sleep(1000);
                }
            }

            return(responseObject);
        }