public async Task <IActionResult> ReAnalyse(string analysisGuid)
        {
            Analysis analysis = await repository.GetAnalysisComplete(analysisGuid);

            if (analysis.Dataset != null || !analysis.Script.RequiresDataset)
            {
                AnalysisModelBase model = AnalysisFactory.CreateAnalysisModel(analysis.Script.ScriptFileName, analysis.Dataset);
                model.LoadArguments(analysis.Arguments);

                return(View(analysis.Script.ScriptFileName, model));
            }
            else //then dataset has been deleted?
            {
                TempData["ErrorMessage"] = "It is not possible to re-analyse this dataset as it has been deleted";
                return(RedirectToAction("Index"));
            }
        }
예제 #2
0
        public async Task Execute(string analysisGuid)
        {
            using (IServiceScope scope = services.CreateScope())
            {
                ISilveRRepository silveRRepository = scope.ServiceProvider.GetRequiredService <ISilveRRepository>();
                AppSettings       appSettings      = scope.ServiceProvider.GetRequiredService <IOptions <AppSettings> >().Value;

                //declared here as used in exception handler
                string workingDir   = Path.GetTempPath();
                string theArguments = null;
                string rscriptPath  = null;

                try
                {
                    Stopwatch sw = Stopwatch.StartNew();

                    //get analysis
                    Analysis analysis = await silveRRepository.GetAnalysisComplete(analysisGuid);

                    //load the analysis entity into the model so that arguments can be extracted
                    AnalysisModelBase analysisModel = AnalysisFactory.CreateAnalysisModel(analysis);
                    analysisModel.LoadArguments(analysis.Arguments);

                    //save the useroptions to the working dir
                    UserOption userOptions = await silveRRepository.GetUserOptions();

                    File.WriteAllLines(Path.Combine(workingDir, analysisGuid + ".useroptions"), userOptions.GetOptionLines());

                    //combine script files into analysisGuid.R
                    string scriptFileName = Path.Combine(workingDir, analysisGuid + ".R");

                    List <string> scriptLines = new List <string>();
                    scriptLines.AddRange(File.ReadAllLines(Path.Combine(Startup.ContentRootPath, "Scripts", "Common_Functions.R")));
                    scriptLines.AddRange(File.ReadAllLines(Path.Combine(Startup.ContentRootPath, "Scripts", analysis.Script.ScriptFileName + ".R")));

                    if (analysisModel.CustomRCode != null)
                    {
                        scriptLines.Add(analysisModel.CustomRCode);
                    }

                    File.WriteAllLines(scriptFileName, scriptLines);

                    //csvfilename is built from the analysis guid and is also used in R to name the output at this time
                    string csvFileName = Path.Combine(workingDir, analysisGuid + ".csv");

                    if (analysisModel is AnalysisDataModelBase analysisDataModelBase) //then has data component
                    {
                        string[] csvData = analysisDataModelBase.ExportData();
                        File.WriteAllLines(csvFileName, csvData);
                    }

                    //setup the r process (way of calling rscript.exe is slightly different for each OS)
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    {
                        if (!String.IsNullOrEmpty(appSettings.CustomRScriptLocation))
                        {
                            rscriptPath = appSettings.CustomRScriptLocation;
                        }
                        else
                        {
                            rscriptPath = Path.Combine(Startup.ContentRootPath, "R", "bin", "Rscript.exe");
                        }
                    }
                    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                    {
                        if (!String.IsNullOrEmpty(appSettings.CustomRScriptLocation))
                        {
                            rscriptPath = appSettings.CustomRScriptLocation;
                        }
                        else
                        {
                            rscriptPath = "Rscript";
                        }
                    }
                    else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                    {
                        if (!String.IsNullOrEmpty(appSettings.CustomRScriptLocation))
                        {
                            rscriptPath = appSettings.CustomRScriptLocation;
                        }
                        else
                        {
                            rscriptPath = "/usr/local/bin/Rscript";
                        }
                    }

                    ProcessStartInfo psi = new ProcessStartInfo();
                    psi.FileName         = rscriptPath;
                    psi.WorkingDirectory = workingDir;

                    theArguments  = analysisModel.GetCommandLineArguments();
                    psi.Arguments = FormatPreArgument(scriptFileName) + " --vanilla --args " + FormatPreArgument(csvFileName) + " " + theArguments;

                    //Configure some options for the R process
                    psi.UseShellExecute        = false;
                    psi.CreateNoWindow         = true;
                    psi.RedirectStandardOutput = true;
                    psi.RedirectStandardError  = true;

                    //start rscript.exe
                    Process R = Process.Start(psi);

                    //create a stringbuilder to hold the output, and get the output line by line until R process finishes
                    StringBuilder output = new StringBuilder();

                    bool completedOK = R.WaitForExit(10 * 60 * 1000); //10 minutes!

                    if (completedOK)
                    {
                        //need to make sure that we have got all the output so do a readtoend here
                        output.AppendLine(R.StandardOutput.ReadToEnd());
                        output.AppendLine();

                        //output the errors from R
                        string errorsFromR = R.StandardError.ReadToEnd().Trim();

                        R.Close();
                        R.Dispose();

                        if (!String.IsNullOrEmpty(errorsFromR))
                        {
                            output.AppendLine();
                            output.Append(errorsFromR);
                            output.AppendLine();
                        }
                    }
                    else //timed out, try and kill it (but usually doesnt work)
                    {
                        output.AppendLine("WARNING! The R process timed out before the script could complete");
                        output.AppendLine();

                        //get the id so can really check if it has died
                        int processID = R.Id;

                        //try and kill it
                        R.Kill();
                        R.WaitForExit(5000); //wait 5 seconds to exit, but this usually doesnt work
                        R.Dispose();

                        if (Process.GetProcesses().Any(x => x.Id == processID)) //then R failed to exit
                        {
                            throw new TimeoutException("R timed out and failed to exit gracefully, aborting analysis without reading results or log. You may need to manually kill the Rscript process. Partial results and log may be in the temp folder.");
                        }
                    }

                    TimeSpan timeElapsed = sw.Elapsed;
                    output.AppendLine();
                    output.AppendLine("Analysis by the R Processor took " + Math.Round(timeElapsed.TotalSeconds, 2) + " seconds.");

                    analysis.RProcessOutput = output.ToString().Trim();

                    //assemble the entire path and file to the html output
                    string htmlFile = Path.Combine(workingDir, analysisGuid + ".html");

                    if (File.Exists(htmlFile)) //won't exist if there is an error!
                    {
                        DirectoryInfo dir         = new DirectoryInfo(workingDir);
                        FileInfo[]    outputFiles = dir.GetFiles(analysis.AnalysisGuid + "*");

                        //first go through all the results files...
                        List <string> resultsFiles = new List <string>();

                        foreach (FileInfo file in outputFiles.Where(x => x.Extension == ".html" || x.Extension == ".png")) //go through all results output
                        {
                            resultsFiles.Add(file.FullName);
                        }

                        //generate the inline html
                        string inlineHtml = InlineHtmlCreator.CreateInlineHtml(resultsFiles);
                        analysis.HtmlOutput = inlineHtml;

                        //do a save at this point so that results can be shown (processing is checking for output at this point)
                        await silveRRepository.UpdateAnalysis(analysis);

                        //now go through any csv output to be imported into datasets
                        foreach (FileInfo file in outputFiles.Where(x => x.Extension == ".csv" && !x.FullName.EndsWith(analysis.AnalysisGuid + ".csv"))) //go through any dataset output (make sure dont import original csv file!)
                        {
                            DataTable dataTable = CSVConverter.CSVDataToDataTable(file.OpenRead());                                                      //because R will write out using , separator

                            string datasetName = file.Name.Replace(analysis.AnalysisGuid, String.Empty);
                            await SaveDatasetToDatabase(silveRRepository, datasetName, dataTable);
                        }
                    }
                    else //something not right then, throw exception
                    {
                        throw new InvalidOperationException("No html output found!" + Environment.NewLine + "RProcessOutput:" + Environment.NewLine + analysis.RProcessOutput);
                    }

#if !DEBUG
                    string[] filesToClean = Directory.GetFiles(workingDir, analysis.AnalysisGuid + "*");
                    foreach (string file in filesToClean)
                    {
                        File.Delete(file);
                    }
#endif
                }
                catch (System.ComponentModel.Win32Exception ex) when(ex.NativeErrorCode == 2)
                {
                    Analysis analysis = await silveRRepository.GetAnalysis(analysisGuid);

                    string message = "Rscript cannot be found, so no analysis was run." + Environment.NewLine;

                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    {
                        message = message + "Your R installation (specifically Rscript.exe) is missing - please reinstall this application.";
                    }
                    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                    {
                        message = message + "No R install could be found on this system. It is recommended that you copy and run the following shell script to install the correct version of R. Then, click the 'Install R packages' button under settings to install the correct R packages." + Environment.NewLine;
                        message = message + "apt-get install r-base -y";
                    }
                    else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                    {
                        message = message + "No R install could be found on this system. It is recommended that you download and install the following R package (https://cran.r-project.org/bin/macosx/R-3.5.2.pkg) into your OSX system. Then, click the 'Install R packages' button under settings to install the correct R packages.";
                    }

                    analysis.RProcessOutput = message;
                    await silveRRepository.UpdateAnalysis(analysis);
                }
                catch (Exception ex)
                {
                    Analysis analysis = await silveRRepository.GetAnalysis(analysisGuid);

                    string message = "ContentRoot=" + Startup.ContentRootPath + Environment.NewLine + Environment.NewLine;
                    message = message + "TempFolder=" + workingDir + Environment.NewLine + Environment.NewLine;
                    message = message + "Arguments=" + theArguments + Environment.NewLine + Environment.NewLine;
                    message = message + "Rscript=" + rscriptPath + Environment.NewLine + Environment.NewLine;

                    message = message + ex.ToString();

                    analysis.RProcessOutput = message;
                    await silveRRepository.UpdateAnalysis(analysis);
                }
            }
        }