diff --git a/.gitignore b/.gitignore index 681e489f3c..e7049bfecb 100644 --- a/.gitignore +++ b/.gitignore @@ -471,3 +471,17 @@ SkylineTester Results /tutorialTestNames.txt /tests.txt /pwiz_tools/Skyline/Translation/Scratch +/Nick_quickbuild_bs64.bat +/pwiz_tools/Skyline/Model/Tools/TEST_FOR_RAMdrive_PythonInstaller.cs +/pwiz_tools/Skyline/SettingsUI/TEST_PYTHON312_RAMDRIVE_BuildLibraryDlg.cs +/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest-Backup.zip +/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest/LFQ_Orbitrap_AIF_Human_01.mzML +/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest/Test-imported.sky/Test-imported-assay.blib +/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest/Test-imported.sky/Test-imported-assay.slc +/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest/Test-imported.sky/Test-imported.sky +/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest/Test-imported.sky/Test-imported.sky.view +/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest/Test-imported.sky/Test-imported.skyl +/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest/report.tsv +/pwiz_tools/Skyline/TestFunctional/NEWTestFunctional.csproj +/pwiz_tools/Skyline/TestFunctional/PythonInstallerTest.zip +/pwiz_tools/Skyline/TestFunctional/Rat_plasma.sky diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIBinning.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIBinning.h new file mode 100644 index 0000000000..6bf114a07b --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIBinning.h @@ -0,0 +1,21 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2022 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: Curt Lindmark * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + /// MBIBinning.h file +/// Header file for MBIBinning classes +/// MBIBinning contains functions trim an existing MBI file and save changes to +/// a new file. + +// MBIBinning.h - The top-level API object interface to MBI binning operations. +#pragma once +#pragma warning(disable : 4251) +#pragma warning(disable : 4996) +#ifndef MBI_DLLCPP +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBICalibration.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBICalibration.h new file mode 100644 index 0000000000..f77b356480 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBICalibration.h @@ -0,0 +1,399 @@ +// MBICalibration.h - Mass / CCS calibration information to associate with data +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2021 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: Greg Van Aken * + * 0.0.0.0 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#pragma once +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif + +#include +#include +#include + +namespace MBISDK +{ +class MBIFile; +class Frame; + +extern "C" +{ + /*! @class MBISDK::TofCalibration + * @brief A calibration object that holds coefficients to convert to/from ToF values from/to mass-to-charge values. + */ + + // Constants for Newton method + const int ATTEMPT_MAX = (int)(1e6); + const double TOLERANCE = 1e-8; + + /* + * CalPolynomial is a polynomial function class. Can be used for Mass calibration and CCS calibration + * The first coefficient has the highest order and the last coefficient has the lowest order (constant) + */ + class MBI_DLLCPP CalPolynomial + { + public: + /// @brief Initialize a polynomial object with coefficients + CalPolynomial(std::vector coefficients); + + /// @brief Compute the y from x + double YFromX(double x); + + /// @brief compute the y' at x + double DerivativeAtX(double x); + + /// @brief degree of polynomial + int GetDegree(); + + private: + std::vector _coefficients; // y = a0*x^3 + a1*x^2 + a2*x + a3 + }; + + /* + * Bisection Search is a numerical method looking for X satisfing Y for a monotonic function y = f(x) + * It is not as faster as Newton method and it will never fail for a montonic function + */ + class MBI_DLLCPP CCSBisectionSearch + { + public: + /// @brief Initialize a BisectionSearch object with polynomial, target, and epsilon + CCSBisectionSearch(CalPolynomial* CCSpolymonial); + + /// @brief Start to search a point close enough to the target + double Search(); + + /// @brief Change epsilon + void NewEpsilion(double epsilon); + + /// @brief set search Target + void NewTarget(double target); + + /// @brief get epsilon + double getEpsilon(); + + + private: + CalPolynomial* _CCSpolynomial; + double _target = 0.0; + double _epsilon = 0.1; + double _lowEnd = 0.0; + double _highEnd = 100.0; + }; + + /* + * Newton Method is a numerical method looking for a root of a real - valued function + * Xn+1 = Xn - f(Xn) / f'(Xn) + * f(X) is the real-valued function and f'(X) is the derivate function + * Newton Method need a starting point X0 and then X1 is calculated from X0, X2 is calculated from X1, ... + * If the newton method is converging, Xn+1 is closer to the root than Xn, otherwise the method fails. + * Finally, if (Xn+1 - Xn) is smaller than a predefined value, the iteration stops and return Xn+1 as the root. + */ + class MBI_DLLCPP NewtonMethod + { + public: + /// @brief Initialize a NewtonMethod object with polynomial + NewtonMethod(CalPolynomial* polynomial); + + /// @brief Calculate Y from X with Newton method + double RootStartFromX(double startingX); + + /// @brief if Newton method Success + bool isSuccess(); + + private: + CalPolynomial* _polynomial; + bool _success = false; + }; + + class MBI_DLLCPP TofCalibration + { + public: + /// @brief Initialize a calibration object + TofCalibration(); + + /// @brief Initialize a calibration object from json representation. + TofCalibration(double sampleRate, const char* jsonString); + + /// @brief Initialize a calibration object from coefficients. + TofCalibration(double sampleRate, double slope, double intercept, std::string strStatus, int nFailureStatus); + + /// @brief Initialize a calibration object from coefficients with mass correction. + TofCalibration(double sampleRate, double slope, double intercept, std::vector residualFit, std::string strStatus, int nFailureStatus); + + /// @brief Compute mass error from polynomial residual fit. + double TofError(double uSecTOF); + + /// @brief Convert tof bin index to time-of-flight + double IndexToMicroseconds(int64_t index); + + /// @brief Convert time-of-flight to m/z using this calibration. + double MicrosecondsToMz(double uSec); + + /// @brief Convert tof bin index to m/z using this calibration. + double IndexToMz(int64_t index); + + /// @brief Convert time-of-flight to tof bin index. + size_t MicrosecondsToIndex(double uSec); + + /// @brief Convert m/z to time-of-flight using this calibration. + double MzToMicroseconds(double mz); + + /// @brief get Agilent polynomial from Mobilion polynomial + std::vector TotalPolynomial(); + + /// @brief Convert m/z to tof bin index using this calibration. + size_t MzToIndex(double mz); + + /// @brief Retrieve the slope of the calibration. + double Slope(); + + /// @brief Retrieve the intercept of the calibration. + double Intercept(); + + /// @brief Retrieve the residual fit coefficients. + std::vector ResidualFit(); + + /// @brief return status of failure + int getFailureStatus(); + + /// @brief return status of StatusText + std::string getStatusText(); + + double slope; ///< The slope of the traditional calibration. + double intercept; ///< The intercept of the traditional calibration. + + private: + + int failure_status; + std::string status_text; + double sample_rate; ///< The digiter's sampling rate in samples/second. + std::vector residual_fit; ///< The terms of the polynomial fit of the mass residuals. + double bin_width; ///< The TOF dimension bin width. + bool mass_corrected; ///< Is this calibration mass-corrected? + std::string json_string; ///< The json representation of the calibration. + }; + + /// @brief The types of calibrations that can be applied + /// @author Greg Van Aken + + /// @brief Calibration type enumeration + enum class eCalibrationType + { + /// @brief Polynomial calibration type + POLYNOMIAL, + /// @brief Unknown calibration type + UNKNOWN + }; + + /// @class MBISDK::CcsCalibration + /// @brief A calibration object that holds coefficients to convert to/from AT to CCS + class MBI_DLLCPP CcsCalibration + { + public: + /// @brief Initialize a calibration object from json representation. + CcsCalibration(double sampleRate, const char* jsonString); + + /// @brief Initialize a calibration object from coefficients. + CcsCalibration(double sampleRate, eCalibrationType type, std::vector coefficients); + + //CcsCalibration(Frame* parent); + + /// @brief Get the type of calibration + eCalibrationType Type(); + + /// @brief Get the coefficients + std::vector Coefficients(); + + /// @brief Default constructor. + CcsCalibration() {}; + + private: + double sample_rate = 0.0; ///< The digiter's sampling rate in samples/second. + eCalibrationType type = eCalibrationType::UNKNOWN; ///< The type of calibration. + std::vector coefficients; ///< The set of coefficients for this calibration. + std::string json_string; ///< The json representation of the calibration. + }; + + /*! @class MBISDK::GlobalCcsCalibration + * @brief A calibration file that holds coefficients to convert to/from AT to CCS + */ + class MBI_DLLCPP GlobalCcsCalibration + { + public: + /// @brief Initialize a calibration object from json representation. + GlobalCcsCalibration(const char* jsonString); + + /// @brief Initialize a calibration object from coefficients. + GlobalCcsCalibration(eCalibrationType type, + std::vector coefficients, + std::vector ccsCoefficients = std::vector(), + std::vector atCoefficients = std::vector(), + std::vector mzCoefficients = std::vector()); + + /// @brief Default constructor + GlobalCcsCalibration(); + + /// @brief Get the type of calibration + eCalibrationType Type(); + + /// @brief Get the coefficients + std::vector Coefficients(); + + /// @brief Get the CSS coefficients + std::vector CSSCoefficients(); + + /// @brief Get the MZ coefficients + std::vector MZCoefficients(); + + /// @brief Get the AT coefficients + std::vector ATCoefficients(); + private: + eCalibrationType type; ///< The type of calibration. + std::vector coefficients; ///< The set of coefficients for this calibration. + std::string json_string; ///< The json representation of the calibration. + std::vector ccs_coefficients; ///< The json representation of the CSS calibration. + std::vector at_coefficients; ///< The json representation of the AT calibration. + std::vector mz_coefficients; ///< The json representation of the MZ calibration. + }; + + /// @class MBISDK::EyeOnCcsCalibration + /// @brief A calibration file that holds coefficients to convert from AT to CCS + class MBI_DLLCPP EyeOnCcsCalibration + { + public: + + /// @brief Initialize a calibration object with just a file pointer. + EyeOnCcsCalibration(MBISDK::MBIFile* input_mbi_file_ptr); + + /// @brief Initialize a calibration object from json representation. + EyeOnCcsCalibration(std::string strCCSData, MBISDK::MBIFile* input_mbi_file_ptr); + + /// @brief list of throwable errors + enum { + /// @brief bad file pointer throwable error + BAD_FILE_POINTER = 0, + /// @brief bad frame index throwable error + BAD_FRAME_INDEX = 1, + /// @brief bad mass value throwable error + BAD_MASS_VALUE = 2, + /// @brief bad calc value throwable error + BAD_CALC_VALUE = 3 + }; + + /// @brief Parse CCS Cal string + void ParseCCSCal(std::string strCCSData); + + /// @brief Get CCS Minimum value + double GetCCSMinimum(); + + /// @brief Get CCS Maximum value + double GetCCSMaximum(); + + /// @brief Get Degree value + int GetDegree(); + + /// @brief Get AT Surfing value, measured in milliseconds + double GetAtSurf(); + + /// @brief Get CCS Coefficient for a given nIndex + std::vector GetCCSCoefficients(); + + /// @brief Get CCS Coefficient for a given nIndex + void SetCCSCoefficients(const std::vector vec_input); + + /// @brief Calculate CCS value for given AT + double ArrivalTimeToCCS(double scan_arrival_time, double ion_mass); + + /// @brief Calculate CCS value for set of AT + std::vector> ArrivalTimeToCCS(std::vector> input_arrival_list); + + /// @brief Calculate CCS value for the AT of a single frame of intensities + std::vector> ArrivalTimeToCCS(int frame_index); + + /// @brief Calculate Arrival Time for given CCS + double CCSToArrivalTime(double ccs_angstroms_squared, double ion_mass); + + /// @brief Return the details during an error to assist developers + std::string GetErrorOutput(); + + /// @brief Return the value of the gas mass for the experiment + double ComputeGasMass(std::string gas_string); + + /// @brief Calculate adjusted CCS value based on arrival time + double AdjustCCSValue(double unadjusted_ccs, double dbArrivalTime, double Mz_ion); + + double InvertAdjustCCSValue(double ccs_value, double Mz_ion); + + /// @brief Return the value of the gas mass for the experiment + double ComputeGasMass(); + + /// @brief Generate a message to throw an error + std::string GenerateThrownErrorMessage(int error_type, int frame_index = 0, double arrival_time = 0.0); + + /// @brief evaluate pointer to ensure it is not null + bool IsValid(void* ptr); + + /// @brief override the gas mass value + void SetGasMass(double gas_mass_input); + + /// @brief decide which of two roots is better + double ChooseGoodRoot(double root_1, double root_2, double ion_mass); + + /// @brief CCS_ERROR_HEADER error text + static constexpr const char* CCS_ERROR_HEADER = "Cannot process EyeOn CCS Calibration information. "; + /// @brief CCS_MISSING_DATA error text + static constexpr const char* CCS_MISSING_DATA = "The global metadata field cal-ccs is not present in this file: "; + /// @brief CCS_INVALID_DATA error text + static constexpr const char* CCS_INVALID_DATA = "The global metadata field cal-ccs is not a valid JSON structure in this file: "; + /// @brief CCS_MISSING_CCS_COEFFICIENTS error text + static constexpr const char* CCS_MISSING_CCS_COEFFICIENTS = "The ccs_coefficients entry for CCS Calibration data is not present in this file: "; + /// @brief CCS_MISSING_MIN_CCS error text + static constexpr const char* CCS_MISSING_MIN_CCS = "The ccs_min entry for CCS Calibration data is not present in this file: "; + /// @brief CCS_MISSING_MAX_CCS error text + static constexpr const char* CCS_MISSING_MAX_CCS = "The ccs_max entry for CCS Calibration data is not present in this file: "; + /// @brief CCS_MISSING_CCS_DEGREE error text + static constexpr const char* CCS_MISSING_CCS_DEGREE = "The degree entry for CCS Calibration data is not present in this file: "; + /// @brief CCS_MISSING_CCS_AT_SURF error text + static constexpr const char* CCS_MISSING_CCS_AT_SURF = "The at_surf entry for CCS Calibration data is not present in this file: "; + /// @brief CCS_DEGREE_COEFFICIENTS_MISMATCH error text + static constexpr const char* CCS_DEGREE_COEFFICIENTS_MISMATCH = "The degree entry for the polynomial should align with the count of ccs coefficients, and it does not in this file: "; + /// @brief CCS_BAD_FRAME_INDEX_HEADER error text + static constexpr const char* CCS_BAD_FRAME_INDEX_HEADER = "The frame index supplied, "; + /// @brief CCS_BAD_FRAME_INDEX_FOOTER error text + static constexpr const char* CCS_BAD_FRAME_INDEX_FOOTER = ", must be at least 1 and no greater than the number of frames in this file: "; + /// @brief CCS_BAD_FILE_POINTER error text + static constexpr const char* CCS_BAD_FILE_POINTER = "The file pointer supplied is not valid for this file: "; + /// @brief CCS_MISSING_CCS_GAS_TYPE error text + static constexpr const char* CCS_MISSING_CCS_GAS_TYPE = "The mass flow gas type entry for CCS Calibration data is not present in this file: "; + /// @brief CCS_ARRIVAL_TIME_HEADER error text + static constexpr const char* CCS_ARRIVAL_TIME_HEADER = "The arrival time chosen, "; + /// @brief CCS_BAD_MASS_VALUE_PART_2 error text + static constexpr const char* CCS_BAD_MASS_VALUE_PART_2 = " yields a mass that is invalid for CCS Calibration data in this file: "; + /// @brief CCS_BAD_ADJUSTMENT_PART_2 error text + static constexpr const char* CCS_BAD_ADJUSTMENT_PART_2 = " yields a calculation that would result in a divide by zero for CCS Calibration data in this file: "; + + + private: + double ccs_minimum; // minimum value for valid CCS + double ccs_maximum; // maximum value for valid ccs + int degree; // numeric degree of polynomial + double at_surfing; // earliest valid AT value for a given calibration, in milliseconds + double mass_gas_medium; //mass of gas for a specific calibration + std::vector ccs_coefficients; // list of coefficients, as read in from MBI file with first coefficient applying to addend with the highest order (usually AX^3) + MBISDK::MBIFile* mbi_file_ptr; + MBISDK::TofCalibration tof_cal; + }; +} +} diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIChem.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIChem.h new file mode 100644 index 0000000000..d0c85c81c3 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIChem.h @@ -0,0 +1,41 @@ +// MBIChem.h - Chemistry-related codes +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2024 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: Bennett Kalafut * + * 0.0.0.0 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#pragma once + +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif + +#include +#include + +namespace MBISDK +{ + + namespace MBIChem + { + extern "C" + { + + /// @fn GasStringToMassMapping + /// @brief A mapping of gas identity strings to masses + /// @author B. Kalafut + MBI_DLLCPP std::map& GasStringToMassMapping(); + + } + } + +}; diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIConstants.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIConstants.h new file mode 100644 index 0000000000..33d2b76cb8 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIConstants.h @@ -0,0 +1,589 @@ +// MBIConstants.h - All constant keys / enum declarations. +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2021 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: Greg Van Aken * + * 0.0.0.0 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#pragma once +#include +#include + +namespace MBISDK +{ + /// @class MBISDK::MBIAttr + /// @brief Set of file attributes (key/values). + /// @author Greg Van Aken + namespace MBIAttr + { + /// @class MBISDK::MBIAttr::Name + /// @brief Names present in MBI files (groups / datasets). + /// @author Greg Van Aken + namespace Name + { + /// @brief HDF5 frame metadata table name + static constexpr const char* DATA_DESCRIPTION = "data-description"; ///< Detailed (frame) metadata. + /// @brief HDF5 global metadata table name + static constexpr const char* GLOBAL_DESCRIPTION = "global-description"; ///< Global (file) metadata. + /// @brief HDF5 intensity table name + static constexpr const char* DATA_CUBES = "data-cubes"; ///< All datasets. + /// @brief HDF5 frame intensity frame specific table name + static constexpr const char* FRAME_DATA = "frame-%d-data"; ///< The datasets for a single frame p1. + /// @brief HDF5 frame metadata frame specific table name + static constexpr const char* FRAME_METADATA = "frame-%d-metadata"; ///< The metadata for a single frame p1. + /// @brief HDF5 frame data intensity offset table name + static constexpr const char* INDEX_COUNTS = "index-counts"; ///< Offsets into the data-counts array for each scan. + /// @brief HDF5 frame data gate offset table name + static constexpr const char* INDEX_POSITIONS = "index-positions"; ///< Offsets into the data-positions (gateS) array for each scan. + /// @brief HDF5 frame data table name + static constexpr const char* DATA_COUNTS = "data-counts"; ///< All of the intensity values recorded in a frame. + /// @brief HDF5 frame data gate table name + static constexpr const char* DATA_POSITIONS = "data-positions"; ///< All of the gate positions recorded in a frame. + /// @brief HDF5 frame trigger timestamps table name + static constexpr const char* TRIGGER_TIMESTAMPS = "trigger-timestamps"; ///< The timestamps (in seconds) of each scan in a frame. + /// @brief HDF5 global rt_tic table name + static constexpr const char* RT_TIC = "rt-tic"; ///< The total ion count for each frame. + /// @brief HDF5 frame at_tic table name + static constexpr const char* AT_TIC = "at-tic"; ///< The total ion count for each scan. + /// @brief HDF5 CCS Calibration traditional metdata name + static constexpr const char* CAL_CSS_TRADITIONAL = "cal-css-traditional"; ///< Traditional (json) ccs coefficients. + }; + + /// @class MBISDK::MBIAttr::FrameKey + /// @brief Keys present in Frame Metadata + /// @author Greg Van Aken + namespace FrameKey + { + /// @brief CAL_DT_POLYNOMIAL metdata name + static constexpr const char* CAL_DT_POLYNOMIAL = "cal-dt-polynomial"; + /// @brief CAL_DT_POWER_FLAGS metdata name + static constexpr const char* CAL_DT_POWER_FLAGS = "cal-dt-power-flags"; + /// @brief CAL_DT_TRADITIONAL metdata name + static constexpr const char* CAL_DT_TRADITIONAL = "cal-dt-traditional"; ///< Traditional (json) ccs coefficients. + /// @brief CAL_MS_POLYNOMIAL metdata name + static constexpr const char* CAL_MS_POLYNOMIAL = "cal-ms-polynomial"; + /// @brief CAL_MS_POWER_FLAGS metdata name + static constexpr const char* CAL_MS_POWER_FLAGS = "cal-ms-power-flags"; + /// @brief CAL_MS_TRADITIONAL metdata name + static constexpr const char* CAL_MS_TRADITIONAL = "cal-ms-traditional"; ///< Traditional (json) coefficients. + /// @brief FRM_COLLISION_ENERGY metdata name + static constexpr const char* FRM_COLLISION_ENERGY = "frm-collision-energy"; + /// @brief FRM_DT_PERIOD metdata name + static constexpr const char* FRM_DT_PERIOD = "frm-dt-period"; ///< Average time (milliseconds) between scans. + /// @brief FRM_FRAG_ENERGY metdata name + static constexpr const char* FRM_FRAG_ENERGY = "frm-frag-energy"; ///< The fragmentation energy + /// @brief FRM_FRAG_ENERGY_MODE metdata name + static constexpr const char* FRM_FRAG_ENERGY_MODE = "frm-frag-energy-mode"; + /// @brief FRM_FRAG_OP_MODE metdata name + static constexpr const char* FRM_FRAG_OP_MODE = "frm-frag-op-mode"; ///< The fragmentation operational mode + /// @brief FRM_INTENSITY_LIMIT metdata name + static constexpr const char* FRM_INTENSITY_LIMIT = "frm-intensity-limit"; + /// @brief FRM_METADATA_ID metdata name + static constexpr const char* FRM_METADATA_ID = "frm-metadata-id"; + /// @brief FRM_METHOD_NAME metdata name + static constexpr const char* FRM_METHOD_NAME = "frm-method-name"; + /// @brief FRM_METHOD_STATE metdata name + static constexpr const char* FRM_METHOD_STATE = "frm-method-state"; + /// @brief FRM_MUX_GATE metdata name + static constexpr const char* FRM_MUX_GATE = "frm-mux-gate"; + /// @brief FRM_MUX_SEQUENCE metdata name + static constexpr const char* FRM_MUX_SEQUENCE = "frm-mux-sequence"; + /// @brief FRM_NUM_SCANS metdata name + static constexpr const char* FRM_NUM_SCANS = "frm-num-bin-dt"; ///< Number of scans in the frame. + /// @brief FRM_NUM_MICROFRAMES metdata name + static constexpr const char* FRM_NUM_MICROFRAMES = "frm-num-microframes"; + /// @brief FRM_POLARITY metdata name + static constexpr const char* FRM_POLARITY = "frm-polarity"; + /// @brief FRM_START_TIME metdata name + static constexpr const char* FRM_START_TIME = "frm-start-time"; ///< The time (seconds) of the first scan in this frame relative to the first scan of the first frame. + /// @brief FRM_TIMIING_INTENTS metdata name + static constexpr const char* FRM_TIMIING_INTENTS = "frm-timing-intents"; + /// @brief KEY_FRAG metdata name + static constexpr const char* KEY_FRAG = "key-frag"; + /// @brief SLIM_RF_FUNNEL_POWER metdata name + static constexpr const char* SLIM_RF_FUNNEL_POWER = "slim-rf-funnel-power"; + /// @brief SLM_ENTRANCE_OFFSET metdata name + static constexpr const char* SLM_ENTRANCE_OFFSET = "slm-entrance-offset"; + /// @brief SLM_EXIT_CL metdata name + static constexpr const char* SLM_EXIT_CL = "slm-exit-cl"; + /// @brief SLM_EXIT_IN metdata name + static constexpr const char* SLM_EXIT_IN = "slm-exit-in"; + /// @brief SLM_EXIT_OUT metdata name + static constexpr const char* SLM_EXIT_OUT = "slm-exit-out"; + /// @brief SLM_FUNNEL_CL metdata name + static constexpr const char* SLM_FUNNEL_CL = "slm-funnel-cl"; + /// @brief SLM_FUNNEL_IN metdata name + static constexpr const char* SLM_FUNNEL_IN = "slm-funnel-in"; + /// @brief SLM_FUNNEL_OUT metdata name + static constexpr const char* SLM_FUNNEL_OUT = "slm-funnel-out"; + /// @brief SLM_OBA_GATE metdata name + static constexpr const char* SLM_OBA_GATE = "slm-oba-gate"; + /// @brief SLM_QUAD_BIAS metdata name + static constexpr const char* SLM_QUAD_BIAS = "slm-quad-bias"; + /// @brief SLM_RF_BOTTOM_DRIVE metdata name + static constexpr const char* SLM_RF_BOTTOM_DRIVE = "slm-rf-bottom-drive"; + /// @brief SLM_RF_BOTTOM_FREQ metdata name + static constexpr const char* SLM_RF_BOTTOM_FREQ = "slm-rf-bottom-freq"; + /// @brief SLM_RF_BOTTOM_NEG metdata name + static constexpr const char* SLM_RF_BOTTOM_NEG = "slm-rf-bottom-neg"; + /// @brief SLM_RF_BOTTOM_POS metdata name + static constexpr const char* SLM_RF_BOTTOM_POS = "slm-rf-bottom-pos"; + /// @brief SLM_RF_BOTTOM_POWER metdata name + static constexpr const char* SLM_RF_BOTTOM_POWER = "slm-rf-bottom-power"; + /// @brief SLM_RF_FUNNEL_DRIVE metdata name + static constexpr const char* SLM_RF_FUNNEL_DRIVE = "slm-rf-funnel-drive"; + /// @brief SLM_RF_FUNNEL_FREQ metdata name + static constexpr const char* SLM_RF_FUNNEL_FREQ = "slm-rf-funnel-freq"; + /// @brief SLM_RF_FUNNEL_NEG metdata name + static constexpr const char* SLM_RF_FUNNEL_NEG = "slm-rf-funnel-neg"; + /// @brief SLM_RF_FUNNEL_POS metdata name + static constexpr const char* SLM_RF_FUNNEL_POS = "slm-rf-funnel-pos"; + /// @brief SLM_RF_QUAD_DRIVE metdata name + static constexpr const char* SLM_RF_QUAD_DRIVE = "slm-rf-quad-drive"; + /// @brief SLM_RF_QUAD_FREQ metdata name + static constexpr const char* SLM_RF_QUAD_FREQ = "slm-rf-quad-freq"; + /// @brief SLM_RF_QUAD_NEG metdata name + static constexpr const char* SLM_RF_QUAD_NEG = "slm-rf-quad-neg"; + /// @brief SLM_RF_QUAD_POS metdata name + static constexpr const char* SLM_RF_QUAD_POS = "slm-rf-quad-pos"; + /// @brief SLM_QUAD_POWER metdata name + static constexpr const char* SLM_QUAD_POWER = "slm-rf-quad-power"; + /// @brief SLM_RF_TOP_DRIVE metdata name + static constexpr const char* SLM_RF_TOP_DRIVE = "slm-rf-top-drive"; + /// @brief SLM_RF_TOP_FREQ metdata name + static constexpr const char* SLM_RF_TOP_FREQ = "slm-rf-top-freq"; + /// @brief SLM_RF_TOP_NEG metdata name + static constexpr const char* SLM_RF_TOP_NEG = "slm-rf-top-neg"; + /// @brief SLM_RF_TOP_POS metdata name + static constexpr const char* SLM_RF_TOP_POS = "slm-rf-top-pos"; + /// @brief SLM_RF_TOP_POWER metdata name + static constexpr const char* SLM_RF_TOP_POWER = "slm-rf-top-power"; + /// @brief SLM_SLIM_BIAS metdata name + static constexpr const char* SLM_SLIM_BIAS = "slm-slim-bias"; + /// @brief SLM_SLIM_OFFSET metdata name + static constexpr const char* SLM_SLIM_OFFSET = "slm-slim-offset"; + /// @brief SLM_TW_OBA_AMP metdata name + static constexpr const char* SLM_TW_OBA_AMP = "slm-tw-oba-amp"; + /// @brief SLM_TW_OBA_AUX metdata name + static constexpr const char* SLM_TW_OBA_AUX = "slm-tw-oba-aux"; + /// @brief SLM_TW_OBA_DIR metdata name + static constexpr const char* SLM_TW_OBA_DIR = "slm-tw-oba-dir"; + /// @brief SLM_TW_OBA_FREQ metdata name + static constexpr const char* SLM_TW_OBA_FREQ = "slm-tw-oba-freq"; + /// @brief SLM_TW_OBA_OFFSET metdata name + static constexpr const char* SLM_TW_OBA_OFFSET = "slm-tw-oba-offset"; + /// @brief SLM_TW_OBA_WAVEFORM metdata name + static constexpr const char* SLM_TW_OBA_WAVEFORM = "slm-tw-oba-waveform"; + /// @brief SLM_TW_SEP_AMP metdata name + static constexpr const char* SLM_TW_SEP_AMP = "slm-tw-sep-amp"; + /// @brief SLM_TW_SEP_AUX metdata name + static constexpr const char* SLM_TW_SEP_AUX = "slm-tw-sep-aux"; + /// @brief SLM_TW_SEP_DIR metdata name + static constexpr const char* SLM_TW_SEP_DIR = "slm-tw-sep-dir"; + /// @brief SLM_TW_SEP_FREQ metdata name + static constexpr const char* SLM_TW_SEP_FREQ = "slm-tw-sep-freq"; + /// @brief SLM_TW_SEP_OFFSET metdata name + static constexpr const char* SLM_TW_SEP_OFFSET = "slm-tw-sep-offset"; + /// @brief SLM_TW_SEP_WAVEFORM metdata name + static constexpr const char* SLM_TW_SEP_WAVEFORM = "slm-tw-sep-waveform"; + /// @brief SLM_TW_WASTE_AMP metdata name + static constexpr const char* SLM_TW_WASTE_AMP = "slm-tw-waste-amp"; + /// @brief SLM_TW_WASTE_AUX metdata name + static constexpr const char* SLM_TW_WASTE_AUX = "slm-tw-waste-aux"; + /// @brief SLM_TW_WASTE_DIR metdata name + static constexpr const char* SLM_TW_WASTE_DIR = "slm-tw-waste-dir"; + /// @brief SLM_TW_WASTE_FREQ metdata name + static constexpr const char* SLM_TW_WASTE_FREQ = "slm-tw-waste-freq"; + /// @brief SLM_TW_WASTE_OFFSET metdata name + static constexpr const char* SLM_TW_WASTE_OFFSET = "slm-tw-waste-offset"; + /// @brief SLM_TW_WASTE_WAVEFORM metdata name + static constexpr const char* SLM_TW_WASTE_WAVEFORM = "slm-tw-waste-waveform"; + }; + + namespace FrameValue + { + /// @brief HI_LO_FRAG metdata name + static constexpr const char* HI_LO_FRAG = "HiLoFrag"; ///< Fragmentation op mode of HiLoFrag + }; + + /// @class MBISDK::MBIAttr::TofCalibrationKey + /// @brief Keys present in tof calibration metadata + /// @author Greg Van Aken + namespace TofCalibrationKey + { + /// @brief SLOPE metdata name + static constexpr const char* SLOPE = "slope"; + /// @brief INTERCEPT metdata name + static constexpr const char* INTERCEPT = "intercept"; + /// @brief RESIDUAL_FIT metdata name + static constexpr const char* RESIDUAL_FIT = "mz_residual_terms"; + }; + + /// @class MBISDK::MBIAttr::CcsCalibrationKey + /// @brief Keys present in ccs calibration metadata + /// @author Greg Van Aken + namespace CcsCalibrationKey + { + /// @brief TYPE metdata name + static constexpr const char* TYPE = "type"; + /// @brief COEFFICIENTS metdata name + static constexpr const char* COEFFICIENTS = "coefficients"; + /// @brief CSS_COEFFICIENTS_HRIM metdata name + static constexpr const char* CSS_COEFFICIENTS_HRIM = "css_coefficients"; + /// @brief AT_COEFFICIENTS metdata name + static constexpr const char* AT_COEFFICIENTS = "at_coefficients"; + /// @brief MZ_COEFFICIENTS metdata name + static constexpr const char* MZ_COEFFICIENTS = "mz_coefficients"; + }; + + /// @class MBISDK::MBIAttr::CcsCalibrationValue + /// @brief Values present in ccs calibration metadata + /// @author Greg Van Aken + namespace CcsCalibrationValue + { + /// @brief POLYNOMIAL metdata name + static constexpr const char* POLYNOMIAL = "polynomial"; + /// @brief UNKNOWN metdata name + static constexpr const char* UNKNOWN = "unknown"; + }; + + /// @class MBISDK::MBIAttr::EyeOnCcsCalibrationKey + /// @brief Keys present in ccs calibration metadata + /// @author Greg Van Aken + namespace EyeOnCcsCalibrationKey + { + /// @brief PEAKS metdata name + static constexpr const char* PEAKS = "peaks"; + /// @brief CSS_COEFFICIENTS metdata name + static constexpr const char* CSS_COEFFICIENTS = "coefficients"; + /// @brief MIN_CCS metdata name + static constexpr const char* MIN_CCS = "min"; + /// @brief MAX_CCS metdata name + static constexpr const char* MAX_CCS = "max"; + /// @brief CCS_DEGREE metdata name + static constexpr const char* CCS_DEGREE = "degree"; + /// @brief CCS_AT_SURF metdata name + static constexpr const char* CCS_AT_SURF = "at_surfing"; + /// @brief CCS_CCAPS metdata name + static constexpr const char* CCS_CCAPS = "ccaps"; + /// @brief GAS_TYPE metdata name + static constexpr const char* GAS_TYPE = "Mass Flow.gas type"; + }; + + + /// @class MBISDK::MBIAttr::GlobalKey + /// @brief Keys present in Global Metadata + /// @author Greg Van Aken + namespace GlobalKey + { + /// @brief ACQ_AGT_FILEPATH metdata name + static constexpr const char* ACQ_AGT_FILEPATH = "acq-agt-filepath"; + /// @brief ACQ_BOARD_TEMP metdata name + static constexpr const char* ACQ_BOARD_TEMP = "acq-board-temp"; + /// @brief Data collection mode (SIFF, MIFF, etc) + static constexpr const char* ACQ_COLLECTION_MODE = "acq-collection-mode"; + /// @brief ACQ_COMMENTS metdata name + static constexpr const char* ACQ_COMMENTS = "acq-comments"; + /// @brief ACQ_LC_MODEL metdata name + static constexpr const char* ACQ_LC_MODEL = "acq-lc-model"; + /// @brief ACQ_MS_LEVEL metadata name + static constexpr const char* ACQ_MS_LEVEL = "acq-ms-level"; + /// @brief ACQ_MS_METHOD metdata name + static constexpr const char* ACQ_MS_METHOD = "acq-ms-method"; + /// @brief ACQ_MS_MODEL metdata name + static constexpr const char* ACQ_MS_MODEL = "acq-ms-model"; + /// @brief ACQ_NUM_FRAMES metdata name + static constexpr const char* ACQ_NUM_FRAMES = "acq-num-frames"; ///< Number of frames in the file. + /// @brief ACQ_SLIM_METHOD metdata name + static constexpr const char* ACQ_SLIM_METHOD = "acq-slim-method"; + /// @brief ACQ_SLIM_MODEL metdata name + static constexpr const char* ACQ_SLIM_MODEL = "acq-slim-model"; + /// @brief ACQ_SLIM_PATH_LENGTH metdata name + static constexpr const char* ACQ_SLIM_PATH_LENGTH = "acq-slim_path_length"; + /// @brief ACQ_SOFTWARE_VERSION metdata name + static constexpr const char* ACQ_SOFTWARE_VERSION = "acq-software-version"; + /// @brief ACQ_TAGS metdata name + static constexpr const char* ACQ_TAGS = "acq-tags"; + /// @brief ACQ_TEMPERATURE_CORRECTION metdata name + static constexpr const char* ACQ_TEMPERATURE_CORRECTION = "acq-temperature_correction"; + /// @brief ACQ_TIMESTAMP metdata name + static constexpr const char* ACQ_TIMESTAMP = "acq-timestamp"; + /// @brief ACQ_TUNE_FILE metdata name + static constexpr const char* ACQ_TUNE_FILE = "acq-tune-file"; + /// @brief ACQ_TYPE metdata name + static constexpr const char* ACQ_TYPE = "acq-type"; + /// @brief ACQ_VENDOR_METADATA metdata name + static constexpr const char* ACQ_VENDOR_METADATA = "acq-vendor-metadata"; + /// @brief ADC_AVG_MODE_ENABLE metdata name + static constexpr const char* ADC_AVG_MODE_ENABLE = "adc-avg-mode-enable"; + /// @brief ADC_AVG_MODE_FREQUENCY metdata name + static constexpr const char* ADC_AVG_MODE_FREQUENCY = "adc-avg-mode-frequency"; + /// @brief ADC_AVG_MODE_RESCALE metdata name + static constexpr const char* ADC_AVG_MODE_RESCALE = "adc-avg-mode-rescale"; + /// @brief ADC_BASELINE_STABILIZE_ENABLE metdata name + static constexpr const char* ADC_BASELINE_STABILIZE_ENABLE = "adc-baseline-stabilize-enable"; + /// @brief ADC_BASELINE_STABILIZE_MODE metdata name + static constexpr const char* ADC_BASELINE_STABILIZE_MODE = "adc-baseline-stabilize-mode"; + /// @brief ADC_CHANNEL metdata name + static constexpr const char* ADC_CHANNEL = "adc-channel"; + /// @brief ADC_COUPLING metdata name + static constexpr const char* ADC_COUPLING = "adc-coupling"; + /// @brief ADC_DIGITAL_OFFSET metdata name + static constexpr const char* ADC_DIGITAL_OFFSET = "adc-digital-offset"; + /// @brief ADC_DRIVER_REV metdata name + static constexpr const char* ADC_DRIVER_REV = "adc-driver-rev"; + /// @brief ADC_FIRMWARE_REV metdata name + static constexpr const char* ADC_FIRMWARE_REV = "adc-firmware-rev"; + /// @brief ADC_MASS_SPEC_RANGE metdata name + static constexpr const char* ADC_MASS_SPEC_RANGE = "adc-mass-spec-range"; + /// @brief ADC_MIN_NANOSECONDS metdata name + static constexpr const char* ADC_MIN_NANOSECONDS = "adc-min-nanoseconds"; + /// @brief ADC_MODEL metdata name + static constexpr const char* ADC_MODEL = "adc-model"; + /// @brief ADC_OFFSET metdata name + static constexpr const char* ADC_OFFSET = "adc-offset"; + /// @brief ADC_PULSE_POLARITY metdata name + static constexpr const char* ADC_PULSE_POLARITY = "adc-pulse-polarity"; + /// @brief ADC_PULSE_THRESHOLD metdata name + static constexpr const char* ADC_PULSE_THRESHOLD = "adc-pulse-threshold"; + /// @brief ADC_RANGE metdata name + static constexpr const char* ADC_RANGE = "adc-range"; + /// @brief ADC_RECORD_SIZE metdata name + static constexpr const char* ADC_RECORD_SIZE = "adc-record-size"; + /// @brief ADC_REDUCTION_MODE metdata name + static constexpr const char* ADC_REDUCTION_MODE = "adc-reduction-mode"; + /// @brief ADC_SAMPLE_RATE metdata name + static constexpr const char* ADC_SAMPLE_RATE = "adc-sample-rate"; ///< Digitizer sample rate. + /// @brief ADC_SELF_TRIGGER_DUTY_CYCLE metdata name + static constexpr const char* ADC_SELF_TRIGGER_DUTY_CYCLE = "adc-self-trigger-duty-cycle"; + /// @brief ADC_SELF_TRIGGER_ENABLE metdata name + static constexpr const char* ADC_SELF_TRIGGER_ENABLE = "adc-self-trigger-enable"; + /// @brief ADC_SELF_TRIGGER_FREQUENCY metdata name + static constexpr const char* ADC_SELF_TRIGGER_FREQUENCY = "adc-self-trigger-frequency"; + /// @brief ADC_SELF_TRIGGER_POLARITY metdata name + static constexpr const char* ADC_SELF_TRIGGER_POLARITY = "adc-self-trigger-polarity"; + /// @brief ADC_STREAMING_MODE metdata name + static constexpr const char* ADC_STREAMING_MODE = "adc-streaming-mode"; + /// @brief ADC_ZA_HYSTERESIS metdata name + static constexpr const char* ADC_ZA_HYSTERESIS = "adc-zs-hysteresis"; + /// @brief ADC_ZA_POSTGATE_SAMPLES metdata name + static constexpr const char* ADC_ZA_POSTGATE_SAMPLES = "adc-zs-postgate-samples"; + /// @brief ADC_ZA_PREGATE_SAMPLES metdata name + static constexpr const char* ADC_ZA_PREGATE_SAMPLES = "adc-zs-pregate-samples"; + /// @brief ADC_ZA_THRESHOLD metdata name + static constexpr const char* ADC_ZA_THRESHOLD = "adc-zs-threshold"; + /// @brief ADC_ZERO_VALUE metdata name + static constexpr const char* ADC_ZERO_VALUE = "adc-zero-value"; + /// @brief CAL_CCS metdata name + static constexpr const char* CAL_CCS = "cal-ccs"; + /// @brief GAS_MASS metdata name + static constexpr const char* GAS_MASS = "gas-mass"; + /// @brief MERGED_FILE metdata name + static constexpr const char* MERGED_FILE = "merged-file"; + /// @brief READINGS metdata name + static constexpr const char* READINGS = "readings"; + /// @brief SMP_AMOUNT metdata name + static constexpr const char* SMP_AMOUNT = "smp-amount"; + /// @brief SMP_AMOUNT_UNIT metdata name + static constexpr const char* SMP_AMOUNT_UNIT = "smp-amount-unit"; + /// @brief SMP_BALANCE_TYPE metdata name + static constexpr const char* SMP_BALANCE_TYPE = "smp-balance-type"; + /// @brief SMP_CONCENTRATION metdata name + static constexpr const char* SMP_CONCENTRATION = "smp-concentration"; + /// @brief SMP_CONCENTRATION_UNIT metdata name + static constexpr const char* SMP_CONCENTRATION_UNIT = "smp-concentration-unit"; + /// @brief SMP_DILUTION_FACTOR metdata name + static constexpr const char* SMP_DILUTION_FACTOR = "smp-dilution-factor"; + /// @brief SMP_INJECTION_VOLUME metdata name + static constexpr const char* SMP_INJECTION_VOLUME = "smp-injection-volume"; + /// @brief SMP_MOLECULE_CLASS metdata name + static constexpr const char* SMP_MOLECULE_CLASS = "smp-molecule-class"; + /// @brief SMP_NAME metdata name + static constexpr const char* SMP_NAME = "smp-name"; + /// @brief SMP_PLATE_CODE metdata name + static constexpr const char* SMP_PLATE_CODE = "smp-plate-code"; + /// @brief SMP_PLATE_POSITION metdata name + static constexpr const char* SMP_PLATE_POSITION = "smp-plate-position"; + /// @brief SMP_POSITION metdata name + static constexpr const char* SMP_POSITION = "smp-position"; + /// @brief SMP_RACK_CODE metdata name + static constexpr const char* SMP_RACK_CODE = "smp-rack-code"; + /// @brief SMP_SOLVENT metdata name + static constexpr const char* SMP_SOLVENT = "smp-solvent"; + /// @brief SMP_TYPE metdata name + static constexpr const char* SMP_TYPE = "smp-type"; + /// @brief STITCHED_FILE metdata name + static constexpr const char* STITCHED_FILE = "stitched-file"; + /// @brief USR_GROUP metdata name + static constexpr const char* USR_GROUP = "usr-group"; + /// @brief USR_ROLE metdata name + static constexpr const char* USR_ROLE = "usr-role"; + /// @brief USR_USERNAME metdata name + static constexpr const char* USR_USERNAME = "usr-username"; + + // This is the list of metadata items which are considered "redundant", in that they do not change from one frame + // to the next, and can afford to be stored and read from the global metadata list instead. + // while the text of the metadata item will be the same, the ENUM value will carry the GMD prefix to help + // associate these entries as global metadata items instead of frame metadata items. + // To maintain backwards compatability, the frame metadata functions will be changed to seek out the frame metadata value first, + // and then look for the global metadata item should the frame metadata item not be found + /// @brief GMD_CAL_DT_POLYNOMIAL metdata name + static constexpr const char* GMD_CAL_DT_POLYNOMIAL = "cal-dt-polynomial"; + /// @brief GMD_CAL_DT_POWER_FLAGS metdata name + static constexpr const char* GMD_CAL_DT_POWER_FLAGS = "cal-dt-power-flags"; + /// @brief GMD_CAL_DT_TRADITIONAL metdata name + static constexpr const char* GMD_CAL_DT_TRADITIONAL = "cal-dt-traditional"; ///< Traditional (json) ccs coefficients. + /// @brief GMD_CAL_MS_POLYNOMIAL metdata name + static constexpr const char* GMD_CAL_MS_POLYNOMIAL = "cal-ms-polynomial"; + /// @brief GMD_CAL_MS_POWER_FLAGS metdata name + static constexpr const char* GMD_CAL_MS_POWER_FLAGS = "cal-ms-power-flags"; + /// @brief GMD_CAL_MS_TRADITIONAL metdata name + static constexpr const char* GMD_CAL_MS_TRADITIONAL = "cal-ms-traditional"; ///< Traditional (json) coefficients. + /// @brief GMD_FRM_FRAG_ENERGY_MODE metdata name + static constexpr const char* GMD_FRM_FRAG_ENERGY_MODE = "frm-frag-energy-mode"; + /// @brief GMD_FRM_FRAG_OP_MODE metdata name + static constexpr const char* GMD_FRM_FRAG_OP_MODE = "frm-frag-op-mode"; ///< The fragmentation operational mode + /// @brief GMD_FRM_INTENSITY_LIMIT metdata name + static constexpr const char* GMD_FRM_INTENSITY_LIMIT = "frm-intensity-limit"; + /// @brief GMD_FRM_METHOD_STATE metdata name + static constexpr const char* GMD_FRM_METHOD_STATE = "frm-method-state"; + /// @brief GMD_FRM_MUX_GATE metdata name + static constexpr const char* GMD_FRM_MUX_GATE = "frm-mux-gate"; + /// @brief GMD_FRM_MUX_SEQUENCE metdata name + static constexpr const char* GMD_FRM_MUX_SEQUENCE = "frm-mux-sequence"; + /// @brief GMD_FRM_NUM_MICROFRAMES metdata name + static constexpr const char* GMD_FRM_NUM_MICROFRAMES = "frm-num-microframes"; + /// @brief GMD_FRM_POLARITY metdata name + static constexpr const char* GMD_FRM_POLARITY = "frm-polarity"; + /// @brief GMD_FRM_TIMIING_INTENTS metdata name + static constexpr const char* GMD_FRM_TIMIING_INTENTS = "frm-timing-intents"; + /// @brief GMD_KEY_FRAG metdata name + static constexpr const char* GMD_KEY_FRAG = "key-frag"; + /// @brief GMD_SLIM_RF_FUNNEL_POWER metdata name + static constexpr const char* GMD_SLIM_RF_FUNNEL_POWER = "slim-rf-funnel-power"; + /// @brief GMD_SLM_ENTRANCE_OFFSET metdata name + static constexpr const char* GMD_SLM_ENTRANCE_OFFSET = "slm-entrance-offset"; + /// @brief GMD_SLM_EXIT_CL metdata name + static constexpr const char* GMD_SLM_EXIT_CL = "slm-exit-cl"; + /// @brief GMD_SLM_EXIT_IN metdata name + static constexpr const char* GMD_SLM_EXIT_IN = "slm-exit-in"; + /// @brief GMD_SLM_EXIT_OUT metdata name + static constexpr const char* GMD_SLM_EXIT_OUT = "slm-exit-out"; + /// @brief GMD_SLM_FUNNEL_CL metdata name + static constexpr const char* GMD_SLM_FUNNEL_CL = "slm-funnel-cl"; + /// @brief GMD_SLM_FUNNEL_IN metdata name + static constexpr const char* GMD_SLM_FUNNEL_IN = "slm-funnel-in"; + /// @brief GMD_SLM_FUNNEL_OUT metdata name + static constexpr const char* GMD_SLM_FUNNEL_OUT = "slm-funnel-out"; + /// @brief GMD_SLM_OBA_GATE metdata name + static constexpr const char* GMD_SLM_OBA_GATE = "slm-oba-gate"; + /// @brief GMD_SLM_QUAD_BIAS metdata name + static constexpr const char* GMD_SLM_QUAD_BIAS = "slm-quad-bias"; + /// @brief GMD_SLM_RF_BOTTOM_DRIVE metdata name + static constexpr const char* GMD_SLM_RF_BOTTOM_DRIVE = "slm-rf-bottom-drive"; + /// @brief GMD_SLM_RF_BOTTOM_FREQ metdata name + static constexpr const char* GMD_SLM_RF_BOTTOM_FREQ = "slm-rf-bottom-freq"; + /// @brief GMD_SLM_RF_BOTTOM_NEG metdata name + static constexpr const char* GMD_SLM_RF_BOTTOM_NEG = "slm-rf-bottom-neg"; + /// @brief GMD_SLM_RF_BOTTOM_POS metdata name + static constexpr const char* GMD_SLM_RF_BOTTOM_POS = "slm-rf-bottom-pos"; + /// @brief GMD_SLM_RF_BOTTOM_POWER metdata name + static constexpr const char* GMD_SLM_RF_BOTTOM_POWER = "slm-rf-bottom-power"; + /// @brief GMD_SLM_RF_FUNNEL_DRIVE metdata name + static constexpr const char* GMD_SLM_RF_FUNNEL_DRIVE = "slm-rf-funnel-drive"; + /// @brief GMD_SLM_RF_FUNNEL_FREQ metdata name + static constexpr const char* GMD_SLM_RF_FUNNEL_FREQ = "slm-rf-funnel-freq"; + /// @brief GMD_SLM_RF_FUNNEL_NEG metdata name + static constexpr const char* GMD_SLM_RF_FUNNEL_NEG = "slm-rf-funnel-neg"; + /// @brief GMD_SLM_RF_FUNNEL_POS metdata name + static constexpr const char* GMD_SLM_RF_FUNNEL_POS = "slm-rf-funnel-pos"; + /// @brief GMD_SLM_RF_QUAD_DRIVE metdata name + static constexpr const char* GMD_SLM_RF_QUAD_DRIVE = "slm-rf-quad-drive"; + /// @brief GMD_SLM_RF_QUAD_FREQ metdata name + static constexpr const char* GMD_SLM_RF_QUAD_FREQ = "slm-rf-quad-freq"; + /// @brief GMD_SLM_RF_QUAD_NEG metdata name + static constexpr const char* GMD_SLM_RF_QUAD_NEG = "slm-rf-quad-neg"; + /// @brief GMD_SLM_RF_QUAD_POS metdata name + static constexpr const char* GMD_SLM_RF_QUAD_POS = "slm-rf-quad-pos"; + /// @brief GMD_SLM_QUAD_POWER metdata name + static constexpr const char* GMD_SLM_QUAD_POWER = "slm-rf-quad-power"; + /// @brief GMD_SLM_RF_TOP_DRIVE metdata name + static constexpr const char* GMD_SLM_RF_TOP_DRIVE = "slm-rf-top-drive"; + /// @brief GMD_SLM_RF_TOP_FREQ metdata name + static constexpr const char* GMD_SLM_RF_TOP_FREQ = "slm-rf-top-freq"; + /// @brief GMD_SLM_RF_TOP_NEG metdata name + static constexpr const char* GMD_SLM_RF_TOP_NEG = "slm-rf-top-neg"; + /// @brief GMD_SLM_RF_TOP_POS metdata name + static constexpr const char* GMD_SLM_RF_TOP_POS = "slm-rf-top-pos"; + /// @brief GMD_SLM_RF_TOP_POWER metdata name + static constexpr const char* GMD_SLM_RF_TOP_POWER = "slm-rf-top-power"; + /// @brief GMD_SLM_SLIM_BIAS metdata name + static constexpr const char* GMD_SLM_SLIM_BIAS = "slm-slim-bias"; + /// @brief GMD_SLM_SLIM_OFFSET metdata name + static constexpr const char* GMD_SLM_SLIM_OFFSET = "slm-slim-offset"; + /// @brief GMD_SLM_TW_OBA_AMP metdata name + static constexpr const char* GMD_SLM_TW_OBA_AMP = "slm-tw-oba-amp"; + /// @brief GMD_SLM_TW_OBA_AUX metdata name + static constexpr const char* GMD_SLM_TW_OBA_AUX = "slm-tw-oba-aux"; + /// @brief GMD_SLM_TW_OBA_DIR metdata name + static constexpr const char* GMD_SLM_TW_OBA_DIR = "slm-tw-oba-dir"; + /// @brief GMD_SLM_TW_OBA_FREQ metdata name + static constexpr const char* GMD_SLM_TW_OBA_FREQ = "slm-tw-oba-freq"; + /// @brief GMD_SLM_TW_OBA_OFFSET metdata name + static constexpr const char* GMD_SLM_TW_OBA_OFFSET = "slm-tw-oba-offset"; + /// @brief GMD_SLM_TW_OBA_WAVEFORM metdata name + static constexpr const char* GMD_SLM_TW_OBA_WAVEFORM = "slm-tw-oba-waveform"; + /// @brief GMD_SLM_TW_SEP_AMP metdata name + static constexpr const char* GMD_SLM_TW_SEP_AMP = "slm-tw-sep-amp"; + /// @brief GMD_SLM_TW_SEP_AUX metdata name + static constexpr const char* GMD_SLM_TW_SEP_AUX = "slm-tw-sep-aux"; + /// @brief GMD_SLM_TW_SEP_DIR metdata name + static constexpr const char* GMD_SLM_TW_SEP_DIR = "slm-tw-sep-dir"; + /// @brief GMD_SLM_TW_SEP_FREQ metdata name + static constexpr const char* GMD_SLM_TW_SEP_FREQ = "slm-tw-sep-freq"; + /// @brief GMD_SLM_TW_SEP_OFFSET metdata name + static constexpr const char* GMD_SLM_TW_SEP_OFFSET = "slm-tw-sep-offset"; + /// @brief GMD_SLM_TW_SEP_WAVEFORM metdata name + static constexpr const char* GMD_SLM_TW_SEP_WAVEFORM = "slm-tw-sep-waveform"; + /// @brief GMD_SLM_TW_WASTE_AMP metdata name + static constexpr const char* GMD_SLM_TW_WASTE_AMP = "slm-tw-waste-amp"; + /// @brief GMD_SLM_TW_WASTE_AUX metdata name + static constexpr const char* GMD_SLM_TW_WASTE_AUX = "slm-tw-waste-aux"; + /// @brief GMD_SLM_TW_WASTE_DIR metdata name + static constexpr const char* GMD_SLM_TW_WASTE_DIR = "slm-tw-waste-dir"; + /// @brief GMD_SLM_TW_WASTE_FREQ metdata name + static constexpr const char* GMD_SLM_TW_WASTE_FREQ = "slm-tw-waste-freq"; + /// @brief GMD_SLM_TW_WASTE_OFFSET metdata name + static constexpr const char* GMD_SLM_TW_WASTE_OFFSET = "slm-tw-waste-offset"; + /// @brief GMD_SLM_TW_WASTE_WAVEFORM metdata name + static constexpr const char* GMD_SLM_TW_WASTE_WAVEFORM = "slm-tw-waste-waveform"; + }; + }; + + /// @class MBISDK::MBINumeric + /// @brief Numeric constants for math and conversions + /// @author Greg Van Aken + namespace MBINumeric + { + /// @brief NANOSECONDS_PER_SECOND numeric constant + static constexpr const double NANOSECONDS_PER_SECOND = 1e9; ///< Number of nanoseconds in a second. + /// @brief MICROSECONDS_PER_SECOND numeric constant + static constexpr const double MICROSECONDS_PER_SECOND = 1e6; ///< Number of microseconds in a second. + /// @brief MILLISECONDS_PER_SECOND numeric constant + static constexpr const double MILLISECONDS_PER_SECOND = 1e3; ///< Number of milliseconds in a second. + }; + + /// @class MBISDK::MBIFileAcces + /// @brief The ways in which files can be accessed / initialized + /// @author Greg Van Aken + namespace MBIFileAccess + { + /// @brief READONLY file access constant + static constexpr const char* READONLY = "r"; + /// @brief WRITE file access constant + static constexpr const char* WRITE = "w"; + /// @brief APPEND file access constant + static constexpr const char* APPEND = "a"; + }; + +} diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIExceptions.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIExceptions.h new file mode 100644 index 0000000000..a0b9ff3844 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIExceptions.h @@ -0,0 +1,38 @@ +// MBIException.h - SDK top-level exceptions +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2024 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: Bennett Kalafut * + * 0.0.0.0 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#pragma once + +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif + +#include + +namespace MBISDK +{ +namespace Exceptions +{ + class MBISDKUnknownCaseException : public std::runtime_error + { + using std::runtime_error::runtime_error; + }; + + class MBISDKEmptyFile : public std::runtime_error + { + using std::runtime_error::runtime_error; + }; +} +} diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIFile.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIFile.h new file mode 100644 index 0000000000..d8224a1479 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIFile.h @@ -0,0 +1,405 @@ +// MBIFile.h - The top-level API object interface to MBI data. +// dbodden 8/18/2021 +// 0.0.0.0 +#pragma once +#pragma warning(disable : 4996) +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif + +#include "MBIConstants.h" +#include "MBIMetadata.h" +#include "MBICalibration.h" +#include "MBIFrame.h" + +#include +#include +#include "MBIFileHDF5Adapter.h" +#include + +namespace MBISDK +{ +extern "C" +{ + /*! @class MBISDK::MBIFile + * @brief Top level object for interfacing to the MBI data API. + * @author Doug Bodden */ + class MBI_DLLCPP MBIFile + { + + public: + /// + ///Experiment types represented by a MBI file. + /// + enum class ExperimentType + { + /// @brief An MS1 experiment. Note that if collected on MOBIE with a MassHunter method with fragmentation turned on this can be AIF + MS1, + /// @brief AIF data collected through EYEON, as e.g. the high-energy file for stiteched (MIFF) MAF + AIF, + /// @brief MAF with a single input file + SIFF_MAF, + /// @brief MAF stitched from two input files. + MIFF_MAF + }; + + + /// @brief MBIFile unexpected error code + static const int ERR_UNEXPECTED = -1; + /// @brief MBIFile Success code + static const int ERR_SUCCESS = 0; + /// @brief MBIFile file not found error code + static const int ERR_FILE_NOT_FOUND = 1; + /// @brief MBIFile HDF5 file error code + static const int ERR_HDF5_FILE_ERROR = 2; + /// @brief MBIFile file not initialized error code + static const int ERR_FILE_NOT_INITIALIZED = 3; + /// @brief MBIFile metadata not loaded error code + static const int ERR_METADATA_NOT_LOADED = 101; + /// @brief MBIFile frame not loaded error code + static const int ERR_FRAME_NOT_LOADED = 102; + /// @brief MBIFile bad frame index error code + static const int ERR_BAD_FRAME_INDEX = 201; + /// @brief MBIFile bad scan index error code + static const int ERR_BAD_SCAN_INDEX = 202; + /// @brief MBIFile item missing error code + static const int ERR_ITEM_MISSING = 301; + /// @brief MBIFile operation not supported error code + static const int ERR_OPERATION_NOT_SUPPORTED = 401; + + // MBISDK-45 - compiler flagged an uninitialized memory use warning for processOffsetLengthStats(). Indeed, if the number of frames retrieved is actually zero, + // then some of the function variables used to set the MBI object members would be uninitialized. Created this new error to represent that condition. + /// @brief MBIFile operation zero frames return error code + static const int ERR_ZERO_FRAMES = 501; + + /// + /// Construct an MBIFile for read from a file. + /// + MBIFile(); + + /// + /// Construct an MBIFile for read from a file. + /// + /// Path to the file to load. + MBIFile(const char* path); + + /// + /// Close the input file and free any associated resources. + /// + void Close(); + + /// + /// Retrieve the number of scans per Frame for the experiment. + /// + /// The frame index. + /// The number of scans in the Frame., or 0 if nFrame is invalid + int GetMaxScansInFrame(int nFrame); + + /// + /// set the global metadata item for the file. + /// + /// + void SetGlobalMetaData(std::shared_ptr globalMetadata); + + /// + /// The MBIFile's mass calibration. + /// This method will be removed in a future version of MBI SDK. + /// + /// + [[deprecated("Access mass calibrations through Frame objects.")]] + MBISDK::TofCalibration GetCalibration(); + + /// + /// The MBIFile's calibration. + /// + /// + MBISDK::EyeOnCcsCalibration GetEyeOnCCSCalibration(); + + /// + /// The MBIFile's calibration. + /// + /// + std::string GetCCSCalibration(std::vector* coefficients); + + /// @brief + /// The type of experiment (e.g. MS1, SIFF-MAF) represented by the MBI File. + /// @return + /// An instance of the enum MBIFile::ExperimentType + ExperimentType GetExperimentType(); + + /// + /// THe number of Frames in the MBIFile. + /// + /// THe number of Frames in the MBIFile. + int NumFrames(); + + /// + /// Load all Frames and return a shared pointer to the collection. + /// + /// A shared pointer to a vector of Frame pointers, all loaded, or a NULL ptr is frameIndex is invalid + std::shared_ptr>> GetFrames(); + + /// + /// Load a Frame (if unloaded) and return a shared_ptr to it. + /// + /// 1-based index into the Frame collection. + /// shared_ptr to a loaded Frame w/ the specified index. + std::shared_ptr GetFrame(int frameIndex); + + /// + /// Return the metadata pointer for a given Frame. + /// + /// 1-based index into the frame collection. + /// shared_ptr to the frame's metadata object, or NULL if frameIndex is invalid. + std::shared_ptr GetFrameMetadata(int frameIndex); + + /// + /// Deep-load a frame's data. If you only want to deepload frames based on metadata criteria, you may use this to avoid calling GetFrames() to load them all. + /// + /// The 1-based index into the frame collection. + void LoadFrameData(int frameIndex); + + /// + /// Unload the data sets and memory associated w/ the Frame, and set it to unloaded state. + /// + void UnloadFrame(int frameIndex); + + /// + /// Return the max scan samples for the experiment. + [[deprecated]] + /// + /// The max number of samples in a scan in the file. + /// This method will be removed in a future version of MBI SDK. + int GetMaxPointsInScan(); + + /// + /// Retrieve the name of the input file (full path). + /// + /// std::string the path used to open the file. + std::shared_ptr GetFilename(); + + /// + /// Set the name of the input file (full path). + /// + /// Path to the file to load. + void SetFilename(const char* path); + + /// + /// Retrieve the RT-TIC for a frame. + /// + /// A pair, first being time since beginning of experiment, the second being the TIC, or empty std::pair if frameIndex is invalid.. + std::pair GetRtTic(int frameIndex); + + /// + /// Evaluate the frame index being passed in to ensure it is valid + /// + /// A bool checking it is safe to use as an array index + bool CheckFrameIndex(int nFrameIndex); + + /// + /// Retrieve a start time for a frame metadata + /// + /// A double from frame metadata + double GetFrameStartTime(std::shared_ptr pFrameMD); + + /// + /// load the arrival time intensity counts for arrival bins in a frame. + /// + /// The 1-based index of the frame desired. + /// A vector of arrival time intensity counts per bin in the frame. + std::shared_ptr > loadAtTic(int frameIndex); + + /// + /// Initialize the MBIFile object. + /// + bool Init(); + + /// + /// Return the error code from the last method called. + /// + /// The error code from the last method call. + int GetErrorCode(); + + /// + /// Return an error message from the last method call. + /// + /// The error message from the last method call. + std::string GetErrorMessage(); + + /// + /// Retrieve the sample rate from global meta data + /// + /// A double, the sample rate from the file's global metadata + double GetSampleRate(); + + /// + /// Retrieve the number of frames global meta data + /// + /// An int, the number of frames from the file's global metadata + int GetNumFrames(); + + /// + /// Return the version string of the library. + /// + /// + std::string GetVersion(); + + /// + /// + /// + /// + std::shared_ptr GetGlobalMetaData(); + + /// + /// Return the member variable value representing the initialzation status of the class. + /// Many functions of the class rely on data being properly initialized. By checking this status first, the library + /// can be protected against attempted usage in an uninitialized condition + /// + /// The value stored in the member variable, true or false. + bool IsInitialized(); + + /// + /// Return the lowest offset length statistics from all frames in the data file. + /// This method will be removed from a future version of MBI SDK + /// + /// The lowest value computed from all frames. + [[deprecated]] + int64_t GetToFOffset(); + + /// + /// Return the length of offset length statistics from all frames in the data file. + /// This method will be removed in a future version of MBI SDK. + /// + /// The length of all value computed from all frames. + [[deprecated]] + int64_t GetToFLength(); + + /// + /// Return the lowest arrival bin offset statistics from all frames in the data file. + /// This method will be removed from a future version of MBI SDK + /// + /// The lowest value computed from all frames. + [[deprecated]] + size_t GetArrivalBinOffset(); + + /// + /// Return the length of arrival bin offset statistics from all frames in the data file. + /// This method will be removed from a future version of MBI SDK + /// + /// The length of all value computed from all frames. + [[deprecated]] + size_t GetArrivalBinLength(); + + + /// + /// Return specific RT_TIC data from a specific frame + /// + /// int specific rt value + int64_t GetSpecificRtTicValue(int nFrameIndex); + + /// + /// List of RT Tic values + /// + /// List of int64_t values + std::shared_ptr>> GetRtTicList(); + + /// + /// Retrieves single metadata item + /// + /// String of metadata item + std::string getMetaDataItem(std::string key); + + /// + /// Retrieves single metadata item + /// + /// Int of metadata item + int getMetaDataItemInt(std::string key); + + /// + /// Retrieves single metadata item + /// + /// Double of metadata item + double getMetaDataItemDouble(std::string key); + + /// + /// Set the external error message + /// + void setExternalErrorMessage(std::string input_str); + + /// + /// Retrieves external error message + /// + /// external error message + std::string getExternalErrorMessage(); + + private: + enum class CollectionMode + { + UNKNOWN, + NONE, + SIFF, + MIFF + }; + + void setErrorCode(int error_code); + MBIFileHDF5Adapter * getFileAbstract(); + void loadAllFrameMetadata(); + std::shared_ptr loadFrameMetadata(int frameIndex); + void loadRtTic(); + void processOffsetLengthStats(); + + int MSLevel(); + + CollectionMode AcqCollectionMode(); + ExperimentType MIFFFileCollectionMode(); + + + private: + bool is_initialized; + bool processed_offset_length_stats; + int error_code; + std::string external_error_message; + std::shared_ptr < std::map> map_meta_all; + // file-oriented members + std::shared_ptr input_file_path; + + // Abstract data members + // Frame collection: + std::shared_ptr >> frame_collection; + std::shared_ptr >> frame_metadata_collection; + + int num_frames; ///< Number of frames present in the file. + int total_scans; ///< Number of scans in the file. + int max_scans; ///< Maximum number of scans in a frame in the file. + int max_samples; ///< Maximum number of samples in a frame in the file. + int total_samples; ///< Number of samples in the file. + + std::shared_ptr global_metadata; + + // Acquisition metadata + int ms_level; /// MS1 or MS2 + CollectionMode collection_mode; // SIFF or MIFF; corresponds to acq-collection-mode in metadata + + // Frame-level internal implementation stuff + + std::shared_ptr>> rt_tic_collection; + std::string oob_error_message; + bool has_rt_tic; + + int64_t tof_offset; + int64_t tof_length; + size_t scan_offset; + size_t scan_length; + + MBIFileHDF5Adapter * mbifile_abstract; + }; +} +} diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIFileHDF5Adapter.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIFileHDF5Adapter.h new file mode 100644 index 0000000000..b13134e4ba --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIFileHDF5Adapter.h @@ -0,0 +1,198 @@ +// MBI Data Access API * +// Copyright 2024 MOBILion Systems, Inc. ALL RIGHTS RESERVED +// MBIFileHDF5Adapter.h - Top-level object initialized and gatheing point of HDF5 operations +// Revised by B. Kalafut, August 2004 +// Curt Lindmark 9/1/2021 + +// 0.0.0.0 +#pragma once +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif + +#include "MBIConstants.h" +#include "MBIMetadata.h" +#include "MBICalibration.h" +#include "MBIFrame.h" + +#include +#include +#include + +// Do not include H5cpp.h here. +// All methods with signatures that directly reference HDF5 classes or structures should be in H5Bridge, +// which is declared as an opaque pointer here and implemented in the source file. + +namespace MBISDK +{ + + /// @class MBISDK::MBIFileHDF5Adapter + /// @brief Top level object for interfacing to the MBI data API. + /// @author Doug Bodden */ + class MBI_DLLCPP MBIFileHDF5Adapter + { + + public: + /// @brief HDF5 Adapter Unexpected error + static const int ERR_UNEXPECTED = -1; + /// @brief HDF5 Adapter Success code + static const int ERR_SUCCESS = 0; + /// @brief HDF5 Adapter Internal error + static const int ERR_HDF5_INTERNAL_ERROR = 1; + /// @brief HDF5 Adapter name size constant + static const int NAME_SIZE = 50; + /// @brief HDF5 Adapter Upper limit for metadata + static const int HDF5_METADATA_SIZE_UPPER_LIMIT = 32 * 1024 * 1024; //This is the upper limit of writing as restricted by HDF5. + //It equates to 33,554,432 bytes. + + /// + /// Construct an MBIFile for read from a file. + /// + /// Path to the file to load. + /// File being opened. + MBIFileHDF5Adapter(std::shared_ptr path, MBIFile *parent); + + // We need to prevent the insertion of a compiler-defined destructor where the opaque pointer to H5Bridge is incomplete + // In the implementation file MBIFileHDF5Adapter.cpp, we generate the default destructor by + // MBIFileHDF5Adapter::~MBIFileHDF5Adapter() = default; + ~MBIFileHDF5Adapter(); + + /// + /// Initialize an existing MBI file. + /// + bool Init(); + + /// + /// Close the input file and free any associated resources. + /// + void Close(); + + /// + /// Retrieve the name of the input file (full path). + /// + /// std::string the path used to open the file. + std::shared_ptr GetFilename(); + + /// + /// Retrieve a start time for a frame metadata + /// + /// Pointer to frame metadata. + /// A double from frame metadata + double GetFrameStartTime(std::shared_ptr pFrameMD); + + /// + /// Retrieve a start time for a frame metadata + /// + /// Pointer to frame metadata. + /// A double from frame metadata + std::string GetCalibration(std::shared_ptr pFrameMD); + + /// + /// Retrieve a list of intensities for a given frame + /// + /// Frame index. + /// A list of intensities for a given frame + std::shared_ptr> loadSparseSampleIntensities(int frameIndex); + + /// + /// Retrieve a list of samples for a given frame + /// + /// Frame index. + /// A list of samples for a given frame + std::shared_ptr>> loadScanSampleIndexPairs(int frameIndex); + + /// + /// Retrieve a list of sample offsets for a given frame + /// + /// Frame index. + /// A list of sample offsets for a given frame + std::shared_ptr> loadScanSampleOffsets(int frameIndex); + + /// + /// Retrieve a list of scan index pair offsets for a given frame + /// + /// Frame index. + /// A list of scan index pair offsets for a given frame + std::shared_ptr> loadScanIndexPairOffsets(int frameIndex); + + /// + /// Retrieve a list of scan index pair offsets for a given frame + /// + /// Frame index. + /// A list of scan index pair offsets for a given frame + std::shared_ptr> LoadTriggerTimeStamps(int frameIndex); + + // Frame-level internal implementation stuff + /// + /// Retrieve a list of frame metadata for a given frame + /// + /// Frame index. + /// Metadata map. + /// A list of frame metadata for a given frame + std::shared_ptr loadFrameMetadata(int frameIndex, std::map &metadataMap); + + /// + /// Retrieve a list of retention time data for a given file + /// + /// Pointer to vector for rttic data. + /// A list of retention time data for a given file + bool LoadRtTic(std::vector *rtTic); + + /// + /// Retrieve a list of arrival time data for a given file + /// + /// Frame index. + /// Size of at_tic list. + /// A list of arrival time data for a given file + std::shared_ptr > LoadAtTic(int nFrameIndex, int nSize); + + /// + /// Loads global metadata information to map + /// + void LoadGlobalMetadataToMap(std::map& mapMetaAll); + + /// + /// Loads frame metadata information for a given frame to map + /// + /// Frame index. + /// Metadata map. + void LoadFrameMetadataToMap(int frameIndex, std::map& mapMetaAll); + + /// + /// Return the error code from the last method called. + /// + /// The error code from the last method call. + int GetErrorCode(); + + /// + /// Return an error message from the last method call. + /// + /// The error message from the last method call. + std::string GetErrorMessage(); + + private: + + // Thin HDF5 wrapper + class H5Bridge; + + // Opaque pointer to HDF5 wrapper + std::unique_ptr pH5Bridge; + + MBIFile* parent_file; + // file-oriented members + std::shared_ptr input_file_path; + + void setErrorCode(int local_error_code); + std::string error_message; + int error_code; + bool has_rt_tic; + }; +} diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIFrame.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIFrame.h new file mode 100644 index 0000000000..5160e31753 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIFrame.h @@ -0,0 +1,556 @@ +// Frame.h - Represents a frame (ion injection event). +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2021 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: DougBodden * + * 0.0.0.0 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#pragma once +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif + +#include "MBIMetadata.h" +#include "MBICalibration.h" +#include +#include + + + +namespace MBISDK +{ + class MBIFile; //forward declaration + + /*! @struct MBISDK::COOArray + * @brief A structure holding a coordinate object array + */ + template + struct COOArray + { + std::vector data; + std::vector rowIndices; + std::vector columnIndices; + size_t nRows; + size_t nColumns; + size_t nnz; + }; + + /*! @struct MBISDK::CSRArray + * @brief A structure for an aray in the Compressed Sparse Row format + */ + template + struct CSRArray + { + std::vector data; + std::vector indices; + std::vector indptr; + size_t nRows; + size_t nColumns; + size_t nnz; + }; + +extern "C" +{ + + + /*! @struct MBIDSK::MassSpectrum + * @brief A 1-D collection of intensities, together with values to assign on the "x" axis + */ + struct MassSpectrum + { + std::vector intensities; + std::vector indices; + std::vector mz; + bool isZeroPadded = false; + size_t nnz; + }; + + + + /*! @class MBISDK::Frame + * @brief A class allowing interface with an individual MBI Frame within a file. + */ + class MBI_DLLCPP Frame + { + public: + /// + /// Default constructor for creating a Frame independently of a file to laod it from. + /// This should generally NOT be called by API users looking to use Frame/Scan data, use MBIFile::GetFrame() instead. + /// + Frame(); + + /// + /// Construct a frame w/ metadata only (for fast access without deep load). + /// This should generally NOT be called by API users looking to use Frame/Scan data, use MBIFile::GetFrame() instead. + /// + /// The frame's metadata. + /// The frame's file pointer. + Frame(std::shared_ptr metadata, MBISDK::MBIFile* pFile); + + /// + /// Fully construct and load a frame from abstract metadata and a particular sparse matrix implementation. + /// This should generally NOT be called by API users looking to use Frame/Scan data, use MBIFile::GetFrame() instead. + /// + /// The frame's metadata. + /// The file's pointer. + /// The sparse vector of samples (intensities) across the entire frame. + /// The start/stop index values per scan. + /// The offset into the sample intensities per scan. + /// The offset into the sample index pairs per scan. + /// The list of AT_TIC for each frame. + /// The list of trigger timestamps for each scan. + Frame(std::shared_ptr metadata, MBISDK::MBIFile* pFile, + std::shared_ptr> inputSparseSampleIntensities, + std::shared_ptr>> inputGates, + std::shared_ptr> inputSampleOffsets, + std::shared_ptr> inputGateIndexOffsets, + std::shared_ptr> inputVecAtTic, + std::shared_ptr> inputVecTriggerTimeStamps); + + /// + /// Indicates whether a Frame's deep-data load has been completed. If not, it's usually + /// safest to do a MBIFile::GetFrame(frameIndex). + /// + /// true if loaded, false if not loaded. + bool IsLoaded(); + + /// + /// Load metadata into a Frame. + /// This should not be used if you are reading an MBI file from disk. + /// + /// Key/value pairs of metadata. + void LoadMetadata(std::shared_ptr metadata); + + /// + /// Load the abstract sparse cube data to the Frame for later use. + /// This should generally NOT be called by API users looking to use Frame/Scan data, use MBIFile::GetFrame() instead. + /// + /// An implementation-specific approach to storing sparse intinsity data. + /// An implementation-specific approach to storing sparse intinsity data. + /// An implementation-specific approach to storing sparse intinsity data. + /// An implementation-specific approach to storing sparse intinsity data. + /// The list of AT_TIC for each frame. + /// The list of trigger timestamps for each scan. + void Load(std::shared_ptr> inputSparseSampleIntensities, + std::shared_ptr>> inputGates, + std::shared_ptr> inputSampleOffsets, + std::shared_ptr> intputGateIndexOffsets, + std::shared_ptr> inputVecAtTic, + std::shared_ptr> inputVecTriggerTimeStamps); + + /// + /// Removes references to the deep-loaded Frame data and frees the associated memory. + /// + void Unload(); + + /// + /// Pulls the non-zero scan indices out of a Frame's deep data. This has to be done after a frame is loaded. + /// + /// A vector of indices (0 based) that contain samples. + std::vector GetNonZeroScanIndices(); + + /// + /// Return total intensity in a frame. + /// + /// A sum of all samples in a frame. + int64_t GetFrameTotalIntensity(); + + /// + /// Return total intensity in a Scan. + /// + /// 0-based scan index of the frame. + /// The sum of the intensities in a Scan. + size_t GetScanTotalIntensity(size_t scanIndex); + + /// + /// Return the number of samples per scan (dense/acquisition, not sparse/nonzero). + /// + /// The number of samples per scan during acquisition. + int GetMaxPointsInScan(); + + /// + /// The Frame's calibration. + /// + /// + MBISDK::TofCalibration GetCalibration(); + + /// + /// Indicate whether there is fragmentation data available in the Frame. + /// This method will be removed in a future version of MBI SDK + /// + /// true or falsed based on the frm-frag-op-mode value being "FragHiLo" and the energy value not being equal to 0.0 + [[deprecated]] + bool isFragmentationData(); + + /// + /// Get the Frame's offset Time in seconds. + /// + /// The start time in seconds of a given frame relative the beginning of the experiment. + double Time(); + + /// + /// return the number of scans in the frame. + /// + /// + size_t GetNumScans(); + + /// + /// Retrieve the frame's data as a coordinate object array. + /// + /// An int32 COOArray containing the frame intensities and indices. + COOArray GetFrameDataAsCOOArray(); + + /// + /// Retrieve the frame's data as a compressed sparse row array. + /// + /// An int32 CSRArray containing the frame intensities and indices. + CSRArray GetFrameDataAsCSRArray(); + + /// + /// Retrieve the frame data into pointers to vectors as the data, indices, indptr arrays of a CSR array + /// + /// 0-based index of the Scan within the Frame. + /// A pointer to the vector of intensities to be populated. + /// A pointer to the vector of indices to be populated. + /// A pointer to the indptr ("index pointer") vector to be populated. + /// true on success, false on failure + bool GetFrameDataAsCSRComponents(std::vector* data, std::vector* indices, std::vector* indptr); + + /// + /// Retrieve a Scan's mZ-based intensity as a pair of sparse vectors. + /// + /// 0-based index of the Scan within the Frame. + /// A pointer to the vector of mZ values to be populated. + /// A pointer to the vector of intensities to be populated. + /// true on success, false on error (bad calibration data) + bool GetScanDataMzIndexedSparse(size_t scanIndex, std::vector* mzIndex, std::vector* intensity); + + /// + /// Retrieve a Scan's mZ-based intensity as a dense vector with all sample points represented, including zeroes, with the accompanying mZ indexes. + /// + /// 0-based index of the Scan within the Frame. + /// A pointer to the vector of mZ values to be populated. + /// A pointer to the vector of intensities to be populated. + /// true on success, false on error (bad calibration data) + bool GetScanDataMzIndexedDense(size_t scanIndex, std::vector* mzIndex, std::vector* intensity); + + /// + /// Retrieve a mass spectrum from the frame. + /// + /// 0-based index of the scan within the frame + /// Set to true if zeroes are to be added (for plotting convenience) to the beginning and end of nonzero intervals. + /// An instance of the MassSpectrum structure, containing the points and indices of interest. + MassSpectrum GetMassSpectrum(size_t scanIndex, bool zeroPaddedReturns); + + /// + /// Retrieve a mass spectrum from the frame. + /// + /// 0-based index of the scan within the frame + /// An instance of the MassSpectrum structure, containing the points and indices of interest. + MassSpectrum GetMassSpectrum(size_t scanIndex); + + /// + /// Retrieve a scan (row) from the frame. + /// + /// 0-based index of the scan within the frame + /// A pair of vectors, for indices and intensities. + std::pair, std::vector> GetScan(size_t scanIndex); + + /// + /// Retrieve a scan (row) from the frame. + /// + /// 0-based index of the scan within the frame + /// Set to true if zeroes are to be added (for plotting convenience) to the beginning and end of nonzero intervals. + /// A pair of vectors, for indices and intensities. + std::pair, std::vector> GetScan(size_t scanIndex, bool zeroPaddedReturns); + + /// + /// Retrieve a Scan's ToF-based intensity as a sparse vector w/ accompanying sparse ToF indexes.. + /// + /// 0-based index of the Scan within the Frame. + /// A pointer to the vector of ToF index values to be populated. + /// A pointer to the vector of intensities to be populated. + void GetScanDataToFIndexedSparse(size_t scanIndex, std::vector* tofIndex, std::vector* intensity); + + /// + /// Retrieve a Scan's ToF-based intensity as a dense vector. + /// + /// 0-based index of the Scan within the Frame. + /// THe dense vector of intensity samples. + void GetScanDataToFIndexedDense(size_t scanIndex, std::vector* intensity); + + /// + /// Alternative way to get metadata item. + /// + /// + /// + std::string getFrameMetaDataItem(std::string key); + + /// + /// Build a summed scan from all scans in the frame, and provide the minimum and maximum ToF + /// indices and the scans that contained them. + /// + /// The vector (dense) of sample intensities summed across all scans in the frame. + /// The minimum ToF index found. + /// The scan index containing the minimum tof index. + /// The maximum ToF index found. + /// The scan index containing the maximum ToF index. + void GetScanSummationToFIndexedDense(std::vector* intensityOut, + int64_t * minToFIndex, + size_t * minToFScanIndex, + int64_t * maxToFIndex, + size_t * maxToFScanIndex); + + /// + /// Return the width of an arrival bin (scan/drift/AT). + /// + /// The bin width in milliseconds. + double GetArrivalBinWidth(); + + /// + /// Give the arrival bin time offset for a given index. + /// + /// The 0-based bin index. + /// The time offset, in milliseconds, of the start of the bin. -1.0 if an invalid bin index is passed in. + double GetArrivalBinTimeOffset(size_t binIndex); + + /// + /// Return the arrival bin index for the given time offset, or -1 if it's an invalid offset. + /// + /// Time offset into the frame, in milliseconds. + /// The bin which contains the time offset, or -1 if it's an invalid time offset. + int GetArrivalBinIndex(double timeOffset); + + /// + /// Return the lowest offset length statistics from all frames in the data file. + /// + /// The lowest value computed from all frames. + int64_t GetToFOffset(); + + /// + /// Return the length of offset length statistics from all frames in the data file. + /// + /// The length of all value computed from all frames. + int64_t GetToFLength(); + + /// + /// Return the lowest arrival bin offset statistics from all frames in the data file. + /// + /// The lowest value computed from all frames. + size_t GetArrivalBinOffset(); + + /// + /// Return the length of arrival bin offset statistics from all frames in the data file. + /// + /// The length of all value computed from all frames. + size_t GetArrivalBinLength(); + + + /// + /// A single gathering point for all metadata reading + /// + /// A string of metadata + std::string GetGenericMetaData(std::string strKey); + + /// + /// A single gathering point for all metadata reading + /// + /// An int of metadata + int GetGenericMetaDataInt(std::string strKey); + + /// + /// A single gathering point for all metadata reading + /// + /// A double of metadata + double GetGenericMetaDataDouble(std::string strKey); + + /// + /// Return list of ATTic values for this frame + /// + /// None. + std::shared_ptr> GetATTicList(); + + + /// + /// Return a specific value to a specific index of ATTic list for a given frame + /// + /// None. + int64_t GetATTicItem(int nIndex); + + /// + /// This method will be removed in a future version of MBI SDK. + /// Return list of DATA_COUNT values for this frame + /// + /// None. + [[deprecated("Use GetFrameDataAsCSRArray() or GetFrameDataAsCOOArray() to access frame data.")]] + std::shared_ptr> GetSampleIntensities(); + + + /// + /// Return a specific value to a specific index of DATA_COUNT list for a given frame + /// This method will be removed in a future version of MBI SDK. + /// + /// None. + [[deprecated("Use GetFrameDataAsCSRArray() or GetFrameDataAsCOOArray() to access frame data.")]] + int32_t GetDataCountItem(int nIndex); + + /// + /// Return list of DATA_POSITIONS values for this frame + /// This method will be removed in a future version of MBI SDK. + /// + /// None. + [[deprecated("Use GetFrameDataAsCSRArray() or GetFrameDataAsCOOArray() to access frame data.")]] + std::shared_ptr>> GetGates(); + + + /// + /// Return a specific value to a specific index of DATA_POSITIONS first list for a given frame + /// This method will be removed in a future version of MBI SDK. + /// + /// None. + [[deprecated("Use GetFrameDataAsCSRArray() or GetFrameDataAsCOOArray() to access frame data.")]] + int64_t GetDataPositionItemFirst(int nIndex); + + + /// + /// Return a specific value to a specific index of DATA_POSITIONS second list for a given frame + /// This method will be removed in a future version of MBI SDK. + /// + /// None. + [[deprecated("Use GetFrameDataAsCSRArray() or GetFrameDataAsCOOArray() to access frame data.")]] + int64_t GetDataPositionItemSecond(int nIndex); + + /// + /// Return list of INDEX_COUNT values for this frame + /// This method will be removed in a future version of MBI SDK. + /// + /// None. + [[deprecated("Use GetFrameDataAsCSRArray() or GetFrameDataAsCOOArray() to access frame data.")]] + std::shared_ptr> GetSampleOffsets(); + + + /// + /// Return a specific value to a specific index of INDEX_COUNT list for a given frame + /// This method will be removed in a future version of MBI SDK. + /// + /// None. + [[deprecated("Use GetFrameDataAsCSRArray() or GetFrameDataAsCOOArray() to access frame data.")]] + int64_t GetIndexCountItem(int nIndex); + + /// + /// Return list of INDEX_POSITION values for this frame + /// This method will be removed in a future version of MBI SDK. + /// + /// None. + [[deprecated("Use GetFrameDataAsCSRArray() or GetFrameDataAsCOOArray() to access frame data.")]] + std::shared_ptr> GetGateIndexOffsets(); + + + /// + /// Return a specific value to a specific index of INDEX_POSITION list for a given frame + /// This method will be removed in a future version of MBI SDK. + /// + /// None. + [[deprecated("Use GetFrameDataAsCSRArray() or GetFrameDataAsCOOArray() to access frame data.")]] + int64_t GetIndexPositionItem(int nIndex); + + /// + /// Return list of trigger timestamps for this frame + /// + /// None. + std::shared_ptr> GetTriggerTimeStamps(); + + + /// + /// Return a specific value to a specific index of trigger timestamp list for a given frame + /// + /// Trigger Timestamp Item + double GetTriggerTimeStampItem(int nIndex); + + /// + /// retrieves local copy of frame metadata + /// + /// pointer to frame metadata. + std::shared_ptr GetFrameMetaData(); + + /// @brief Examines frame metadata to determine collision energy for specific frame + /// @returns Collision energy value + /// @throws std::runtime_error if collision energy is not present + double GetCollisionEnergy(); + + /// + /// Examines frame metadata to determine collision energy for specific frame is valid + /// + /// true if collision energy is not blank + bool IsCollisionEnergyValid(); + + /// @brief Does the frame have a fixed collision energy? + /// @returns True if fixed, false if ramped or otherwise varied. + bool HasFixedCE(); + + /// @brief A non-throwing (safe) call for collision energy + /// @returns Collision energy value + double GetCE(int64_t scanIndex); + + /// @brief Return the details during an error to assist developers + std::string GetErrorOutput(); + + private: + std::map map_frame_all; + bool is_loaded; + double frame_dt_period; + std::shared_ptr frame_metadata_ptr; + // sampleintensities is a sparse collection of all the samples in a frame + std::shared_ptr> sample_intensities; + // This vector represents the non-zero scan samples with their + // respective ToF indexes offset into the sample_intensities + // int scanindex, pair tofIndexes> + std::shared_ptr>>>>> scan_table; + + // Returning to using the implementation-specific memory structures. A little + // harder to follow the code, but this will make things faster. + std::shared_ptr>> gates; + std::shared_ptr> sample_offsets; + std::shared_ptr> gate_index_offsets; + std::vector non_zero_scan_indices; + std::shared_ptr> attic_list; + std::shared_ptr> trigger_time_stamps_list; + + size_t numGatesInScan(size_t scanIndex); + size_t numSamplesInScan(size_t scanIndex); + + void processOffsetLengthStats(); + bool processedOffsetLengthStats{ false }; + + int64_t tof_offset; + int64_t tof_length; + size_t arrival_bin_offset; + size_t arrival_bin_length; + + int hasValidCollisionEnergy; + double fixedCE; + + MBISDK::MBIFile* pMbiFile; + + public: + /// @brief Collision energy parsing error + static constexpr const char* FRAME_CE_PARSE_ERROR_HEADER = "Cannot process collision energy for frame "; + /// @brief Collision energy invalid data error + static constexpr const char* FRAME_CE_PARSE_INVALID_DATA = ". The frame metadata field frm-collision-energy is not a valid JSON structure in this file: "; + /// @brief Collision energy frag data error + static constexpr const char* FRAME_CE_PARSE_BAD_FRAG_DATA = ". The frame metadata field frm-frag-energy is not valid in this file: "; + /// @brief Collision energy invalid data error + static constexpr const char* FRAME_CE_PARSE_MISSING_DATA = ". The frame metadata field frm-collision-energy is blank or missing in this file: "; + + }; +} +} diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIMerge.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIMerge.h new file mode 100644 index 0000000000..4787261196 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIMerge.h @@ -0,0 +1,109 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2022 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: Curt Lindmark * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + /// MBIMerge.h file +/// Header file for MBIMerge classes +/// MBIMerge contains functions trim an existing MBI file and save changes to +/// a new file. + +// MBIMerge.h - The top-level API object interface to MBI trim operations. +#pragma once +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif + +#include "MBIFile.h" +#include "MBIMetadata.h" +#include "MBICalibration.h" +#include "MBIFrame.h" + +#include +#include + + +namespace MBISDK +{ + /*! @class MBISDK::MBIMerge + * @brief Top level object for interfacing to the MBI data API. + * @author Curt Lindmark */ +extern "C" +{ + class MBIMerge + { + std::map MBIMerge_error_codes = + { + { ERR_UNEXPECTED, "Unexpected error."}, + { ERR_SUCCESS, "Success"}, + { ERR_FILE_NOT_FOUND, "File not found."}, + { ERR_HDF5_FILE_ERROR, "HDF5 file error."}, + { ERR_METADATA_NOT_LOADED, "Metadata not loaded yet."}, + { ERR_BAD_FRAME_INDEX, "Frame index out of bounds."}, + { ERR_BAD_SCAN_INDEX, "Scan index out of bounds."}, + { ERR_ITEM_MISSING, "Item missing."}, + { ERR_OPERATION_NOT_SUPPORTED, "Operation is not supported."}, + { ERR_INVALID_FRAME_SELECTION, "The frames selected are not valid."}, + { ERR_INPUT_FILE_LOW_PATH_FAIL, "The low energy input path was not found."}, + { ERR_INPUT_FILE_HIGH_PATH_FAIL, "The high energy input path was not found."}, + { ERR_OUTPUT_FILE_PATH_FAIL, "The output path was not found."}, + { ERR_INPUT_FILE_LOW_OPEN_FAIL, "The low energy input file could not be opened"}, + { ERR_INPUT_FILE_HIGH_OPEN_FAIL, "The high energy input file could not be opened"}, + { ERR_OUTPUT_FILE_CREATE_FAIL, "The output file could not be created"}, + {ERR_INPUT_LOW_FRAME_COUNT_MUST_MATCH_INPUT_HIGH_FRAME_COUNT, "The frame count of the low energy and high energy file must match."}, + }; + + public: + static const int ERR_UNEXPECTED = -1; + static const int ERR_SUCCESS = 0; + static const int ERR_FILE_NOT_FOUND = 1; + static const int ERR_HDF5_FILE_ERROR = 2; + static const int ERR_FILE_NOT_INITIALIZED = 3; + static const int ERR_INVALID_FRAME_SELECTION = 4; + static const int ERR_INPUT_FILE_LOW_PATH_FAIL = 5; + static const int ERR_INPUT_FILE_HIGH_PATH_FAIL = 6; + static const int ERR_OUTPUT_FILE_PATH_FAIL = 7; + static const int ERR_INPUT_FILE_LOW_OPEN_FAIL = 8; + static const int ERR_INPUT_FILE_HIGH_OPEN_FAIL = 9; + static const int ERR_OUTPUT_FILE_CREATE_FAIL = 10; + static const int ERR_INPUT_LOW_FRAME_COUNT_MUST_MATCH_INPUT_HIGH_FRAME_COUNT = 11; + + static const int ERR_METADATA_NOT_LOADED = 101; + + static const int ERR_BAD_FRAME_INDEX = 201; + static const int ERR_BAD_SCAN_INDEX = 202; + + static const int ERR_ITEM_MISSING = 301; + + static const int ERR_OPERATION_NOT_SUPPORTED = 401; + + /// + /// Construct an MBIMerge for read from a file. + /// + MBI_DLLCPP MBIMerge(); + + private: + void setErrorCode(int local_error_code); + + MBIFile* first_input_file_ptr; + MBIFile* second_input_file_ptr; + MBIFile* output_file_ptr; + int error_code; + std::string first_input_file_path_str; + std::string second_input_file_path_str; + std::string output_file_path_str; + std::string oob_error_message; + int frame_count; + double optional_low_energy; + double optional_high_energy; + }; +} +} diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIMetadata.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIMetadata.h new file mode 100644 index 0000000000..e9619cc3ea --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIMetadata.h @@ -0,0 +1,129 @@ +// MBIMetadata.h - Wrap global and frame metadata attributes. +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2021 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: Greg Van Aken * + * 0.0.0.0 + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#pragma once +#ifndef MBI_DLLCPP + #ifdef SWIG_WIN + #define MBI_DLLCPP + #else + #ifdef MBI_EXPORTS + #define MBI_DLLCPP __declspec(dllexport) + #else + #define MBI_DLLCPP __declspec(dllimport) + #endif + #endif +#endif + +#include +#include + +namespace MBISDK +{ + /*! @class MBISDK::Metadata + * @brief Abstract interface for metadata from an MBI file. + * @author Greg Van Aken + */ + class MBI_DLLCPP Metadata + { + public: + /// @brief Initialize an empty metadata object. + Metadata(); + + /// @brief Initialize metadata from a group. + //Metadata(H5::Group group); + Metadata(std::map& map); + + /// @brief Initialize metadata from another metadata object. + void Copy(Metadata& toCopy); + + /// @brief Determine whether the metadata table has a value at the requested key. + /// @param key a const char* key to lookup. + bool HasKey(const char* key); + + /// @brief Read value at the requested key as a const char* (string). + /// @param key a const char* key to lookup. + const char* ReadString(const char* key); + + /// @brief Read a value at the requested key as a const char*. + /// @param key a std::string key to lookup. + const char* ReadString(std::string key); + + /// @brief Read value at the requested key as an integer. + /// @param key a const char* key to lookup. + const int ReadInt(const char* key); + + /// @brief Read value at the requested key as a double float. + /// @param key a const char* key to lookup. + const double ReadDouble(const char* key); + + /// @brief Read all metadata key, value pairs into cache. + //void LoadAll(); + + /// @brief Read all metadata from the file + const std::map& ReadAll(); + + /// @brief Retrieve all cached metadata. + const std::map& GetCache(); + + /// @brief Overwrite all cached metadata. + void SetCache(const std::map& toCopy); + + /// @brief Close file metadata + void Close(); + + ~Metadata(); + + private: + //H5::Group group; + std::map cache; + + }; + + /// @class MBISDK::GlobalMetadata + /// @brief Metadata global to the MBI file. + /// @author Greg Van Aken + class MBI_DLLCPP GlobalMetadata : public Metadata + { + using Metadata::Metadata; + }; + + /// @class MBISDK::FrameMetadata + /// @brief Metadata specific to a single frame. + /// @author Greg Van Aken + class MBI_DLLCPP FrameMetadata : public Metadata + { + using Metadata::Metadata; + }; + + /// @class MBISDK::FragmentationMetadata + /// @brief Metadata pertinent to fragmentation + /// @author Greg Van Aken + class MBI_DLLCPP FragmentationMetadata + { + public: + /// @brief The types of fragmentation data for a single frame + /// @author Greg Van Aken + + /// @brief Fragmentation Type enumeration. + enum class eType + { + /// @brief Fragmentation Metadata type None. + NONE, ///< No fragmentation data present + /// @brief Fragmentation Metadata type HILO. + HILO ///< Alternating high CE, low CE frames + }; + + /// @brief Initialize a fragmentation metadata object. + FragmentationMetadata(); + + /// @brief Type instance + eType type; ///< Type of fragmentation used to collect the data. + /// @brief frag energy + double frag_energy; ///< Fragmentation energy of the frame. + }; +} diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIStitch.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIStitch.h new file mode 100644 index 0000000000..5e1551a2f5 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIStitch.h @@ -0,0 +1,32 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2022 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: Curt Lindmark * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + /// MBIStitch.h file +/// Header file for MBIStitch classes +/// MBIStitch contains functions trim an existing MBI file and save changes to +/// a new file. + +// MBIStitch.h - The top-level API object interface to MBI trim operations. +#pragma once +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif + +#include "MBIFile.h" +#include "MBIMetadata.h" +#include "MBICalibration.h" +#include "MBIFrame.h" + +#include +#include + diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBITrim.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBITrim.h new file mode 100644 index 0000000000..4f1cc09e63 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBITrim.h @@ -0,0 +1,24 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * MBI Data Access API * + * Copyright 2022 MOBILion Systems, Inc. ALL RIGHTS RESERVED * + * Author: Curt Lindmark * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + /// MBITrim.h file +/// Header file for MBITrim classes +/// MBITrim contains functions trim an existing MBI file and save changes to +/// a new file. + +// MBITrim.h - The top-level API object interface to MBI trim operations. +#pragma once +#pragma warning(disable : 4996) +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIWrapper.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIWrapper.h new file mode 100644 index 0000000000..81b73e23d5 --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/MBIWrapper.h @@ -0,0 +1,790 @@ +// MBIWrapper.h - The top-level API object interface to MBI data. +// Curt Lindmark 2/2/2024 +// 0.0.0.0 +#pragma once +#pragma warning(disable : 4996) +#ifndef MBI_DLLCPP +#ifdef SWIG_WIN +#define MBI_DLLCPP +#else +#ifdef MBI_EXPORTS +#define MBI_DLLCPP __declspec(dllexport) +#else +#define MBI_DLLCPP __declspec(dllimport) +#endif +#endif +#endif + +#include "MBIFile.h" +#include "MBIFrame.h" +#include "MBICalibration.h" +#include "MBIBinning.h" +#include "MBIStitch.h" +#include "MBITrim.h" +#include "MBIMetadata.h" +#include "MBIMerge.h" + +using namespace MBISDK; + +extern "C" +{ + static std::string global_error_message; + /// + /// Return the error message for the caller to ask "what went wrong" when something goes wrong. + /// + /// none + MBI_DLLCPP void GetWrapperErrorMessage(char* str, int strlen); + + // begining of MBIFile functions + /// + /// Construct an MBIFile for read from a file. + /// + MBI_DLLCPP MBISDK::MBIFile* Make_MBIFile(); + + /// + /// Construct an MBIFile for read from a file. + /// + MBI_DLLCPP MBISDK::MBIFile* Make_MBIFile_Path(const char* path); + + /// + /// Free an MBIFile memory space. + /// + /// Path to the file to load. + MBI_DLLCPP void Free_MBIFile(MBISDK::MBIFile* ptr); + + /// + /// Construct an MBIFile for read from a file. + /// + /// Path to the file to load. + MBI_DLLCPP bool Init_MBIFile(MBISDK::MBIFile* ptr); + + /// + /// Construct an MBIFile for read from a file. + /// + /// Path to the file to load. + MBI_DLLCPP void Close_MBIFile(MBISDK::MBIFile* ptr); + + /// + /// Return the number of frames for the given file + /// + /// Path to the file to load. + MBI_DLLCPP int Get_NumFrames(MBISDK::MBIFile* ptr); + + /// + /// Retrieve the number of scans per Frame for the experiment. + /// + /// The frame index. + /// The number of scans in the Frame., or 0 if nFrame is invalid + MBI_DLLCPP int GetMaxScansInFrame(MBISDK::MBIFile* ptr, int nFrame); + + /// + /// The MBIFile's calibration. + /// + /// + MBI_DLLCPP MBISDK::TofCalibration* GetTofCalibration(MBISDK::MBIFile* ptr); + + /// + /// The MBIFile's calibration. + /// + /// + MBI_DLLCPP MBISDK::EyeOnCcsCalibration* GetEyeOnCCSCalibration(MBISDK::MBIFile* ptr); + + /// + /// The MBIFile's calibration. + /// + /// + MBI_DLLCPP void GetCCSCalibration(MBISDK::MBIFile* ptr, char* str, int strlen, char* str_coefficients, int strlen_coefficients); + + /// + /// Load all Frames and return a shared pointer to the collection. + /// + /// A shared pointer to a vector of Frame pointers, all loaded, or a NULL ptr is frameIndex is invalid + MBI_DLLCPP bool GetFrames(MBISDK::MBIFile* ptr); + + /// + /// Load a Frame (if unloaded) and return a shared_ptr to it. + /// + /// 1-based index into the Frame collection. + /// shared_ptr to a loaded Frame w/ the specified index. + MBI_DLLCPP MBISDK::Frame* GetFrame(MBISDK::MBIFile* ptr, int frameIndex); + + /// + /// Return the metadata pointer for a given Frame. + /// + /// 1-based index into the frame collection. + /// shared_ptr to the frame's metadata object, or NULL if frameIndex is invalid. + MBI_DLLCPP void GetFrameMetadata(MBISDK::MBIFile* ptr, int frameIndex, char* str, int strlen); + + /// + /// Deep-load a frame's data. If you only want to deepload frames based on metadata criteria, you may use this to avoid calling GetFrames() to load them all. + /// + /// The 1-based index into the frame collection. + MBI_DLLCPP void LoadFrameData(MBISDK::MBIFile* ptr, int frameIndex); + + /// + /// Unload the data sets and memory associated w/ the Frame, and set it to unloaded state. + /// + MBI_DLLCPP void UnloadFrame(MBISDK::MBIFile* ptr, int frameIndex); + + /// + /// Return the max scan samples for the experiment. + /// + /// The max number of samples in a scan in the file. + MBI_DLLCPP int GetMaxPointsInScan(MBISDK::MBIFile* ptr); + + /// + /// Retrieve the name of the input file (full path). + /// + /// std::string the path used to open the file. + MBI_DLLCPP void GetFilename(MBISDK::MBIFile* ptr, char* str, int strlen); + + /// + /// Set the name of the input file (full path). + /// + /// Path to the file to load. + /// None + MBI_DLLCPP void SetFilename(MBISDK::MBIFile* ptr, const char* path); + + /// + /// Retrieve the RT-TIC for a frame. + /// + /// A pair, first being time since beginning of experiment, the second being the TIC, or empty std::pair if frameIndex is invalid.. + MBI_DLLCPP int64_t GetRtTicValue(MBISDK::MBIFile* ptr, int frameIndex); + + /// + /// Evaluate the frame index being passed in to ensure it is valid + /// + /// A bool checking it is safe to use as an array index + MBI_DLLCPP bool CheckFrameIndex(MBISDK::MBIFile* ptr, int nFrameIndex); + + /// + /// load the arrival time intensity counts for arrival bins in a frame. + /// + /// The 1-based index of the frame desired. + /// A vector of arrival time intensity counts per bin in the frame. + MBI_DLLCPP void loadAtTic(MBISDK::MBIFile* ptr, int frameIndex, char* str, int strlen); + + /// + /// Return the error code from the last method called. + /// + /// The error code from the last method call. + MBI_DLLCPP int GetErrorCode(MBISDK::MBIFile* ptr); + + /// + /// Return an error message from the last method call. + /// + /// The error message from the last method call. + MBI_DLLCPP void File_GetErrorMessage(MBISDK::MBIFile* ptr, char* str, int strlen); + + /// + /// Retrieve the sample rate from global meta data + /// + /// A double, the sample rate from the file's global metadata + MBI_DLLCPP double GetSampleRate(MBISDK::MBIFile* ptr); + + /// + /// Return the version string of the library. + /// + /// + MBI_DLLCPP void File_GetVersion(MBISDK::MBIFile* ptr, char* str, int strlen); + + /// + /// Return a copy of the global metadata for the file. + /// + /// + MBI_DLLCPP void GetGlobalMetaData(MBISDK::MBIFile* ptr, char* str, int strlen); + + /// + /// Return the member variable value representing the initialzation status of the class. + /// Many functions of the class rely on data being properly initialized. By checking this status first, the library + /// can be protected against attempted usage in an uninitialized condition + /// + /// The value stored in the member variable, true or false. + MBI_DLLCPP bool IsInitialized(MBISDK::MBIFile* ptr); + + /// + /// Return the lowest offset length statistics from all frames in the data file. + /// + /// The lowest value computed from all frames. + MBI_DLLCPP int64_t GetToFOffset(MBISDK::MBIFile* ptr); + + /// + /// Return the length of offset length statistics from all frames in the data file. + /// + /// The length of all value computed from all frames. + MBI_DLLCPP int64_t GetToFLength(MBISDK::MBIFile* ptr); + + /// + /// Return the lowest arrival bin offset statistics from all frames in the data file. + /// + /// The lowest value computed from all frames. + MBI_DLLCPP size_t GetArrivalBinOffset(MBISDK::MBIFile* ptr); + + /// + /// Return the length of arrival bin offset statistics from all frames in the data file. + /// + /// The length of all value computed from all frames. + MBI_DLLCPP size_t GetArrivalBinLength(MBISDK::MBIFile* ptr); + + + /// + /// Return specific RT_TIC data from a specific frame + /// + /// None + MBI_DLLCPP int64_t GetSpecificRtTicValue(MBISDK::MBIFile* ptr, int nFrameIndex); + + /// + /// List of RT Tic values + /// + /// List of int64_t values + MBI_DLLCPP void GetRtTicList(MBISDK::MBIFile* ptr, char* str, int strlen); + + /// + /// Retrieves single metadata item + /// + /// String of metadata item + MBI_DLLCPP void getMetaDataItem(MBISDK::MBIFile* ptr, char* key, char* str, int strlen); + + /// + /// Retrieves single metadata item + /// + /// Int of metadata item + MBI_DLLCPP int getMetaDataItemInt(MBISDK::MBIFile* ptr, char* key); + + /// + /// Retrieves single metadata item + /// + /// Double of metadata item + MBI_DLLCPP double getMetaDataItemDouble(MBISDK::MBIFile* ptr, char* key); + + /// + /// Set the external error message + /// + MBI_DLLCPP void File_setExternalErrorMessage(MBISDK::MBIFile* ptr, std::string input_str); + + /// + /// Retrieves external error message + /// + /// external error message + MBI_DLLCPP void File_getExternalErrorMessage(MBISDK::MBIFile* ptr, char* str, int strlen); + + // end of MBIFile functions + + // beginning of MBIFrame functions + /// + /// Default constructor for creating a Frame independently of a file to load it from. + /// This should generally NOT be called by API users looking to use Frame/Scan data, use MBIFile::GetFrame() instead. + /// + MBI_DLLCPP MBISDK::Frame* Make_MBIFrame(); + + /// + /// Indicates whether a Frame's deep-data load has been completed. If not, it's usually + /// safest to do a MBIFile::GetFrame(frameIndex). + /// + /// true if loaded, false if not loaded. + MBI_DLLCPP bool IsLoaded(MBISDK::Frame* ptr); + + /// + /// Load the abstract sparse cube data to the Frame for later use. + /// This should generally NOT be called by API users looking to use Frame/Scan data, use MBIFile::GetFrame() instead. + /// + /// An implementation-specific approach to storing sparse intinsity data. + /// An implementation-specific approach to storing sparse intinsity data. + /// An implementation-specific approach to storing sparse intinsity data. + /// An implementation-specific approach to storing sparse intinsity data. + /// The list of AT_TIC for each frame. + /// The list of trigger timestamps for each scan. + MBI_DLLCPP void Load(MBISDK::Frame* ptr, char* str_data_count_list, char* str_data_position_list, char* str_index_count_list, char* str_index_position_list, char* str_attic_list, char* str_trigger_timestamp_list); + + /// + /// Removes references to the deep-loaded Frame data and frees the associated memory. + /// + MBI_DLLCPP void Unload(MBISDK::Frame* ptr); + + /// + /// Pulls the non-zero scan indices out of a Frame's deep data. This has to be done after a frame is loaded. + /// + /// A vector of indices (0 based) that contain samples. + MBI_DLLCPP void GetNonZeroScanIndices(MBISDK::Frame* ptr, char* str, int strlen); + + /// + /// Return total intensity in a frame. + /// + /// A sum of all samples in a frame. + MBI_DLLCPP int64_t GetFrameTotalIntensity(MBISDK::Frame* ptr); + + /// + /// Return total intensity in a Scan. + /// + /// 0-based scan index of the frame. + /// The sum of the intensities in a Scan. + MBI_DLLCPP size_t GetScanTotalIntensity(MBISDK::Frame* ptr, size_t scanIndex); + + /// + /// Return the number of samples per scan (dense/acquisition, not sparse/nonzero). + /// + /// The number of samples per scan during acquisition. + MBI_DLLCPP int Frame_GetMaxPointsInScan(MBISDK::Frame* ptr); + + /// + /// Indicate whether there is fragmentation data available in the Frame. + /// + /// true or falsed based on the frm-frag-op-mode value being "FragHiLo" and the energy value not being equal to 0.0 + MBI_DLLCPP bool isFragmentationData(MBISDK::Frame* ptr); + + /// + /// Get the Frame's offset Time in seconds. + /// + /// The start time in seconds of a given frame relative the beginning of the experiment. + MBI_DLLCPP double Time(MBISDK::Frame* ptr); + + /// + /// return the number of scans in the frame. + /// + /// + MBI_DLLCPP size_t GetNumScans(MBISDK::Frame* ptr); + + /// + /// Retrieve a Scan's mZ-based intensity as a pair of sparse vectors. + /// + /// 0-based index of the Scan within the Frame. + /// A pointer to the vector of mZ values to be populated. + /// A pointer to the vector of intensities to be populated. + /// true on success, false on error (bad calibration data) + MBI_DLLCPP bool GetScanDataMzIndexedSparse(MBISDK::Frame* ptr, size_t scanIndex, char* output_mz_list, int mz_len, char* output_intensity_list, int intensity_len); + + /// + /// Retrieve a Scan's mZ-based intensity as a dense vector with all sample points represented, including zeroes, with the accompanying mZ indexes. + /// + /// 0-based index of the Scan within the Frame. + /// A pointer to the vector of mZ values to be populated. + /// A pointer to the vector of intensities to be populated. + /// true on success, false on error (bad calibration data) + MBI_DLLCPP bool GetScanDataMzIndexedDense(MBISDK::Frame* ptr, size_t scanIndex, char* output_mz_list, int mz_len, char* output_intensity_list, int intensity_len); + + /// + /// Retrieve a Scan's ToF-based intensity as a sparse vector w/ accompanying sparse ToF indexes.. + /// + /// 0-based index of the Scan within the Frame. + /// A pointer to the vector of ToF index values to be populated. + /// A pointer to the vector of intensities to be populated. + MBI_DLLCPP void GetScanDataToFIndexedSparse(MBISDK::Frame* ptr, size_t scanIndex, char* output_tof_list, int tof_len, char* output_int_list, int int_len); + + /// + /// Retrieve a Scan's ToF-based intensity as a dense vector. + /// + /// 0-based index of the Scan within the Frame. + /// THe dense vector of intensity samples. + MBI_DLLCPP void GetScanDataToFIndexedDense(MBISDK::Frame* ptr, size_t scanIndex, char* output_int_list, int int_len); + + /// + /// Alternative way to get metadata item. + /// + /// + /// + MBI_DLLCPP void getFrameMetaDataItem(MBISDK::Frame* ptr, char* key, char* str, int strlen); + + /// + /// Build a summed scan from all scans in the frame, and provide the minimum and maximum ToF + /// indices and the scans that contained them. + /// + /// The vector (dense) of sample intensities summed across all scans in the frame. + /// The minimum ToF index found. + /// The scan index containing the minimum tof index. + /// The maximum ToF index found. + /// The scan index containing the maximum ToF index. + MBI_DLLCPP void GetScanSummationToFIndexedDense(MBISDK::Frame* ptr, + char* str_size_t_intensity, int str_size_t_intensity_len, + int64_t* minToFIndex, size_t* minToFScanIndex, + int64_t* maxToFIndex, size_t* maxToFScanIndex); + + /// + /// Return the width of an arrival bin (scan/drift/AT). + /// + /// The bin width in milliseconds. + MBI_DLLCPP double GetArrivalBinWidth(MBISDK::Frame* ptr); + + /// + /// Give the arrival bin time offset for a given index. + /// + /// The 0-based bin index. + /// The time offset, in milliseconds, of the start of the bin. -1.0 if an invalid bin index is passed in. + MBI_DLLCPP double GetArrivalBinTimeOffset(MBISDK::Frame* ptr, size_t binIndex); + + /// + /// Return the arrival bin index for the given time offset, or -1 if it's an invalid offset. + /// + /// Time offset into the frame, in milliseconds. + /// The bin which contains the time offset, or -1 if it's an invalid time offset. + MBI_DLLCPP int GetArrivalBinIndex(MBISDK::Frame* ptr, double timeOffset); + + + /// + /// A single gathering point for all metadata reading + /// + /// A string of metadata + MBI_DLLCPP void GetGenericMetaData(MBISDK::Frame* ptr, char* strKey, char* str, int strlen); + + /// + /// A single gathering point for all metadata reading + /// + /// An int of metadata + MBI_DLLCPP int GetGenericMetaDataInt(MBISDK::Frame* ptr, char* strKey); + + /// + /// A single gathering point for all metadata reading + /// + /// A double of metadata + MBI_DLLCPP double GetGenericMetaDataDouble(MBISDK::Frame* ptr, char* strKey); + + /// + /// Return list of ATTic values for this frame + /// + /// None. + MBI_DLLCPP void GetATTicList(MBISDK::Frame* ptr, char* str, int strlen); + + + + /// + /// Return a specific value to a specific index of ATTic list for a given frame + /// + /// None. + MBI_DLLCPP int64_t GetATTicItem(MBISDK::Frame* ptr, int nIndex); + + /// + /// Return list of DATA_COUNT values for this frame + /// + /// None. + MBI_DLLCPP void GetSampleIntensities(MBISDK::Frame* ptr, char* str, int strlen); + + + /// + /// Return a specific value to a specific index of DATA_COUNT list for a given frame + /// + /// None. + MBI_DLLCPP int32_t GetDataCountItem(MBISDK::Frame* ptr, int nIndex); + + /// + /// Return list of DATA_POSITIONS values for this frame + /// + /// None. + MBI_DLLCPP void GetGates(MBISDK::Frame* ptr, char* str, int strlen); + + + /// + /// Return a specific value to a specific index of DATA_POSITIONS first list for a given frame + /// + /// None. + MBI_DLLCPP int64_t GetDataPositionItemFirst(MBISDK::Frame* ptr, int nIndex); + + + /// + /// Return a specific value to a specific index of DATA_POSITIONS second list for a given frame + /// + /// None. + MBI_DLLCPP int64_t GetDataPositionItemSecond(MBISDK::Frame* ptr, int nIndex); + + /// + /// Return list of INDEX_COUNT values for this frame + /// + /// None. + MBI_DLLCPP void GetSampleOffsets(MBISDK::Frame* ptr, char* str, int strlen); + + + /// + /// Return a specific value to a specific index of INDEX_COUNT list for a given frame + /// + /// None. + MBI_DLLCPP int64_t GetIndexCountItem(MBISDK::Frame* ptr, int nIndex); + + /// + /// Return list of INDEX_POSITION values for this frame + /// + /// None. + MBI_DLLCPP void GetGateIndexOffsets(MBISDK::Frame* ptr, char* str, int strlen); + + + /// + /// Return a specific value to a specific index of INDEX_POSITION list for a given frame + /// + /// None. + MBI_DLLCPP int64_t GetIndexPositionItem(MBISDK::Frame* ptr, int nIndex); + + /// + /// Return a list of trigger time stamps for a given frame + /// + /// None. + MBI_DLLCPP void GetTriggerTimeStamps(MBISDK::Frame* ptr, char* str, int strlen); + + + /// + /// Return a specific value to a specific index of INDEX_POSITION list for a given frame + /// + /// Trigger Timestamp Item + MBI_DLLCPP double GetTriggerTimeStampItem(MBISDK::Frame* ptr, int nIndex); + + /// + /// retrieves local copy of frame metadata + /// + /// pointer to frame metadata. + MBI_DLLCPP void GetFrameMetaData(MBISDK::Frame* ptr, char* str, int strlen); + + /// + /// Examines frame metadata to determine collision energy for specific frame + /// + /// Collision energy value + MBI_DLLCPP double GetCollisionEnergy(MBISDK::Frame* ptr); + + /// + /// Examines frame metadata to determine collision energy for specific frame is valid + /// + /// true if collision energy is not blank + MBI_DLLCPP bool IsCollisionEnergyValid(MBISDK::Frame* ptr); + + /// @brief Return the details during an error to assist developers + MBI_DLLCPP void Frame_GetErrorOutput(MBISDK::Frame* ptr, char* str, int strlen); + + //end of Frame functions + + //beginning of Calibration functions + /// @brief Initialize a calibration object + MBI_DLLCPP MBISDK::TofCalibration* Make_TofCalibration(); + + /// @brief De-initialize a calibration object + MBI_DLLCPP void Free_TofCalibration(MBISDK::TofCalibration* ptr); + + /// @brief Compute mass error from polynomial residual fit. + MBI_DLLCPP double TofError(MBISDK::TofCalibration* ptr, double uSecTOF); + + /// @brief Convert tof bin index to time-of-flight + MBI_DLLCPP double IndexToMicroseconds(MBISDK::TofCalibration* ptr, int64_t index); + + /// @brief Convert time-of-flight to m/z using this calibration. + MBI_DLLCPP double MicrosecondsToMz(MBISDK::TofCalibration* ptr, double uSec); + + /// @brief Convert tof bin index to m/z using this calibration. + MBI_DLLCPP double IndexToMz(MBISDK::TofCalibration* ptr, int64_t index); + + /// @brief Convert time-of-flight to tof bin index. + MBI_DLLCPP size_t MicrosecondsToIndex(MBISDK::TofCalibration* ptr, double uSec); + + /// @brief Convert m/z to time-of-flight using this calibration. + MBI_DLLCPP double MzToMicroseconds(MBISDK::TofCalibration* ptr, double mz); + + /// @brief Convert m/z to tof bin index using this calibration. + MBI_DLLCPP size_t MzToIndex(MBISDK::TofCalibration* ptr, double mz); + + /// @brief Retrieve the slope of the calibration. + MBI_DLLCPP double Slope(MBISDK::TofCalibration* ptr); + + /// @brief Retrieve the intercept of the calibration. + MBI_DLLCPP double Intercept(MBISDK::TofCalibration* ptr); + + /// @brief return status of failure + MBI_DLLCPP int getFailureStatus(MBISDK::TofCalibration* ptr); + + /// @brief return status of StatusText + MBI_DLLCPP void TOF_getStatusText(MBISDK::TofCalibration* ptr, char* str, int strlen); + + /// @brief Initialize a calibration object from json representation. + MBI_DLLCPP MBISDK::CcsCalibration* Make_CcsCalibration(double sampleRate, const char* jsonString); + + /// @brief De-initialize a calibration object from json representation. + MBI_DLLCPP void Free_CcsCalibration(MBISDK::CcsCalibration* ptr); + + /// @brief Get the type of calibration + MBI_DLLCPP int CCS_Type(MBISDK::CcsCalibration* ptr); + + /// @brief Get the coefficients + MBI_DLLCPP void CCS_CAL_Coefficients(MBISDK::CcsCalibration* ptr, char* str, int strlen); + + /// @brief Initialize a GlobalCcsCalibration object from json representation. + MBI_DLLCPP MBISDK::GlobalCcsCalibration* Make_GlobalCcsCalibration(const char* jsonString); + + /// @brief De-initialize a GlobalCcsCalibration object from json representation. + MBI_DLLCPP void Free_GlobalCcsCalibration(MBISDK::GlobalCcsCalibration* ptr); + + /// @brief Get the type of calibration + MBI_DLLCPP int GlobalCcs_Type(MBISDK::GlobalCcsCalibration* ptr); + + /// @brief Initialize a t object from json representation. + MBI_DLLCPP EyeOnCcsCalibration* Make_EyeOnCcsCalibration(MBISDK::MBIFile* input_mbi_file_ptr); + + /// @brief De-initialize a EyeOnCcsCalibration object from json representation. + MBI_DLLCPP void Free_EyeOnCcsCalibration(MBISDK::EyeOnCcsCalibration* ptr); + + /// @brief Parse a JSON string for EyeOnCcsCalibration. + MBI_DLLCPP void ParseCCSCal(MBISDK::EyeOnCcsCalibration* ptr, std::string strCCSData); + + /// @brief Get CCS Minimum value + MBI_DLLCPP double GetCCSMinimum(MBISDK::EyeOnCcsCalibration* ptr); + + /// @brief Get CCS Maximum value + MBI_DLLCPP double GetCCSMaximum(MBISDK::EyeOnCcsCalibration* ptr); + + /// @brief Get Degree value + MBI_DLLCPP int GetDegree(MBISDK::EyeOnCcsCalibration* ptr); + + /// @brief Get AT Surfing value, measured in milliseconds + MBI_DLLCPP double GetAtSurf(MBISDK::EyeOnCcsCalibration* ptr); + + /// @brief Get CCS Coefficient for a given file + MBI_DLLCPP void GetEyeOnCCSCoefficients(MBISDK::EyeOnCcsCalibration* ptr, char* str, int strlen); + + /// @brief Get CCS Coefficient for a given file + MBI_DLLCPP void SetEyeOnCCSCoefficients(MBISDK::EyeOnCcsCalibration* ptr, char* str); + + /// @brief Calculate CCS value for given AT + MBI_DLLCPP double ArrivalTimeToCCS(MBISDK::EyeOnCcsCalibration* ptr, double scan_arrival_time, double ion_mass); + + /// @brief Calculate CCS value for set of AT + MBI_DLLCPP void ArrivalTimeToCCS2(MBISDK::EyeOnCcsCalibration* ptr, char* input_str, char* output_str, int strlen); + + /// @brief Calculate CCS value for the AT of a single frame of intensities + MBI_DLLCPP void ArrivalTimeToCCS3(MBISDK::EyeOnCcsCalibration* ptr, int frame_index, char* str, int strlen); + + /// @brief Return the details during an error to assist developers + MBI_DLLCPP void EyeOnCcs_GetErrorOutput(MBISDK::EyeOnCcsCalibration* ptr, char* str, int strlen); + + /// @brief Return the value of the gas mass for the experiment + MBI_DLLCPP double ComputeGasMass(MBISDK::EyeOnCcsCalibration* ptr, std::string gas_string); + + /// @brief Calculate adjusted CCS value based on arrival time + MBI_DLLCPP double AdjustCCSValue(MBISDK::EyeOnCcsCalibration* ptr, double unadjusted_ccs, double dbArrivalTime, double Mz_ion); + + /// @brief Return the value of the gas mass for the experiment + MBI_DLLCPP double ComputeGasMassDefault(MBISDK::EyeOnCcsCalibration* ptr); + + /// @brief Generate a message to throw an error + MBI_DLLCPP void EyeOn_CCS_GenerateThrownErrorMessage(MBISDK::EyeOnCcsCalibration* ptr, int error_type, char* str, int strlen, int frame_index = 0, double arrival_time = 0.0); + + /// @brief evaluate pointer to ensure it is not null + MBI_DLLCPP bool IsValid(MBISDK::EyeOnCcsCalibration* calptr, void* ptr); + + /// @brief override the gas mass value + MBI_DLLCPP void SetGasMass(MBISDK::EyeOnCcsCalibration* ptr, double gas_mass_input); + //end of Calibration functions + +} + +//local utility functions +/// +/// Convert shared pointer to a vector of int64s to a JSON structure for interop. +/// +/// JSON string +std::string MarshalIntoJSON(std::string strType, std::shared_ptr > input_list); + +/// +/// Convert shared pointer to a vector of int32s to a JSON structure for interop. +/// +/// JSON string +std::string MarshalIntoJSON32(std::string strType, std::shared_ptr > input_list); + +/// +/// Convert shared pointer to a vector of pairs of int32s to a JSON structure for interop. +/// +/// JSON string +std::string MarshalIntoJSONPairs(std::string strType, std::shared_ptr>> input_list); + +/// +/// Convert shared pointer to a vector of pairs of doubles and int32s to a JSON structure for interop. +/// +/// JSON string +std::string MarshalIntoJSONDoubleInt(std::string strType, std::shared_ptr>> input_list); + +/// +/// Convert shared pointer to a metadata map to a JSON structure for interop. +/// +/// JSON string +std::string MarshalIntoJSONMetaData(std::string strType, std::shared_ptr input_list); + +/// +/// Convert vector of doubles to a JSON structure for interop. +/// +/// JSON string +std::string MarshalIntoJsonDoubleVector(std::string strType, std::vector input_list); + +/// +/// Convert vector of size_t to a JSON structure for interop. +/// +/// JSON string +std::string MarshalIntoJSONSizeT(std::string strType, std::vector input_list); + +/// +/// Convert vector of size_t to a JSON structure for interop. +/// +/// JSON string +std::vector MarshalIntoJSONStringVector(std::string input_list); + +/// +/// Convert vector of tuples to a JSON structure for interop. +/// +/// JSON string +std::string MarshalIntoJsonTupleIntDoubleDouble(std::string strType, std::vector> input_list); + +/// +/// Convert vector of long long to a JSON structure for interop. +/// +/// JSON string +std::string MarshalIntoJSONLongLong(std::string strType, std::vector input_list); + +/// +/// Convert vector of doubles to a JSON structure for interop. +/// +/// vector of doubles +std::vector MarshalJSONIntoJsonDoubleVector(std::string input); + + +/// +/// Convert shared pointer to a vector of pairs of doubles and int32s to a JSON structure for interop. +/// +/// vector of pairs +std::shared_ptr>> MarshalFromJSONDoubleInt(std::string input); + +/// +/// Convert string to a shared pointer to a vector of int32s for interop. +/// +/// vector of pairs +std::shared_ptr> MarshalFromJSonInt32(std::string input); + +/// +/// Convert string to a shared pointer to a vector of int64s for interop. +/// +/// vector of int64s +std::shared_ptr> MarshalFromJSonInt64(std::string input); + +/// +/// Convert string to a shared pointer to a vector of doubles for interop. +/// +/// vector of doubles +std::shared_ptr> MarshalFromJsonDouble(std::string input); + +/// +/// Convert string to a shared pointer to a vector of pairs of ints for interop. +/// +/// vector of pairs of ints +std::shared_ptr>> MarshalFromJsonPairInts(std::string input); + +/// +/// Convert string to a shared pointer to a vector of pairs of ints for interop. +/// +/// vector of pairs of ints +std::vector> MarshalFromJsonTupleIntDoubleDouble(std::string input); + +/// +/// Convert string with token to a vector of sub strings +/// +/// vector of strings +std::vector SplitStringByToken(std::string input, std::string token); + +/// +/// Convert string to a shared pointer to a vector of size_t's for interop. +/// +/// vector of size_t's +std::vector MarshalIntoJSONSizeT(std::string input); + +/// +/// Set the error message for the caller to ask "what went wrong" when something goes wrong. +/// +/// none +void SetWrapperErrorMessage(std::string function_name, std::string input); diff --git a/pwiz_aux/msrc/utility/vendor_api/Mobilion/resource.h b/pwiz_aux/msrc/utility/vendor_api/Mobilion/resource.h new file mode 100644 index 0000000000..83a77519eb --- /dev/null +++ b/pwiz_aux/msrc/utility/vendor_api/Mobilion/resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AssemblyInfo.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 10 +#define VERSION_PATCH 1 +#define VERSION_BUILD 0 + + +#define PRODUCT_DESCRIPTION "MOBILion MBI Data Access Library" +#define PRODUCT_VERSION STRINGIZE(VERSION_MAJOR) \ + "." STRINGIZE(VERSION_MINOR) \ + "." STRINGIZE(VERSION_PATCH) \ + "." STRINGIZE(VERSION_BUILD) +#define ORIGINAL_FILENAME "MBI_SDK.dll" +#define COMPANY_NAME "MOBILion Systems, Inc." +#define COPYRIGHT "Copyright © 2021-2024, MOBILion Systems, Inc." +#define PRODUCT_NAME "MBI SDK" diff --git a/pwiz_tools/Shared/BiblioSpec/Properties/Resources.ja.resx b/pwiz_tools/Shared/BiblioSpec/Properties/Resources.ja.resx index 833b4af033..4b1320979a 100644 --- a/pwiz_tools/Shared/BiblioSpec/Properties/Resources.ja.resx +++ b/pwiz_tools/Shared/BiblioSpec/Properties/Resources.ja.resx @@ -1,76 +1,96 @@ + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + + + + + + + + + + + + + + + + + + - + + @@ -89,18 +109,18 @@ text/microsoft-resx - 1.3 + 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + 作成 - + 追加 diff --git a/pwiz_tools/Shared/BiblioSpec/Properties/Resources.resx b/pwiz_tools/Shared/BiblioSpec/Properties/Resources.resx index 916c7333f9..5deeacb494 100644 --- a/pwiz_tools/Shared/BiblioSpec/Properties/Resources.resx +++ b/pwiz_tools/Shared/BiblioSpec/Properties/Resources.resx @@ -1,76 +1,96 @@ + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + + + + + + + + + + + + + + + + + + - + + @@ -89,18 +109,18 @@ text/microsoft-resx - 1.3 + 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + Create - + Append diff --git a/pwiz_tools/Shared/Common/DataBinding/AbstractViewContext.cs b/pwiz_tools/Shared/Common/DataBinding/AbstractViewContext.cs index 07053b529b..47d7c4017b 100644 --- a/pwiz_tools/Shared/Common/DataBinding/AbstractViewContext.cs +++ b/pwiz_tools/Shared/Common/DataBinding/AbstractViewContext.cs @@ -46,7 +46,6 @@ public abstract class AbstractViewContext : IViewContext public const string DefaultViewName = "default"; private IList _rowSources; - protected AbstractViewContext(DataSchema dataSchema, IEnumerable rowSources) { DataSchema = dataSchema; @@ -335,7 +334,7 @@ protected virtual ViewEditor CreateViewEditor(ViewGroup viewGroup, ViewSpec view { return new ViewEditor(this, GetViewInfo(viewGroup, viewSpec)); } - + public virtual ViewSpec CustomizeView(Control owner, ViewSpec viewSpec, ViewGroup viewPath) { using (var customizeViewForm = CreateViewEditor(viewPath, viewSpec)) @@ -344,9 +343,11 @@ public virtual ViewSpec CustomizeView(Control owner, ViewSpec viewSpec, ViewGrou { return null; } + // Consider: if save fails, reshow CustomizeViewForm? ViewInfo viewInfo = customizeViewForm.ViewInfo; - viewInfo = new ViewInfo(viewInfo.ParentColumn, viewInfo.GetViewSpec().SetName(customizeViewForm.ViewName)); + viewInfo = new ViewInfo(viewInfo.ParentColumn, + viewInfo.GetViewSpec().SetName(customizeViewForm.ViewName)); SaveView(viewPath.Id, viewInfo.GetViewSpec(), viewSpec.Name); return viewInfo.GetViewSpec(); } diff --git a/pwiz_tools/Shared/Common/DataBinding/Controls/NavBar.cs b/pwiz_tools/Shared/Common/DataBinding/Controls/NavBar.cs index 7256b749ba..fcbe27ab08 100644 --- a/pwiz_tools/Shared/Common/DataBinding/Controls/NavBar.cs +++ b/pwiz_tools/Shared/Common/DataBinding/Controls/NavBar.cs @@ -38,6 +38,7 @@ public partial class NavBar : UserControl { private BindingListSource _bindingListSource; private string _waitingMsg; + public NavBar() { InitializeComponent(); diff --git a/pwiz_tools/Shared/CommonUtil/Properties/Resources.Designer.cs b/pwiz_tools/Shared/CommonUtil/Properties/Resources.Designer.cs index a2266dbac5..d32bd21940 100644 --- a/pwiz_tools/Shared/CommonUtil/Properties/Resources.Designer.cs +++ b/pwiz_tools/Shared/CommonUtil/Properties/Resources.Designer.cs @@ -60,6 +60,24 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to Run command:. + /// + internal static string ProcessRunner_Run_Run_command_ { + get { + return ResourceManager.GetString("ProcessRunner_Run_Run_command_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Working.... + /// + internal static string ProcessRunner_Run_Working_ { + get { + return ResourceManager.GetString("ProcessRunner_Run_Working_", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed reading {0} bytes. Source may be corrupted.. /// @@ -86,13 +104,5 @@ internal static string Units_ppm { return ResourceManager.GetString("Units_ppm", resourceCulture); } } - /// - /// Looks up a localized string similar to Run command:. - /// - internal static string ProcessRunner_Run_Run_command_ { - get { - return ResourceManager.GetString("ProcessRunner_Run_Run_command_", resourceCulture); - } - } } } diff --git a/pwiz_tools/Shared/CommonUtil/Properties/Resources.resx b/pwiz_tools/Shared/CommonUtil/Properties/Resources.resx index 168f02387c..e5e5a0a7e5 100644 --- a/pwiz_tools/Shared/CommonUtil/Properties/Resources.resx +++ b/pwiz_tools/Shared/CommonUtil/Properties/Resources.resx @@ -130,4 +130,7 @@ Run command: + + Working... + \ No newline at end of file diff --git a/pwiz_tools/Shared/CommonUtil/SystemUtil/ProcessRunner.cs b/pwiz_tools/Shared/CommonUtil/SystemUtil/ProcessRunner.cs index e53df52a56..d7f65ac231 100644 --- a/pwiz_tools/Shared/CommonUtil/SystemUtil/ProcessRunner.cs +++ b/pwiz_tools/Shared/CommonUtil/SystemUtil/ProcessRunner.cs @@ -51,6 +51,8 @@ public class ProcessRunner : IProcessRunner private readonly List _messageLog = new List(); private string _tmpDirForCleanup; + public bool EnableImmediateLog { get; set; } + /// /// Used in R package installation. We print progress % for processRunner progress /// but we dont want that output to be shown to the user when we display the output @@ -118,7 +120,7 @@ public void Run(ProcessStartInfo psi, string stdin, IProgressMonitor progress, r { try { - proc.StandardInput.Write(stdin); + proc.StandardInput.WriteLine(stdin); } finally { @@ -153,6 +155,9 @@ public void Run(ProcessStartInfo psi, string stdin, IProgressMonitor progress, r string line; while ((line = reader.ReadLine(progress)) != null) { + if (EnableImmediateLog) + Messages.WriteAsyncUserMessage(line); + if (writer != null && (HideLinePrefix == null || !line.StartsWith(HideLinePrefix))) writer.WriteLine(line); @@ -198,7 +203,11 @@ public void Run(ProcessStartInfo psi, string stdin, IProgressMonitor progress, r if (StatusPrefix != null) line = line.Substring(StatusPrefix.Length); - status = status.ChangeMessage(line); + if (EnableImmediateLog) + status = status.ChangeMessage(Resources.ProcessRunner_Run_Working_); + else + status = status.ChangeMessage(line); + progress.UpdateProgress(status); } } diff --git a/pwiz_tools/Skyline/Controls/LongWaitDlg.cs b/pwiz_tools/Skyline/Controls/LongWaitDlg.cs index 561edf3a87..3c7e2cb00b 100644 --- a/pwiz_tools/Skyline/Controls/LongWaitDlg.cs +++ b/pwiz_tools/Skyline/Controls/LongWaitDlg.cs @@ -18,6 +18,7 @@ */ using System; +using System.Runtime.CompilerServices; using System.Threading; using System.Windows.Forms; using pwiz.Common.SystemUtil; @@ -26,6 +27,8 @@ using pwiz.Skyline.Util; using pwiz.Skyline.Util.Extensions; +[assembly: InternalsVisibleTo("TestFunctional")] + namespace pwiz.Skyline.Controls { public partial class LongWaitDlg : FormEx, ILongWaitBroker @@ -131,7 +134,7 @@ public void SetProgressCheckCancel(int step, int totalSteps) public void PerformWork(Control parent, int delayMillis, Action performWork) { var indefiniteWaitBroker = new IndefiniteWaitBroker(performWork); - PerformWork(parent, delayMillis, indefiniteWaitBroker.PerformWork); + PerformWork(parent, delayMillis, indefiniteWaitBroker.PerformWork, CancellationToken.None); } public IProgressStatus PerformWork(Control parent, int delayMillis, Action performWork) @@ -143,7 +146,8 @@ public IProgressStatus PerformWork(Control parent, int delayMillis, Action performWork) + + public void PerformWork(Control parent, int delayMillis, Action performWork, CancellationToken cancellationToken = default) { _startTime = DateTime.UtcNow; // Said to be 117x faster than Now and this is for a delta _parentForm = parent; @@ -157,7 +161,7 @@ public void PerformWork(Control parent, int delayMillis, Action } // Action> runner = RunWork; // _result = runner.BeginInvoke(performWork, runner.EndInvoke, null); - ActionUtil.RunAsync(() => RunWork(performWork)); + ActionUtil.RunAsync(() => RunWork(performWork, cancellationToken)); // Wait as long as the caller wants before showing the progress // animation to the user. @@ -245,7 +249,7 @@ protected override void OnFormClosing(FormClosingEventArgs e) } - private void RunWork(Action performWork) + private void RunWork(Action performWork, CancellationToken cancellationToken) { try { diff --git a/pwiz_tools/Skyline/Executables/Installer/Product-template.wxs b/pwiz_tools/Skyline/Executables/Installer/Product-template.wxs index 7259139387..ac433cd0fe 100644 --- a/pwiz_tools/Skyline/Executables/Installer/Product-template.wxs +++ b/pwiz_tools/Skyline/Executables/Installer/Product-template.wxs @@ -237,6 +237,9 @@ + + + diff --git a/pwiz_tools/Skyline/InstallNvidiaLibraries.bat b/pwiz_tools/Skyline/InstallNvidiaLibraries.bat new file mode 100644 index 0000000000..eb54e0612a --- /dev/null +++ b/pwiz_tools/Skyline/InstallNvidiaLibraries.bat @@ -0,0 +1,48 @@ +@echo off +setlocal enabledelayedexpansion + +:: Set paths and URIs (you need to define these) +set "CudaVersion=12.6.3" +set "CudaInstaller=cuda_%CudaVersion%_windows_network.exe" +set "CudaDownloadUri=https://developer.download.nvidia.com/compute/cuda/%CudaVersion%/network_installers/%CudaInstaller%" +set "CudaDownloadPath=%USERPROFILE%\Downloads\%CudaInstaller%" + +set "CuDNNVersion=9.6.0.74_cuda12" +set "CuDNNArchive=cudnn-windows-x86_64-%CuDNNVersion%-archive" +set "CuDNNDownloadUri=https://developer.download.nvidia.com/compute/cudnn/redist/cudnn/windows-x86_64/%CuDNNArchive%.zip" +set "CuDNNDownloadPath=%USERPROFILE%\Downloads\%CuDNNArchive%.zip" +set "CuDNNVersionDir=%USERPROFILE%\Downloads\cudnn\%CuDNNVersion%" +set "CuDNNInstallDir=C:\Program Files\NVIDIA\CUDNN\v9.x" + +:: Download CUDA Library +echo Downloading CUDA Library... +powershell -Command "Invoke-WebRequest -Uri '%CudaDownloadUri%' -OutFile '%CudaDownloadPath%'" + +:: Install CUDA Library +echo Installing CUDA Library... +"%CudaDownloadPath%" + +:: Download cuDNN Library +echo Downloading cuDNN Library... +powershell -Command "Invoke-WebRequest -Uri '%CuDNNDownloadUri%' -OutFile '%CuDNNDownloadPath%'" + +:: Install cuDNN Library +echo Installing cuDNN Library... + +:: Extract cuDNN zip +powershell Expand-Archive -Path "%CuDNNDownloadPath%" -DestinationPath "%CuDNNVersionDir%" -Force + +:: Create necessary directories (this might require admin rights) +mkdir "%CuDNNInstallDir%\bin" 2>NUL +mkdir "%CuDNNInstallDir%\include" 2>NUL +mkdir "%CuDNNInstallDir%\lib" 2>NUL + +:: Copy files (you'll need to adjust permissions) +xcopy "%CuDNNVersionDir%\%CuDNNArchive%\bin\cudnn*.dll" "%CuDNNInstallDir%\bin" /Y /I +xcopy "%CuDNNVersionDir%\%CuDNNArchive%\include\cudnn*.h" "%CuDNNInstallDir%\include" /Y /I +xcopy "%CuDNNVersionDir%\%CuDNNArchive%\lib\x64\cudnn*.lib" "%CuDNNInstallDir%\lib" /Y /I + +REM echo Installation complete! + +endlocal +pause diff --git a/pwiz_tools/Skyline/Model/AlphaPeptDeep/AlphapeptdeepLibraryBuilder.cs b/pwiz_tools/Skyline/Model/AlphaPeptDeep/AlphapeptdeepLibraryBuilder.cs new file mode 100644 index 0000000000..292fb1fdf6 --- /dev/null +++ b/pwiz_tools/Skyline/Model/AlphaPeptDeep/AlphapeptdeepLibraryBuilder.cs @@ -0,0 +1,670 @@ +/* + * Author: David Shteynberg , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2025 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using pwiz.Common.SystemUtil; +using pwiz.Skyline.Model.DocSettings; +using pwiz.Skyline.Model.Irt; +using pwiz.Skyline.Model.Koina.Models; +using pwiz.Skyline.Model.Lib; +using pwiz.Skyline.Model.Tools; +using pwiz.Skyline.Util.Extensions; + +namespace pwiz.Skyline.Model.AlphaPeptDeep +{ + + public class LibraryHelper + { + private const string SEQUENCE = @"sequence"; + private const string MODS = @"mods"; + private const string MOD_SITES = @"mod_sites"; + private const string CHARGE = @"charge"; + private const string TAB = "\t"; + private const string PRECURSOR = @"Precursor"; + private const string PEPTIDE = @"Peptide"; + private const string PRECURSOR_CHARGE = @"Precursor Charge"; + private const string ISOTOPE_LABEL_TYPE = @"Isotope Label Type"; + private const string PRECURSOR_MZ = @"Precursor Mz"; + private const string MODIFIED_SEQUENCE = @"Modified Sequence"; + private const string PRECURSOR_EXPLICIT_COLLISION_ENERGY = @"Precursor Explicit Collision Energy"; + private const string PRECURSOR_NOTE = @"Precursor Note"; + private const string LIBRARY_NAME = @"Library Name"; + private const string LIBRARY_TYPE = @"Library Type"; + private const string LIBRARY_PROBABILITY_SCORE = @"Library Probability Score"; + private const string PEPTIDE_MODIFIED_SEQUENCE_UNIMOD_IDS = @"Peptide Modified Sequence Unimod Ids"; + public string InputFilePath { get; private set; } + private static readonly IEnumerable PrecursorTableColumnNames = new[] { SEQUENCE, MODS, MOD_SITES, CHARGE }; + private static readonly IEnumerable PrecursorTableColumnNamesCarafe = + new[] + { + PRECURSOR, PEPTIDE, PRECURSOR_CHARGE, ISOTOPE_LABEL_TYPE, PRECURSOR_MZ, MODIFIED_SEQUENCE, PRECURSOR_EXPLICIT_COLLISION_ENERGY, + PRECURSOR_NOTE, LIBRARY_NAME, LIBRARY_TYPE, LIBRARY_PROBABILITY_SCORE, PEPTIDE_MODIFIED_SEQUENCE_UNIMOD_IDS + }; + + private static IList carafeSupportedModificationIndices => + new[] + { + new ModificationIndex(4, new ModificationType(@"4", @"Carbamidomethyl", @"H(3) C(2) N O")), + new ModificationIndex(21, new ModificationType(@"21", @"Phospho", @"H O(3) P")), + new ModificationIndex(35, new ModificationType(@"35", @"Oxidation", @"O")) + + }; + + internal static readonly IList CarafeSupportedModificationNames = populateUniModList(carafeSupportedModificationIndices); + + /// + /// List of UniMod Modifications available + /// + internal static readonly IList AlphapeptdeepModificationNames = populateUniModList(null); + private static IList populateUniModList(IList supportedList) + { + IList modList = new List(); + for (int m = 0; m < UniModData.UNI_MOD_DATA.Length; m++) + { + if (!UniModData.UNI_MOD_DATA[m].ID.HasValue || + (supportedList != null && + supportedList.Where(x => x.Index == UniModData.UNI_MOD_DATA[m].ID.Value).ToArray().SingleOrDefault() == null) ) + continue; + + var accession = UniModData.UNI_MOD_DATA[m].ID.Value + @":" + UniModData.UNI_MOD_DATA[m].Name; + var name = UniModData.UNI_MOD_DATA[m].Name; + var formula = UniModData.UNI_MOD_DATA[m].Formula; + modList.Add(new ModificationType(accession, name, formula)); + } + return modList; + } + public LibraryHelper(string inputFilePath) + { + InputFilePath = inputFilePath; + + } + public void PrepareInputFile(SrmDocument Document, IProgressMonitor progress, ref IProgressStatus progressStatus, string toolName) + { + progress.UpdateProgress(progressStatus = progressStatus + .ChangeMessage(@"Preparing input file") + .ChangePercentComplete(0)); + + var warningMods = GetWarningMods(Document, toolName); + + var precursorTable = GetPrecursorTable(Document, toolName, warningMods); + File.WriteAllLines(InputFilePath, precursorTable); + progress.UpdateProgress(progressStatus = progressStatus + .ChangePercentComplete(100)); + } + + public static IEnumerable> GetPrecursors(SrmDocument document) + { + foreach (var peptideDocNode in document.Peptides) + { + var modifiedSequence = + ModifiedSequence.GetModifiedSequence(document.Settings, peptideDocNode, IsotopeLabelType.light); + foreach (var charge in peptideDocNode.TransitionGroups + .Select(transitionGroup => transitionGroup.PrecursorCharge).Distinct()) + { + yield return Tuple.Create(modifiedSequence, charge); + } + } + } + + public IEnumerable GetPrecursorTable(SrmDocument Document, string toolName, List warningMods = null) + { + var result = new List(); + string header; + bool alphapeptDeepFormat = toolName.Equals(@"alphapeptdeep"); + + if (alphapeptDeepFormat) + header = string.Join(TAB, PrecursorTableColumnNames); + else if (toolName.Equals(@"carafe")) + header = string.Join(TAB, PrecursorTableColumnNamesCarafe); + else + header = string.Join(TAB, PrecursorTableColumnNamesCarafe); + + + result.Add(header); + + // Build precursor table row by row + foreach (var peptide in Document.Peptides) + { + var unmodifiedSequence = peptide.Peptide.Sequence; + var modifiedSequence = ModifiedSequence.GetModifiedSequence(Document.Settings, peptide, IsotopeLabelType.light); + var modsBuilder = new StringBuilder(); + var modSitesBuilder = new StringBuilder(); + bool unsupportedModification = false; + + var ModificationNames = + alphapeptDeepFormat ? AlphapeptdeepModificationNames : CarafeSupportedModificationNames; + + for (var i = 0; i < modifiedSequence.ExplicitMods.Count; i++) + { + var mod = modifiedSequence.ExplicitMods[i]; + var modWarns = warningMods.Where(m => m == mod.Name).ToArray(); + if (!mod.UnimodId.HasValue && modWarns.Length == 0) + { + var msg = string.Format(ModelsResources.BuildPrecursorTable_UnsupportedModification, modifiedSequence, mod.Name, toolName); + Messages.WriteAsyncUserMessage(msg); + unsupportedModification = true; + continue; + } + + var unimodIdAA = mod.UnimodIdAA; + var modNames = ModificationNames.Where(m => m.Accession == unimodIdAA).ToArray(); + + if (modNames.Length == 0 && modWarns.Length == 0 ) + { + var msg = string.Format(ModelsResources.BuildPrecursorTable_Unimod_UnsupportedModification, modifiedSequence, mod.Name, unimodIdAA, toolName); + Messages.WriteAsyncUserMessage(msg); + unsupportedModification = true; + + continue; + } + if (modNames.Length == 0) continue; + + string modName = ""; + + if (alphapeptDeepFormat) + { + modName = modNames.Single().AlphaNameWithAminoAcid(unmodifiedSequence, mod.IndexAA); + } + else + { + modName = modNames.Single().Name; + } + + modsBuilder.Append(modName); + modSitesBuilder.Append((mod.IndexAA + 1).ToString()); // + 1 because alphapeptdeep mod_site number starts from 1 as the first amino acid + if (i != modifiedSequence.ExplicitMods.Count - 1) + { + modsBuilder.Append(TextUtil.SEMICOLON); + modSitesBuilder.Append(TextUtil.SEMICOLON); + } + } + if (unsupportedModification) + continue; + + foreach (var charge in peptide.TransitionGroups + .Select(transitionGroup => transitionGroup.PrecursorCharge).Distinct()) + { + if (alphapeptDeepFormat) + { + result.Add(string.Join(TAB, new[] + { + unmodifiedSequence, modsBuilder.ToString(), modSitesBuilder.ToString(), charge.ToString() + }) + ); + } + else + { + var docNode = peptide.TransitionGroups.Where(group => group.PrecursorCharge == charge).FirstOrDefault(); + if (docNode == null) + { + continue; + } + var precursor = LabelPrecursor(docNode.TransitionGroup, docNode.PrecursorMz, string.Empty); + var collisionEnergy = docNode.ExplicitValues.CollisionEnergy != null ? docNode.ExplicitValues.CollisionEnergy.ToString() : @"#N/A"; + var note = docNode.Annotations.Note != null ? docNode.Annotations.Note : @"#N/A"; + var libraryName = docNode.LibInfo != null && docNode.LibInfo.LibraryName != null ? docNode.LibInfo.LibraryName : @"#N/A"; + var libraryType = docNode.LibInfo != null && docNode.LibInfo.LibraryTypeName != null ? docNode.LibInfo.LibraryTypeName : @"#N/A"; + var libraryScore = docNode.LibInfo != null && docNode.LibInfo.Score != null ? docNode.LibInfo.Score.ToString() : @"#N/A"; + var unimodSequence = modifiedSequence.ToString(); + + result.Add(string.Join(TAB, new[] + { + precursor, unmodifiedSequence, charge.ToString(), docNode.LabelType.ToString(), + docNode.PrecursorMz.ToString(), unimodSequence, collisionEnergy, + note, libraryName, libraryType, libraryScore, modifiedSequence.UnimodIds + }) + ); + } + } + } + + return result; + } + public List GetWarningMods(SrmDocument Document, string toolName) + { + var resultList = new List(); + + bool alphapeptDeepFormat = toolName.Equals(@"alphapeptdeep"); + + // Build precursor table row by row + foreach (var peptide in Document.Peptides) + { + var modifiedSequence = ModifiedSequence.GetModifiedSequence(Document.Settings, peptide, IsotopeLabelType.light); + + var ModificationNames = + alphapeptDeepFormat ? AlphapeptdeepModificationNames : CarafeSupportedModificationNames; + + for (var i = 0; i < modifiedSequence.ExplicitMods.Count; i++) + { + var mod = modifiedSequence.ExplicitMods[i]; + if (!mod.UnimodId.HasValue) + { + var haveMod = resultList.FirstOrDefault(m => m == mod.Name); + if (haveMod == null) + { + resultList.Add(mod.Name); + } + } + + var unimodIdAA = mod.UnimodIdAA; + var modNames = ModificationNames.Where(m => m.Accession == unimodIdAA).ToArray(); + if (modNames.Length == 0) + { + var haveMod = resultList.FirstOrDefault(m => m == mod.Name); + if (haveMod == null) + { + resultList.Add(mod.Name); + } + } + } + } + return resultList; + } + public static string LabelPrecursor(TransitionGroup tranGroup, double precursorMz, + string resultsText) + { + return string.Format(@"{0}{1}{2}{3}", LabelMz(tranGroup, precursorMz), + Transition.GetChargeIndicator(tranGroup.PrecursorAdduct), + tranGroup.LabelTypeText, resultsText); + } + + private static string LabelMz(TransitionGroup tranGroup, double precursorMz) + { + int? massShift = tranGroup.DecoyMassShift; + double shift = SequenceMassCalc.GetPeptideInterval(massShift); + return string.Format(@"{0:F04}{1}", precursorMz - shift, + Transition.GetDecoyText(massShift)); + } + } + public class ArgumentAndValue + { + public ArgumentAndValue(string name, string value, string dash = @"--") + { + Name = name; + Value = value; + Dash = dash; + } + public string Name { get; private set; } + public string Value { get; private set; } + public string Dash { get; set; } + + public override string ToString() { return Dash + Name + TextUtil.SPACE + Value; } + } + + public class ModificationType + { + public ModificationType(string accession, string name, string comment) + { + Accession = accession; + Name = name; + Comment = comment; + } + public string Accession { get; private set; } + public string Name { get; private set; } + public string Comment { get; private set; } + + public string AlphaNameWithAminoAcid(string unmodifiedSequence, int index) + { + string modification = Name.Replace(@"(", "").Replace(@")", @"").Replace(@" ", @"@").Replace(@"Acetyl@N-term",@"Acetyl@Protein_N-term"); + char delimiter = '@'; + string[] name = modification.Split(delimiter); + string alphaName = name[0] + @"@" + unmodifiedSequence[index]; + if (index == 0 && modification.EndsWith(@"term")) + { + alphaName = modification; + } + return alphaName; + } + public override string ToString() { return string.Format(ModelsResources.BuildPrecursorTable_ModificationType, Accession, Name, Comment); } + } + + public class ModificationIndex + { + public ModificationIndex(int index, ModificationType modification) + { + Index = index; + Modification = modification; + } + public ModificationType Modification { get; private set; } + public int Index { get; private set; } + + public override string ToString() + { + return Index.ToString() + @":" + Modification.ToString(); + } + } + + + public class AlphapeptdeepLibraryBuilder : IiRTCapableLibraryBuilder + { + private const string ALPHAPEPTDEEP = @"alphapeptdeep"; + private const string BLIB_BUILD = @"BlibBuild"; + private const string CMD_FLOW_COMMAND = @"cmd-flow"; + private const string EXPORT_SETTINGS_COMMAND = @"export-settings"; + private const string EXT_TSV = TextUtil.EXT_TSV; + private const string INPUT = @"input"; + private const string LEFT_PARENTHESIS = TextUtil.LEFT_PARENTHESIS; + private const string LEFT_SQUARE_BRACKET = TextUtil.LEFT_SQUARE_BRACKET; + private const string LIBRARY_COMMAND = @"library"; + private const string MODIFIED_PEPTIDE = "ModifiedPeptide"; + private const string MODS = @"mods"; + private const string OUTPUT = @"output"; + private const string OUTPUT_MODELS = @"output_models"; + private const string OUTPUT_SPECTRAL_LIB_FILE_NAME = @"predict.speclib.tsv"; + private const string OUTPUT_SPECTRAL_LIBS = @"output_spectral_libs"; + private const string PEPTDEEP_EXECUTABLE = "peptdeep.exe"; + private const string RIGHT_PARENTHESIS = TextUtil.RIGHT_PARENTHESIS; + private const string RIGHT_SQUARE_BRACKET = TextUtil.RIGHT_SQUARE_BRACKET; + private const string SEMICOLON = TextUtil.SEMICOLON; + private const string SETTINGS_FILE_NAME = @"settings.yaml"; + private const string SPACE = TextUtil.SPACE; + private const string TAB = "\t"; + private const string TRANSFORMED_OUTPUT_SPECTRAL_LIB_FILE_NAME = @"predict_transformed.speclib.tsv"; + private const string UNDERSCORE = TextUtil.UNDERSCORE; + + + + public string AmbiguousMatchesMessage + { + //TODO(xgwang): implement + get { return null; } + } + public IrtStandard IrtStandard + { + //TODO(xgwang): implement + get { return null; } + } + public string BuildCommandArgs + { + //TODO(xgwang): implement + get { return null; } + } + public string BuildOutput + { + //TODO(xgwang): implement + get { return null; } + } + public LibrarySpec LibrarySpec { get; private set; } + public string BuilderLibraryPath => OutputSpectraLibFilepath; + + public LibraryHelper LibraryHelper { get; private set; } + + private static readonly DateTime _nowTime = DateTime.Now; + + public static readonly string TimeStamp = _nowTime.ToString(@"yyyy-MM-dd_HH-mm-ss"); + private string PythonVirtualEnvironmentScriptsDir { get; } + private string PeptdeepExecutablePath => Path.Combine(PythonVirtualEnvironmentScriptsDir, PEPTDEEP_EXECUTABLE); + + private static readonly string RootDir = Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), ALPHAPEPTDEEP, TimeStamp); + private string SettingsFilePath => Path.Combine(RootDir, SETTINGS_FILE_NAME); + private string InputFileName => INPUT + UNDERSCORE + EXT_TSV; //Convert.ToBase64String(Encoding.ASCII.GetBytes(Document.DocumentHash)) + EXT_TSV; + private string InputFilePath => Path.Combine(RootDir, InputFileName); + private string OutputModelsDir => Path.Combine(RootDir, OUTPUT_MODELS); + private string OutputSpectralLibsDir => Path.Combine(RootDir, OUTPUT_SPECTRAL_LIBS); + private string OutputSpectraLibFilepath => Path.Combine(OutputSpectralLibsDir, OUTPUT_SPECTRAL_LIB_FILE_NAME); + private string TransformedOutputSpectraLibFilepath => Path.Combine(OutputSpectralLibsDir, TRANSFORMED_OUTPUT_SPECTRAL_LIB_FILE_NAME); + + public string ToolName { get; } + public SrmDocument Document { get; private set; } + /// + /// The peptdeep cmd-flow command is how we can pass arguments that will override the settings.yaml file. + /// This is how the peptdeep CLI supports command line arguments. + /// + private IList CmdFlowCommandArguments => + new [] + { + new ArgumentAndValue(@"task_workflow", @"library"), + new ArgumentAndValue(@"settings_yaml", SettingsFilePath), + new ArgumentAndValue(@"PEPTDEEP_HOME", RootDir), + new ArgumentAndValue(@"transfer--model_output_folder", OutputModelsDir), + new ArgumentAndValue(@"library--infile_type", @"precursor_table"), + new ArgumentAndValue(@"library--infiles", InputFilePath), + new ArgumentAndValue(@"library--output_folder", OutputSpectralLibsDir), + new ArgumentAndValue(@"library--output_tsv--enabled", @"True"), + new ArgumentAndValue(@"library--output_tsv--translate_mod_to_unimod_id", @"True"), + new ArgumentAndValue(@"library--decoy", @"diann"), + new ArgumentAndValue(@"device", @"gpu"), + }; + + private Dictionary OpenSwathAssayColName => + new Dictionary() + { + { @"RT", @"NormalizedRetentionTime" }, + { @"ModifiedPeptide", @"ModifiedPeptideSequence" }, + { @"FragmentMz", @"ProductMz" }, + { @"RelativeIntensity", @"LibraryIntensity" }, + { @"FragmentNumber", @"FragmentSeriesNumber" } + }; + + public AlphapeptdeepLibraryBuilder(string libName, string libOutPath, string pythonVirtualEnvironmentScriptsDir, SrmDocument document) + { + Document = document; + Directory.CreateDirectory(RootDir); + LibrarySpec = new BiblioSpecLiteSpec(libName, libOutPath); + if (Document.DocumentHash != null) LibraryHelper = new LibraryHelper(InputFilePath); + PythonVirtualEnvironmentScriptsDir = pythonVirtualEnvironmentScriptsDir; + ToolName = @"alphapeptdeep"; + } + + public bool BuildLibrary(IProgressMonitor progress) + { + IProgressStatus progressStatus = new ProgressStatus(); + try + { + RunAlphapeptdeep(progress, ref progressStatus); + progress.UpdateProgress(progressStatus = progressStatus.Complete()); + return true; + } + catch (Exception exception) + { + progress.UpdateProgress(progressStatus.ChangeErrorException(exception)); + return false; + } + } + + private void RunAlphapeptdeep(IProgressMonitor progress, ref IProgressStatus progressStatus) + { + progressStatus = progressStatus.ChangeSegments(0, 5); + Directory.CreateDirectory(RootDir); + LibraryHelper.PrepareInputFile(Document, progress, ref progressStatus, @"alphapeptdeep"); + + progressStatus = progressStatus.NextSegment(); + + PrepareSettingsFile(progress, ref progressStatus); + + progressStatus = progressStatus.NextSegment(); + + ExecutePeptdeep(progress, ref progressStatus); + + progressStatus = progressStatus.NextSegment(); + + TransformPeptdeepOutput(progress, ref progressStatus); + progressStatus = progressStatus.NextSegment(); + + ImportSpectralLibrary(progress, ref progressStatus); + } + + private void PrepareSettingsFile(IProgressMonitor progress, ref IProgressStatus progressStatus) + { + progress.UpdateProgress(progressStatus = progressStatus + .ChangeMessage(@"Preparing settings file") + .ChangePercentComplete(0)); + + // generate template settings.yaml file + var pr = new ProcessRunner(); + var psi = new ProcessStartInfo(PeptdeepExecutablePath, $@"{EXPORT_SETTINGS_COMMAND} {SettingsFilePath}") + { + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = false + }; + try + { + pr.EnableImmediateLog = true; + pr.Run(psi, string.Empty, progress, ref progressStatus, ProcessPriorityClass.BelowNormal, true); + } + catch (Exception ex) + { + // TODO(xgwang): update this exception to an Alphapeptdeep specific one + throw new Exception(@"Failed to generate settings.yaml file by executing the peptdeep export-settings command.", ex); + } + + progress.UpdateProgress(progressStatus = progressStatus + .ChangePercentComplete(100)); + } + + private void ExecutePeptdeep(IProgressMonitor progress, ref IProgressStatus progressStatus) + { + progress.UpdateProgress(progressStatus = progressStatus + .ChangeMessage(@"Executing peptdeep") + .ChangePercentComplete(0)); + + // compose peptdeep cmd-flow command arguments to build library + var args = new StringBuilder(); + foreach (var arg in CmdFlowCommandArguments) + { + args.Append(arg).Append(SPACE); + } + + // execute command + var pr = new ProcessRunner(); + var psi = new ProcessStartInfo(PeptdeepExecutablePath, $@"{CMD_FLOW_COMMAND} {args}") + { + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = false + }; + try + { + pr.EnableImmediateLog = true; + pr.Run(psi, string.Empty, progress, ref progressStatus, ProcessPriorityClass.BelowNormal, true); + } + catch (Exception ex) + { + // TODO(xgwang): update this exception to an Alphapeptdeep specific one + throw new Exception(@"Failed to build library by executing the peptdeep cmd-flow command.", ex); + } + + progress.UpdateProgress(progressStatus = progressStatus + .ChangePercentComplete(100)); + } + + private void TransformPeptdeepOutput(IProgressMonitor progress, ref IProgressStatus progressStatus) + { + progress.UpdateProgress(progressStatus = progressStatus + .ChangeMessage(@"Importing spectral library") + .ChangePercentComplete(0)); + + var result = new List(); + var reader = new DsvFileReader(OutputSpectraLibFilepath, TextUtil.SEPARATOR_TSV); + + // transform table header + var colNames = reader.FieldNames; + var newColNames = new List(); + foreach(var colName in colNames) + { + string newColName; + if (!OpenSwathAssayColName.TryGetValue(colName, out newColName)) + { + newColName = colName; + } + newColNames.Add(newColName); + } + var header = string.Join(TAB, newColNames); + result.Add(header); + + // transform table body line by line + while (null != reader.ReadLine()) + { + var line = new List(); + foreach (var colName in colNames) + { + var cell = reader.GetFieldByName(colName); + if (colName == MODIFIED_PEPTIDE) + { + var transformedCell = cell.Replace(UNDERSCORE, String.Empty) + .Replace(LEFT_SQUARE_BRACKET, LEFT_PARENTHESIS) + .Replace(RIGHT_SQUARE_BRACKET, RIGHT_PARENTHESIS); + line.Add(transformedCell); + } + else + { + line.Add(cell); + } + } + result.Add(string.Join(TAB, line)); + } + + // write to new file + File.WriteAllLines(TransformedOutputSpectraLibFilepath, result); + + progress.UpdateProgress(progressStatus = progressStatus + .ChangePercentComplete(100)); + } + + private void ImportSpectralLibrary(IProgressMonitor progress, ref IProgressStatus progressStatus) + { + progress.UpdateProgress(progressStatus = progressStatus + .ChangeMessage(@"Importing spectral library") + .ChangePercentComplete(0)); + var pr = new ProcessRunner(); + string args = $@"-o -i {TextUtil.Quote(LibrarySpec.Name)} {TextUtil.Quote(TransformedOutputSpectraLibFilepath)} {TextUtil.Quote(LibrarySpec.FilePath)}"; + var psi = new ProcessStartInfo(BLIB_BUILD, args) + { + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = false + }; + try + { + pr.EnableImmediateLog = true; + pr.Run(psi, string.Empty, progress, ref progressStatus, ProcessPriorityClass.BelowNormal, true); + } + catch (Exception ex) + { + // TODO(xgwang): update this exception to an Alphapeptdeep specific one + throw new Exception(@"Failed to import spectral library by executing BlibBuild", ex); + } + progress.UpdateProgress(progressStatus = progressStatus + .ChangePercentComplete(100)); + } + + //TODO(xgwang): implement + public bool IsCanceled => false; + + public UpdateProgressResponse UpdateProgress(IProgressStatus status) + { + //TODO(xgwang): implement + return UpdateProgressResponse.normal; + } + + public bool HasUI => false; + } +} diff --git a/pwiz_tools/Skyline/Model/Carafe/CarafeLibraryBuilder.cs b/pwiz_tools/Skyline/Model/Carafe/CarafeLibraryBuilder.cs new file mode 100644 index 0000000000..bec34c6cbd --- /dev/null +++ b/pwiz_tools/Skyline/Model/Carafe/CarafeLibraryBuilder.cs @@ -0,0 +1,428 @@ +/* + * Author: David Shteynberg , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2025 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using Ionic.Zip; +using JetBrains.Annotations; +using pwiz.Common.Collections; +using pwiz.Common.SystemUtil; +using pwiz.Skyline.Model.Irt; +using pwiz.Skyline.Model.Lib; +using pwiz.Skyline.Model.Tools; +using pwiz.Skyline.Util; +using pwiz.Skyline.Util.Extensions; +using pwiz.Skyline.Model.AlphaPeptDeep; +using pwiz.Skyline.Model.DocSettings; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("TestFunctional")] + +namespace pwiz.Skyline.Model.Carafe +{ + public class CarafeLibraryBuilder : IiRTCapableLibraryBuilder + { + private const string BIN = @"bin"; + private const string INPUT = @"input"; + private const string CARAFE = @"carafe"; + private const string CARAFE_VERSION = @"0.0.1"; + private const string CMD_ARG_C = @"/C"; + private const string CMD_EXECUTABLE = @"cmd.exe"; + private const string CONDITIONAL_CMD_PROCEEDING_SYMBOL = TextUtil.AMPERSAND + TextUtil.AMPERSAND; + private const string DOT_JAR = @".jar"; + private const string DOT_ZIP = @".zip"; + private const string DOWNLOADS = @"Downloads"; + private const string HYPHEN = TextUtil.HYPHEN; + private const string JAVA = @"java"; + private const string JAVA_EXECUTABLE = @"java.exe"; + private const string JAVA_SDK_DOWNLOAD_URL = @"https://download.oracle.com/java/21/latest/"; + private const string OUTPUT_LIBRARY = @"output_library"; + private const string OUTPUT_LIBRARY_FILE_NAME = "SkylineAI_spectral_library.blib"; + private const string SPACE = TextUtil.SPACE; + private const string TAB = "\t"; + public LibraryHelper LibraryHelper { get; private set; } + public string AmbiguousMatchesMessage + { + //TODO(xgwang): implement + get { return null; } + } + public IrtStandard IrtStandard + { + //TODO(xgwang): implement + get { return null; } + } + public string BuildCommandArgs + { + //TODO(xgwang): implement + get { return null; } + } + public string BuildOutput + { + //TODO(xgwang): implement + get { return null; } + } + public LibrarySpec LibrarySpec { get; private set; } + private string PythonVersion { get; } + private string PythonVirtualEnvironmentName { get; } + public SrmDocument Document { get; private set; } + [CanBeNull] private string ProteinDatabaseFilePath { get; } + internal string ExperimentDataFilePath { get; set; } + internal string ExperimentDataTuningFilePath { get; set; } + + private bool BuildLibraryForCurrentSkylineDocument => ProteinDatabaseFilePath.IsNullOrEmpty(); + private string PythonVirtualEnvironmentActivateScriptPath => + PythonInstallerUtil.GetPythonVirtualEnvironmentActivationScriptPath(PythonVersion, + PythonVirtualEnvironmentName); + private string RootDir => Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), CARAFE); + private string JavaDir => Path.Combine(RootDir, JAVA); + private string CarafeDir => Path.Combine(RootDir, CARAFE); + private string UserDir => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + private DirectoryInfo JavaDirInfo => new DirectoryInfo(JavaDir); + private DirectoryInfo CarafeDirInfo => new DirectoryInfo(CarafeDir); + private string JavaSdkDownloadFileName => @"jdk-21_windows-x64_bin.zip"; + private Uri JavaSdkUri => new Uri(JAVA_SDK_DOWNLOAD_URL + JavaSdkDownloadFileName); + private string JavaSdkDownloadPath => Path.Combine(JavaDir, JavaSdkDownloadFileName); + private string JavaExecutablePath { get; set; } + private string CarafeFileBaseName => CARAFE + HYPHEN + CARAFE_VERSION; + private string CarafeJarZipFileName => CarafeFileBaseName + DOT_ZIP; + private string CarafeJarFileName => CarafeFileBaseName + DOT_JAR; + private Uri CarafeJarZipDownloadUrl = new Uri(@$"https://github.com/Noble-Lab/Carafe/releases/download/v{CARAFE_VERSION}/{CARAFE}-{CARAFE_VERSION}{DOT_ZIP}"); + private Uri CarafeJarZipUri => new Uri(CarafeJarZipDownloadUrl + CarafeJarZipFileName); + private string CarafeJarZipLocalPath => Path.Combine(UserDir, DOWNLOADS, CarafeJarZipFileName); + private Uri CarafeJarZipLocalUri => new Uri(@$"file:///{CarafeJarZipLocalPath}"); + private string CarafeJarZipDownloadPath => Path.Combine(CarafeDir, CarafeJarZipFileName); + private string CarafeOutputLibraryDir => Path.Combine(CarafeDir, OUTPUT_LIBRARY); + private string CarafeOutputLibraryFilePath => Path.Combine(CarafeOutputLibraryDir, OUTPUT_LIBRARY_FILE_NAME); + + public string BuilderLibraryPath => CarafeOutputLibraryFilePath; + + private string CarafeJarFileDir => Path.Combine(CarafeDir, CarafeFileBaseName); + private string CarafeJarFilePath => Path.Combine(CarafeJarFileDir, CarafeJarFileName); + private string InputFileName => INPUT + TextUtil.UNDERSCORE + Convert.ToBase64String(Encoding.ASCII.GetBytes(Document.DocumentHash)) + TextUtil.EXT_TSV; + private string InputFilePath => Path.Combine(RootDir, InputFileName); + private IList CommandArguments => + new [] + { + new ArgumentAndValue(@"jar", CarafeJarFilePath, TextUtil.HYPHEN), + new ArgumentAndValue(@"db", InputFilePath , TextUtil.HYPHEN), + new ArgumentAndValue(@"i", ExperimentDataTuningFilePath, TextUtil.HYPHEN), + new ArgumentAndValue(@"ms", ExperimentDataFilePath, TextUtil.HYPHEN), + new ArgumentAndValue(@"o", CarafeOutputLibraryDir, TextUtil.HYPHEN), + new ArgumentAndValue(@"c_ion_min", @"2", TextUtil.HYPHEN), + new ArgumentAndValue(@"cor", @"0.8", TextUtil.HYPHEN), + new ArgumentAndValue(@"device", @"gpu", TextUtil.HYPHEN), + new ArgumentAndValue(@"enzyme", @"2", TextUtil.HYPHEN), + new ArgumentAndValue(@"ez", string.Empty, TextUtil.HYPHEN), + new ArgumentAndValue(@"fast", string.Empty, TextUtil.HYPHEN), + new ArgumentAndValue(@"fixMod", @"1", TextUtil.HYPHEN), + new ArgumentAndValue(@"itol", @"20", TextUtil.HYPHEN), + new ArgumentAndValue(@"itolu", @"ppm", TextUtil.HYPHEN), + new ArgumentAndValue(@"lf_frag_n_min", @"2", TextUtil.HYPHEN), + new ArgumentAndValue(@"lf_top_n_frag", @"20", TextUtil.HYPHEN), + new ArgumentAndValue(@"lf_type", @"skyline", TextUtil.HYPHEN), + new ArgumentAndValue(@"max_pep_mz", @"1000", TextUtil.HYPHEN), + new ArgumentAndValue(@"maxLength", @"35", TextUtil.HYPHEN), + new ArgumentAndValue(@"maxVar", @"1", TextUtil.HYPHEN), + new ArgumentAndValue(@"min_mz", @"200", TextUtil.HYPHEN), + new ArgumentAndValue(@"min_pep_mz", @"400", TextUtil.HYPHEN), + new ArgumentAndValue(@"minLength", @"7", TextUtil.HYPHEN), + new ArgumentAndValue(@"miss_c", @"1", TextUtil.HYPHEN), + new ArgumentAndValue(@"mode", @"general", TextUtil.HYPHEN), + new ArgumentAndValue(@"n_ion_min", @"2", TextUtil.HYPHEN), + new ArgumentAndValue(@"na", @"0", TextUtil.HYPHEN), + new ArgumentAndValue(@"nf", @"4", TextUtil.HYPHEN), + new ArgumentAndValue(@"nm", string.Empty, TextUtil.HYPHEN), + new ArgumentAndValue(@"rf_rt_win", @"1", TextUtil.HYPHEN), + new ArgumentAndValue(@"rf", string.Empty, TextUtil.HYPHEN), + new ArgumentAndValue(@"seed", @"2000", TextUtil.HYPHEN), + new ArgumentAndValue(@"se", @"DIA-NN", TextUtil.HYPHEN), + new ArgumentAndValue(@"skyline", string.Empty, TextUtil.HYPHEN), + new ArgumentAndValue(@"tf", @"all", TextUtil.HYPHEN), + new ArgumentAndValue(@"valid", string.Empty, TextUtil.HYPHEN), + new ArgumentAndValue(@"varMod", @"0", TextUtil.HYPHEN) + }; + + + /// + /// List of UniMod Modifications available + /// + internal static readonly IList AlphapeptdeepModificationName = populateUniModList(); + private static IList populateUniModList() + { + IList modList = new List(); + for (int m = 0; m < UniModData.UNI_MOD_DATA.Length; m++) + { + if (!UniModData.UNI_MOD_DATA[m].ID.HasValue) + continue; + var accession = UniModData.UNI_MOD_DATA[m].ID.Value + @":" + UniModData.UNI_MOD_DATA[m].Name; + var name = UniModData.UNI_MOD_DATA[m].Name; + var formula = UniModData.UNI_MOD_DATA[m].Formula; + modList.Add(new ModificationType(accession, name, formula)); + } + return modList; + } + + public string ProductLibraryPath() + { + return CarafeOutputLibraryFilePath; + } + + public string ToolName { get; } + public CarafeLibraryBuilder( + string libName, + string libOutPath, + string pythonVersion, + string pythonVirtualEnvironmentName, + string experimentDataFilePath, + string experimentDataTuningFilePath, + SrmDocument document) + { + Document = document; + ToolName = @"carafe"; + Directory.CreateDirectory(RootDir); + Directory.CreateDirectory(JavaDir); + Directory.CreateDirectory(CarafeDir); + LibrarySpec = new BiblioSpecLiteSpec(libName, libOutPath); + LibraryHelper = new LibraryHelper(InputFilePath); + PythonVersion = pythonVersion; + PythonVirtualEnvironmentName = pythonVirtualEnvironmentName; + ExperimentDataFilePath = experimentDataFilePath; + ExperimentDataTuningFilePath = experimentDataTuningFilePath; + } + + public CarafeLibraryBuilder( + string libName, + string libOutPath, + string pythonVersion, + string pythonVirtualEnvironmentName, + string proteinDatabaseFilePath, + string experimentDataFilePath, + string experimentDataTuningFilePath, + SrmDocument document) + { + LibrarySpec = new BiblioSpecLiteSpec(libName, libOutPath); + PythonVersion = pythonVersion; + PythonVirtualEnvironmentName = pythonVirtualEnvironmentName; + ProteinDatabaseFilePath = proteinDatabaseFilePath; + ExperimentDataFilePath = experimentDataFilePath; + ExperimentDataTuningFilePath = experimentDataTuningFilePath; + Document = document; + Directory.CreateDirectory(RootDir); + Directory.CreateDirectory(JavaDir); + Directory.CreateDirectory(CarafeDir); + } + + public bool BuildLibrary(IProgressMonitor progress) + { + IProgressStatus progressStatus = new ProgressStatus(); + try + { + RunCarafe(progress, ref progressStatus); + progress.UpdateProgress(progressStatus = progressStatus.Complete()); + return true; + } + catch (Exception exception) + { + progress.UpdateProgress(progressStatus.ChangeErrorException(exception)); + return false; + } + } + + private void RunCarafe(IProgressMonitor progress, ref IProgressStatus progressStatus) + { + progressStatus = progressStatus.ChangeSegments(0, 3); + + SetupJavaEnvironment(progress, ref progressStatus); + progressStatus = progressStatus.NextSegment(); + if (BuildLibraryForCurrentSkylineDocument) + { + LibraryHelper.PrepareInputFile(Document, progress, ref progressStatus, @"carafe"); + + } + ExecuteCarafe(progress, ref progressStatus); + progressStatus = progressStatus.NextSegment(); + + ImportSpectralLibrary(progress, ref progressStatus); + } + private void SetupJavaEnvironment(IProgressMonitor progress, ref IProgressStatus progressStatus) + { + progress.UpdateProgress(progressStatus = progressStatus + .ChangeMessage(@"Setting up Java environment") + .ChangePercentComplete(0)); + + var isJavaValid = ValidateJava(); + var isCarafeValid = ValidateCarafe(); + if (isJavaValid && isCarafeValid) + { + return; + } + + if (!isJavaValid) + { + // clean java dir + JavaDirInfo.Delete(true); + Directory.CreateDirectory(JavaDir); + + // download java sdk package + using var webClient = new MultiFileAsynchronousDownloadClient(progress, 1); + if (!webClient.DownloadFileAsync(JavaSdkUri, JavaSdkDownloadPath, out var exception)) + throw new Exception( + @"Failed to download java sdk package", exception); + + // unzip java sdk package + using var javaSdkZip = ZipFile.Read(JavaSdkDownloadPath); + javaSdkZip.ExtractAll(JavaDir); + SetJavaExecutablePath(); + } + + if (!isCarafeValid) + { + // clean carafe dir + CarafeDirInfo.Delete(true); + Directory.CreateDirectory(CarafeDir); + + // download carafe jar package + using var webClient = new MultiFileAsynchronousDownloadClient(progress, 1); + if (!webClient.DownloadFileAsync(CarafeJarZipDownloadUrl, CarafeJarZipDownloadPath, out var exception)) + throw new Exception( + @"Failed to download carafe jar package", exception); + + // unzip carafe jar package + using var carafeJarZip = ZipFile.Read(CarafeJarZipDownloadPath); + carafeJarZip.ExtractAll(CarafeDir); + } + + + progress.UpdateProgress(progressStatus = progressStatus + .ChangePercentComplete(100)); + } + + private bool ValidateJava() + { + var dirs = Directory.GetDirectories(JavaDir); + if (dirs.Length != 1) + { + return false; + } + var javaSdkDir = dirs[0]; + var javaExecutablePath = Path.Combine(javaSdkDir, BIN, JAVA_EXECUTABLE); + if (!File.Exists(javaExecutablePath)) + { + return false; + } + JavaExecutablePath = javaExecutablePath; + return true; + } + + private bool ValidateCarafe() + { + if (!File.Exists(CarafeJarFilePath)) + { + return false; + } + return true; + } + + private void SetJavaExecutablePath() + { + var dirs = Directory.GetDirectories(JavaDir); + Assume.IsTrue(dirs.Length.Equals(1), @"Java directory has more than one java SDKs"); + var javaSdkDir = dirs[0]; + JavaExecutablePath = Path.Combine(javaSdkDir, BIN, JAVA_EXECUTABLE); + } + + private void ExecuteCarafe(IProgressMonitor progress, ref IProgressStatus progressStatus) + { + progress.UpdateProgress(progressStatus = progressStatus + .ChangeMessage(@"Executing carafe") + .ChangePercentComplete(0)); + + + // compose carafe cmd command arguments to build library + var args = new StringBuilder(); + + // add activate python virtual env command + args.Append(PythonVirtualEnvironmentActivateScriptPath); + args.Append(SPACE); + args.Append(CONDITIONAL_CMD_PROCEEDING_SYMBOL); + args.Append(SPACE); + + // add java carafe command + args.Append(JavaExecutablePath); + args.Append(SPACE); + + // add carafe args + foreach (var arg in CommandArguments) + { + args.Append(arg).Append(SPACE); + } + + // execute command + var pr = new ProcessRunner(); + var psi = new ProcessStartInfo(CMD_EXECUTABLE) + { + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true + }; + try + { + pr.Run(psi, args.ToString(), progress, ref progressStatus, ProcessPriorityClass.BelowNormal, true); + } + catch (Exception ex) + { + // TODO(xgwang): update this exception to an Carafe specific one + throw new Exception(@"Failed to build library by executing carafe", ex); + } + + progress.UpdateProgress(progressStatus = progressStatus + .ChangePercentComplete(100)); + } + + private void ImportSpectralLibrary(IProgressMonitor progress, ref IProgressStatus progressStatus) + { + progress.UpdateProgress(progressStatus = progressStatus + .ChangeMessage(@"Importing spectral library") + .ChangePercentComplete(0)); + + var source = CarafeOutputLibraryFilePath; + var dest = LibrarySpec.FilePath; + try + { + File.Copy(source, dest, true); + } + catch (Exception ex) + { + // TODO(xgwang): update this exception to an Carafe specific one + throw new Exception( + @$"Failed to copy carafe output library file from {source} to {dest}", ex); + } + + progress.UpdateProgress(progressStatus = progressStatus + .ChangePercentComplete(100)); + } + + } +} + diff --git a/pwiz_tools/Skyline/Model/Koina/KoinaLibraryBuilder.cs b/pwiz_tools/Skyline/Model/Koina/KoinaLibraryBuilder.cs index b5de66e690..69f51a7988 100644 --- a/pwiz_tools/Skyline/Model/Koina/KoinaLibraryBuilder.cs +++ b/pwiz_tools/Skyline/Model/Koina/KoinaLibraryBuilder.cs @@ -23,6 +23,7 @@ using System.Linq; using System.Threading; using pwiz.Common.SystemUtil; +using pwiz.Skyline.Model.AlphaPeptDeep; using pwiz.Skyline.Model.Irt; using pwiz.Skyline.Model.Koina.Communication; using pwiz.Skyline.Model.Koina.Models; @@ -44,6 +45,10 @@ public class KoinaLibraryBuilder : IiRTCapableLibraryBuilder private readonly IList _precursors; private readonly int _nce; + public SrmDocument Document { get => _document; } + + public string ToolName { get; } + public KoinaLibraryBuilder(SrmDocument doc, string name, string outPath, Func replaceLibrary, IrtStandard irtStandard, IList peptides, IList precursors, int nce) { @@ -57,6 +62,8 @@ public KoinaLibraryBuilder(SrmDocument doc, string name, string outPath, Func ReadStandardPeptides(IrtStandard standard, int? nce) { var peps = standard.GetDocument().Peptides.ToList(); @@ -208,5 +220,8 @@ public string BuildOutput { get { return null; } } + + public string BuilderLibraryPath => BuildOutput; + } } diff --git a/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.designer.cs b/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.designer.cs index 841a9b6e66..c8a3e62ccb 100644 --- a/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.designer.cs +++ b/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -19,7 +19,7 @@ namespace pwiz.Skyline.Model.Koina.Models { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class ModelsResources { @@ -60,13 +60,58 @@ internal ModelsResources() { } } + /// + /// Looks up a localized string similar to Building library.... + /// + public static string BuildingPrecursorTable_Building_library { + get { + return ResourceManager.GetString("BuildingPrecursorTable_Building_library", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Accession: {0}, Name: {1}, Comment: {2}. + /// + public static string BuildPrecursorTable_ModificationType { + get { + return ResourceManager.GetString("BuildPrecursorTable_ModificationType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Warning: Peptide {0} has Modification with UNIMOD Accession {1}, which is not unique, Skyline will use Modification {2} in this case.. + /// + public static string BuildPrecursorTable_NonUnique_ModificationAccession { + get { + return ResourceManager.GetString("BuildPrecursorTable_NonUnique_ModificationAccession", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Warning: Peptide {0} has Modification {1} with unimod ID of UNIMOD:{2}, which is not yet supported by {2} LibraryBuilder. This peptide will be skipped!. + /// + public static string BuildPrecursorTable_Unimod_UnsupportedModification { + get { + return ResourceManager.GetString("BuildPrecursorTable_Unimod_UnsupportedModification", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Warning: Peptide {0} has Modification {1}, which is missing unimod ID and unknown to {2} LibraryBuilder. This peptide will be skipped!. + /// + public static string BuildPrecursorTable_UnsupportedModification { + get { + return ResourceManager.GetString("BuildPrecursorTable_UnsupportedModification", resourceCulture); + } + } + /// /// Looks up a localized string similar to Exporting Koina spectra to BiblioSpec library. /// public static string KoinaHelpers_ExportKoinaSpectraToBlib_Exporting_Koina_spectra_to_BiblioSpec_library { get { - return ResourceManager.GetString("KoinaHelpers_ExportKoinaSpectraToBlib_Exporting_Koina_spectra_to_BiblioSpec_li" + - "brary", resourceCulture); + return ResourceManager.GetString("KoinaHelpers_ExportKoinaSpectraToBlib_Exporting_Koina_spectra_to_BiblioSpec_libra" + + "ry", resourceCulture); } } diff --git a/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.ja.resx b/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.ja.resx index 7b40840c06..f0215fb9ef 100644 --- a/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.ja.resx +++ b/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.ja.resx @@ -117,7 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - KoinaスペクトルのBiblioSpecライブラリへのエクスポート @@ -130,4 +129,5 @@ Koinaでは電荷{0}に対応していません。対応最大電荷は{1}です。 + \ No newline at end of file diff --git a/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.resx b/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.resx index ba7df1cd9e..40c95c35f0 100644 --- a/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.resx +++ b/pwiz_tools/Skyline/Model/Koina/Models/ModelsResources.resx @@ -117,7 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Exporting Koina spectra to BiblioSpec library @@ -130,4 +129,19 @@ The charge {0} is not supported by Koina. The maximum supported charge is {1}. - \ No newline at end of file + + Warning: Peptide {0} has Modification {1}, which is missing unimod ID and unknown to {2} LibraryBuilder. This peptide will be skipped! + + + Warning: Peptide {0} has Modification {1} with unimod ID of UNIMOD:{2}, which is not yet supported by {2} LibraryBuilder. This peptide will be skipped! + + + Warning: Peptide {0} has Modification with UNIMOD Accession {1}, which is not unique, Skyline will use Modification {2} in this case. + + + Accession: {0}, Name: {1}, Comment: {2} + + + Building library... + + diff --git a/pwiz_tools/Skyline/Model/Lib/BiblioSpecLiteBuilder.cs b/pwiz_tools/Skyline/Model/Lib/BiblioSpecLiteBuilder.cs index 2d6f3c5a64..1b06deefbd 100644 --- a/pwiz_tools/Skyline/Model/Lib/BiblioSpecLiteBuilder.cs +++ b/pwiz_tools/Skyline/Model/Lib/BiblioSpecLiteBuilder.cs @@ -26,6 +26,7 @@ using System.Text.RegularExpressions; using pwiz.BiblioSpec; using pwiz.Common.SystemUtil; +using pwiz.Skyline.Model.AlphaPeptDeep; using pwiz.Skyline.Model.Irt; using pwiz.Skyline.Model.Results; using pwiz.Skyline.Properties; @@ -73,18 +74,28 @@ public sealed class BiblioSpecLiteBuilder : IiRTCapableLibraryBuilder private ReadOnlyCollection _inputFiles; + public SrmDocument Document { get; } + public string ToolName { get; } + public BiblioSpecLiteBuilder(string name, string outputPath, IList inputFiles, IList targetSequences = null, bool useExplicitPeakBounds = true) { LibrarySpec = new BiblioSpecLiteSpec(name, outputPath, useExplicitPeakBounds); InputFiles = inputFiles; + LibraryHelper = null; + Document = null; + ToolName = @"bibliospeclite"; // FUTURE(bspratt) small molecule workflows if (targetSequences != null) TargetSequences = targetSequences.Where(t => t.IsProteomic).Select(t => t.Sequence).ToList(); } - public LibrarySpec LibrarySpec { get; private set; } + public string ProductLibraryPath() + { + return ""; + } + public string OutputPath { get { return LibrarySpec.FilePath; } } public LibraryBuildAction Action { get; set; } @@ -121,6 +132,9 @@ public string AmbiguousMatchesMessage public string BuildCommandArgs { get { return _buildCommandArgs; } } public string BuildOutput { get { return _buildOutput; } } + public string BuilderLibraryPath => BuildOutput; + + private string[] _ambiguousMatches; private string _buildCommandArgs; private string _buildOutput; @@ -233,6 +247,8 @@ public bool BuildLibrary(IProgressMonitor progress) return true; } + public LibraryHelper LibraryHelper { get; } + public static bool HasEmbeddedSpectra(string libraryInputFilepath) { return libraryInputFilepath.EndsWith(EXT_MAX_QUANT); diff --git a/pwiz_tools/Skyline/Model/Lib/Library.cs b/pwiz_tools/Skyline/Model/Lib/Library.cs index 73f2a82acb..1209d80a2c 100644 --- a/pwiz_tools/Skyline/Model/Lib/Library.cs +++ b/pwiz_tools/Skyline/Model/Lib/Library.cs @@ -31,6 +31,7 @@ using pwiz.Common.Chemistry; using pwiz.Common.Collections; using pwiz.Common.SystemUtil; +using pwiz.Skyline.Model.AlphaPeptDeep; using pwiz.Skyline.Model.AuditLog; using pwiz.Skyline.Model.Crosslinking; using pwiz.Skyline.Model.DocSettings; @@ -598,11 +599,18 @@ public interface ILibraryBuilder /// /// Sink for progress updates, and source of user cancel status bool BuildLibrary(IProgressMonitor progress); + + LibraryHelper LibraryHelper { get; } + SrmDocument Document { get; } + + string ToolName { get; } /// /// A referencing the library to be built. /// LibrarySpec LibrarySpec { get; } + + string BuilderLibraryPath { get; } } public enum LibraryRedundancy { best, all, all_redundant } diff --git a/pwiz_tools/Skyline/Model/Lib/Midas/MidasBlibBuilder.cs b/pwiz_tools/Skyline/Model/Lib/Midas/MidasBlibBuilder.cs index a9fb77049a..a030102894 100644 --- a/pwiz_tools/Skyline/Model/Lib/Midas/MidasBlibBuilder.cs +++ b/pwiz_tools/Skyline/Model/Lib/Midas/MidasBlibBuilder.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Linq; using pwiz.Common.SystemUtil; +using pwiz.Skyline.Model.AlphaPeptDeep; using pwiz.Skyline.Model.DocSettings; using pwiz.Skyline.Model.Lib.BlibData; @@ -33,12 +34,16 @@ public class MidasBlibBuilder : ILibraryBuilder private readonly SrmDocument _doc; private readonly MidasLibrary _library; private readonly LibrarySpec _libSpec; - + public string BuilderLibraryPath => _library.FilePath; + public SrmDocument Document { get => _doc; } + public string ToolName { get; } public MidasBlibBuilder(SrmDocument doc, MidasLibrary library, string libName, string blibPath) { _doc = doc; _library = library; + LibraryHelper = null; _libSpec = new BiblioSpecLiteSpec(libName, blibPath); + ToolName = @"midas"; } public bool BuildLibrary(IProgressMonitor progress) @@ -90,6 +95,13 @@ public bool BuildLibrary(IProgressMonitor progress) } } + public LibraryHelper LibraryHelper { get; } + + public string ProductLibraryPath() + { + return _library.FilePath; + + } public LibrarySpec LibrarySpec { get { return _libSpec; } } } } diff --git a/pwiz_tools/Skyline/Model/ModelResources.designer.cs b/pwiz_tools/Skyline/Model/ModelResources.designer.cs index 4bc979e06d..3c58d1844c 100644 --- a/pwiz_tools/Skyline/Model/ModelResources.designer.cs +++ b/pwiz_tools/Skyline/Model/ModelResources.designer.cs @@ -212,6 +212,18 @@ public static string AbstractModificationMatcher_UninterpretedMods_The_following } } + /// + /// Looks up a localized string similar to Warning: The predicted library will be incomplete, due to the following unknown modifications detected: + /// {0} + /// + /// Would you like to continue building this predicted library?. + /// + public static string Alphapeptdeep_Warn_unknown_modification { + get { + return ResourceManager.GetString("Alphapeptdeep_Warn_unknown_modification", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid amino acid '{0}' found in the value '{1}'.. /// @@ -1198,6 +1210,41 @@ public static string MProphetResultsHandler_ChangePeaks_Adjusting_peak_boundarie } } + /// + /// Looks up a localized string similar to + ///@echo off + ///setlocal enabledelayedexpansion + /// + ///:: Set paths and URIs (you need to define these) + ///set "CudaVersion={{0}}" + ///set "CudaInstaller=cuda_%CudaVersion%_windows_network.exe" + ///set "CudaDownloadUri=https://developer.download.nvidia.com/compute/cuda/%CudaVersion%/network_installers/%CudaInstaller%" + ///set "CudaDownloadPath=%USERPROFILE%\Downloads\%CudaInstaller%" + /// + ///set "CuDNNVersion={{1}}" + ///set "CuDNNArchive=cudnn-windows-x86_64-%CuDNNVersion%-archive" + ///set "CuDNNDownloadUri=https://developer.download.n [rest of string was truncated]";. + /// + public static string NvidiaInstaller_Batch_script { + get { + return ResourceManager.GetString("NvidiaInstaller_Batch_script", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Administrative privileges are required to install this feature the first time. Consult with your system administrator if you don't have the necessary permissions and ask them to run the following script: + /// {0} + /// + /// Would you like to continue? + /// + /// Hint: Clicking 'OK' activates installation of Nvidia libraries.... + /// + public static string NvidiaInstaller_Requesting_Administrator_elevation { + get { + return ResourceManager.GetString("NvidiaInstaller_Requesting_Administrator_elevation", resourceCulture); + } + } + /// /// Looks up a localized string similar to The precursor m/z {0} is not measureable with your current instrument settings.. /// diff --git a/pwiz_tools/Skyline/Model/ModelResources.resx b/pwiz_tools/Skyline/Model/ModelResources.resx index 84577bc2a8..1da1b39400 100644 --- a/pwiz_tools/Skyline/Model/ModelResources.resx +++ b/pwiz_tools/Skyline/Model/ModelResources.resx @@ -466,6 +466,78 @@ Adjusting peak boundaries + + Warning: The predicted library will be incomplete, due to the following unknown modifications detected: + {0} + + Would you like to continue building this predicted library? + + + Administrative privileges are required to install this feature the first time. Consult with your system administrator if you don't have the necessary permissions and ask them to run the following script: + {0} + + Would you like to continue? + + Hint: Clicking 'OK' activates installation of Nvidia libraries... + + + NUL +mkdir "%CuDNNInstallDir%\include" 2>NUL +mkdir "%CuDNNInstallDir%\lib" 2>NUL + +:: Copy files (you'll need to adjust permissions) +xcopy "%CuDNNVersionDir%\%CuDNNArchive%\bin\cudnn*.dll" "%CuDNNInstallDir%\bin" /Y /I +xcopy "%CuDNNVersionDir%\%CuDNNArchive%\include\cudnn*.h" "%CuDNNInstallDir%\include" /Y /I +xcopy "%CuDNNVersionDir%\%CuDNNArchive%\lib\x64\cudnn*.lib" "%CuDNNInstallDir%\lib" /Y /I + +set "PS_COMMAND=$oldPath = $env:Path -split ';' | Where-Object { $_ -ne '%CuDNNInstallDir%\bin' }; $env:Path = $oldPath -join ';' ; $env:Path = $env:Path + ';%CuDNNInstallDir%\bin' ; [Environment]::SetEnvironmentVariable('Path', $env:Path, [System.EnvironmentVariableTarget]::Machine)" + +:: Run PowerShell to set PATH +powershell -NoLogo -NoProfile -Command "%PS_COMMAND%" + +REM echo Installation complete! + +endlocal +pause +]]> + + The precursor m/z {0} is not measureable with your current instrument settings. diff --git a/pwiz_tools/Skyline/Model/ModifiedSequence.cs b/pwiz_tools/Skyline/Model/ModifiedSequence.cs index 64f8b9a046..fa43bc1d16 100644 --- a/pwiz_tools/Skyline/Model/ModifiedSequence.cs +++ b/pwiz_tools/Skyline/Model/ModifiedSequence.cs @@ -31,7 +31,7 @@ namespace pwiz.Skyline.Model { /// - /// Holds an unmodified sequence and a list of explicit modifications (i.e. StatidMod and AminoAcid index). + /// Holds an unmodified sequence and a list of explicit modifications (i.e. StaticMod and AminoAcid index). /// This enables the sequence to be formatted in a number of ways, including mass deltas, or modification names. /// public class ModifiedSequence : ProteomicSequence @@ -211,8 +211,6 @@ private string FormatSelf(Func, string> modFormatter) return result.ToString(); } - - public override string ToString() { return FormatDefault(); @@ -369,6 +367,7 @@ public Modification ChangeLinkedPeptideSequence(ModifiedSequence linkedPeptideSe public string ShortName { get { return StaticMod.ShortName; } } public ParsedMolecule Formula { get; private set; } public int? UnimodId { get { return StaticMod.UnimodId; } } + public string UnimodIdAA { get { return StaticMod.UnimodId + @":" + StaticMod.Name; } } public double MonoisotopicMass { get; private set; } public double AverageMass { get; private set; } diff --git a/pwiz_tools/Skyline/Model/Tools/PythonInstaller.cs b/pwiz_tools/Skyline/Model/Tools/PythonInstaller.cs new file mode 100644 index 0000000000..f7c10f350a --- /dev/null +++ b/pwiz_tools/Skyline/Model/Tools/PythonInstaller.cs @@ -0,0 +1,1834 @@ +/* + * Author: David Shteynberg , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2025 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Management; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Principal; +using System.Text; +using System.Threading; +using Ionic.Zip; +using JetBrains.Annotations; +using Microsoft.Win32; +using OneOf; +using pwiz.Common.Collections; +using pwiz.Common.SystemUtil; +using pwiz.Skyline.Util; +using pwiz.Skyline.Util.Extensions; + +[assembly: InternalsVisibleTo("TestFunctional")] +[assembly: InternalsVisibleTo("TestUtil")] + + +namespace pwiz.Skyline.Model.Tools +{ + public class PythonInstaller + { + private const string BOOTSTRAP_PYPA_URL = @"https://bootstrap.pypa.io/"; + private const string CD = @"cd"; + private const string CMD_ESCAPE_SYMBOL = TextUtil.CARET; + internal const string CMD_PROCEEDING_SYMBOL = TextUtil.AMPERSAND; + private const string CONDITIONAL_CMD_PROCEEDING_SYMBOL = TextUtil.AMPERSAND + TextUtil.AMPERSAND; + private const string DOT_ZIP = @".zip"; + private const string DOT_EXE = @".exe"; + internal const string ECHO = @"echo"; + private const string EMBED_LOWER_CASE = @"embed"; + private const string EQUALS = TextUtil.EQUAL + TextUtil.EQUAL; + private const string FORWARD_SLASH = TextUtil.FORWARD_SLASH; + private const string GET_PIP_SCRIPT_FILE_NAME = @"get-pip.py"; + private const string HYPHEN = TextUtil.HYPHEN; + private const string INSTALL = @"install"; + private const string PIP = @"pip"; + private const string PYTHON_EXECUTABLE = @"python.exe"; + private const string PYTHON_FTP_SERVER_URL = @"https://www.python.org/ftp/python/"; + private const string PYTHON_LOWER_CASE = @"python"; + private const string PYTHON_MODULE_OPTION = @"-m"; + private const string SCRIPTS = @"Scripts"; + private const string PIP_EXE = @"pip.exe"; + private const string SPACE = TextUtil.SPACE; + private const string VIRTUALENV = @"virtualenv"; + private const string GIT = @"git"; + internal const string REG_ADD_COMMAND = @"reg add"; + internal const string REG_FILESYSTEM_KEY = @"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem"; + internal const string REG_LONGPATHS_ENABLED = @"LongPathsEnabled"; + internal const string REG_LONGPATH_TYPE = @"/t REG_DWORD"; + internal const string REG_LONGPATH_VALUE = @"/d 0x00000001"; + internal const string REG_LONGPATH_ZERO = @"/d 0x00000000"; + internal const string REG_LONGPATH_FORCE = @"/f"; + internal string REG_LONGPATH_NAME = $@"/v {REG_LONGPATHS_ENABLED}"; + + private static string CUDA_VERSION = @"12.6.3"; + private static string CUDNN_VERSION = @"9.6.0.74_cuda12"; + //private static readonly string CUDA_INSTALLER_URL = $@"https://developer.download.nvidia.com/compute/cuda/{CUDA_VERSION}/local_installers/"; + private static readonly string CUDA_INSTALLER_URL = $@"https://developer.download.nvidia.com/compute/cuda/{CUDA_VERSION}/network_installers/"; + + private static readonly string CUDNN_INSTALLER_URL = + @"https://developer.download.nvidia.com/compute/cudnn/redist/cudnn/windows-x86_64/"; + + private string CUDNN_INSTALLER = $@"cudnn-windows-x86_64-{CUDNN_VERSION}-archive.zip"; + private string CudaVersionDir => Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), @"cuda", CUDA_VERSION); + public string CudaInstallerDownloadPath => Path.Combine(CudaVersionDir, CUDA_INSTALLER); + + public string CuDNNVersionDir => Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), @"cudnn", CUDNN_VERSION); + + public string CuDNNArchive = $@"cudnn-windows-x86_64-{CUDNN_VERSION}-archive"; + public static string CuDNNInstallDir => @"C:\Program Files\NVIDIA\CUDNN\v9.x"; + public string CuDNNInstallerDownloadPath => Path.Combine(CuDNNVersionDir, CUDNN_INSTALLER); + + //private string CUDA_INSTALLER = $@"cuda_{CUDA_VERSION_FULL}_windows.exe"; + private string CUDA_INSTALLER = $@"cuda_{CUDA_VERSION}_windows_network.exe"; + public Uri CudaDownloadUri => new Uri(CUDA_INSTALLER_URL+CUDA_INSTALLER); + public string CudaDownloadPath => Path.Combine(CudaVersionDir, CudaInstallerDownloadPath); + public Uri CuDNNDownloadUri => new Uri(CUDNN_INSTALLER_URL + CUDNN_INSTALLER); + public string CuDNNDownloadPath => Path.Combine(CuDNNVersionDir, CuDNNInstallerDownloadPath); + + public static string InstallNvidiaLibrariesBat => + Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), "InstallNvidiaLibraries.bat"); + public static string GetInstallNvidiaLibrariesBat() + { + return InstallNvidiaLibrariesBat; + } + + private bool? _NvidiaGpuAvailable; + public bool? NvidiaGpuAvailable + { + get + { + if (SimulatedInstallationState == eSimulatedInstallationState.NONVIDIASOFT) return true; + return _NvidiaGpuAvailable; + } + + internal set + { + _NvidiaGpuAvailable = value; + } + } + + public int NumTotalTasks { get; set; } + public int NumCompletedTasks { get; set; } + public string PythonVersion { get; } + public List PythonPackages { get; } + public string PythonEmbeddablePackageFileName => PythonEmbeddablePackageFileBaseName + DOT_ZIP; + public Uri PythonEmbeddablePackageUri => new Uri(PYTHON_FTP_SERVER_URL + PythonVersion + FORWARD_SLASH + PythonEmbeddablePackageFileName); + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public string PythonEmbeddablePackageDownloadPath => Path.Combine(PythonVersionDir, PythonEmbeddablePackageFileName); + public string PythonEmbeddablePackageExtractDir => Path.Combine(PythonVersionDir, PythonEmbeddablePackageFileBaseName); + public Uri GetPipScriptDownloadUri => new Uri(BOOTSTRAP_PYPA_URL + GET_PIP_SCRIPT_FILE_NAME); + public string GetPipScriptDownloadPath => Path.Combine(PythonVersionDir, GET_PIP_SCRIPT_FILE_NAME); + public string BasePythonExecutablePath => Path.Combine(PythonEmbeddablePackageExtractDir, PYTHON_EXECUTABLE); + public string VirtualEnvironmentName { get; } + public string VirtualEnvironmentDir => Path.Combine(PythonVersionDir, VirtualEnvironmentName); + public string VirtualEnvironmentPythonExecutablePath => Path.Combine(VirtualEnvironmentDir, SCRIPTS, PYTHON_EXECUTABLE); + public List PendingTasks { get; set; } + + #region Functional testing support + public enum eSimulatedInstallationState + { + NONE, // Normal tests systems will have registry set suitably + NAIVE, // Simulate system where Python is not installed and Nvidia Hardware not present + NONVIDIAHARD, // Simulate Nvidia Hardware not present + NONVIDIASOFT // Simulate Python is installed, Nvidia Hardware present, Nvidia Software not present + } + public static eSimulatedInstallationState SimulatedInstallationState { get; set; } + public IAsynchronousDownloadClient TestDownloadClient { get; set; } + public IAsynchronousDownloadClient TestPipDownloadClient { get; set; } + public ISkylineProcessRunnerWrapper TestPipeSkylineProcessRunner { get; set; } + /// + /// For testing purpose only. Setting this property will bypass the TaskValidator + /// + public List TestPythonVirtualEnvironmentTaskNames { get; set; } + #endregion + public string PythonVersionDir => Path.Combine(PythonRootDir, PythonVersion); + + private string PythonEmbeddablePackageFileBaseName + { + get + { + var architecture = PythonInstallerUtil.GetPythonPackageArchitectureSubstring( + RuntimeInformation.ProcessArchitecture); + var fileBaseName = string.Join(HYPHEN, new[] { PYTHON_LOWER_CASE, PythonVersion, EMBED_LOWER_CASE, architecture }); + return fileBaseName; + } + } + private string PythonRootDir { get; } = PythonInstallerUtil.PythonRootDir; + internal TextWriter Writer { get; } + private IPythonInstallerTaskValidator TaskValidator { get; } + + public bool HavePythonTasks { get; private set;} + public bool HaveNvidiaTasks { get; private set; } + + + public static bool CudaLibraryInstalled() + { + if (SimulatedInstallationState == eSimulatedInstallationState.NONVIDIAHARD || + SimulatedInstallationState == eSimulatedInstallationState.NONVIDIASOFT || + SimulatedInstallationState == eSimulatedInstallationState.NAIVE) + return false; + string cudaPath = Environment.GetEnvironmentVariable(@"CUDA_PATH"); + return !string.IsNullOrEmpty(cudaPath); + } + + public static bool NvidiaLibrariesInstalled() + { + if (SimulatedInstallationState == eSimulatedInstallationState.NONVIDIAHARD || + SimulatedInstallationState == eSimulatedInstallationState.NONVIDIASOFT || + SimulatedInstallationState == eSimulatedInstallationState.NAIVE) + return false; + + bool cudaYes = CudaLibraryInstalled(); + bool? cudnnYes = CuDNNLibraryInstalled(); + + return cudaYes && cudnnYes != false; + } + + public static bool? CuDNNLibraryInstalled(bool longValidate = false) + { + if (SimulatedInstallationState == eSimulatedInstallationState.NONVIDIAHARD || + SimulatedInstallationState == eSimulatedInstallationState.NONVIDIASOFT || + SimulatedInstallationState == eSimulatedInstallationState.NAIVE) + return false; + + string targetDirectory = CuDNNInstallDir + @"\bin"; + + if (!Directory.Exists(targetDirectory)) return false; + + string newPath = Environment.GetEnvironmentVariable(@"PATH"); + + if (newPath != null && !newPath.Contains(targetDirectory)) + { + newPath += TextUtil.SEMICOLON + targetDirectory; + Environment.SetEnvironmentVariable(@"PATH", newPath, EnvironmentVariableTarget.User); + } + + + if (longValidate) + { + var computeHash = PythonInstallerUtil.IsRunningElevated() + ? PythonInstallerUtil.GetDirectoryHash(targetDirectory) + : null; + bool? isValid = PythonInstallerUtil.IsSignatureValid(targetDirectory, computeHash); + if (isValid == true || isValid == null) + { + targetDirectory = CuDNNInstallDir + @"\include"; + if (!Directory.Exists(targetDirectory)) return false; + + computeHash = PythonInstallerUtil.IsRunningElevated() + ? PythonInstallerUtil.GetDirectoryHash(targetDirectory) + : null; + isValid = PythonInstallerUtil.IsSignatureValid(targetDirectory, computeHash); + if (isValid == true || isValid == null) + { + targetDirectory = CuDNNInstallDir + @"\lib"; + if (!Directory.Exists(targetDirectory)) return false; + + computeHash = PythonInstallerUtil.IsRunningElevated() + ? PythonInstallerUtil.GetDirectoryHash(targetDirectory) + : null; + isValid = PythonInstallerUtil.IsSignatureValid(targetDirectory, computeHash); + } + + } + return isValid; + } + else + { + bool isValid = Directory.Exists(targetDirectory); //PythonInstallerUtil.IsSignedFileOrDirectory(targetDirectory); + if (isValid) + { + targetDirectory = CuDNNInstallDir + @"\include"; + if (!Directory.Exists(targetDirectory)) return false; + + //isValid = PythonInstallerUtil.IsSignedFileOrDirectory(targetDirectory); + //if (isValid) + //{ + targetDirectory = CuDNNInstallDir + @"\lib"; + if (!Directory.Exists(targetDirectory)) return false; + + //isValid = PythonInstallerUtil.IsSignedFileOrDirectory(targetDirectory); + //} + + } + return isValid; + + } + + } + + public void WriteInstallNvidiaBatScript() + { + FileEx.SafeDelete(InstallNvidiaLibrariesBat); + string resourceString = ModelResources.NvidiaInstaller_Batch_script; + resourceString = resourceString.Replace(@"{{0}}", CUDA_VERSION); + resourceString = resourceString.Replace(@"{{1}}", CUDNN_VERSION); + File.WriteAllText(InstallNvidiaLibrariesBat, resourceString); + } + + public static bool IsRunningElevated() + { + // Get current user's Windows identity + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + + // Convert identity to WindowsPrincipal to check for roles + WindowsPrincipal principal = new WindowsPrincipal(identity); + + // Check if the current user is in the Administrators role + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + /// + /// False means NONVIDIA hardware, true means NVIDIA hardware, null means don't know + /// + /// + public static bool? TestForNvidiaGPU() + { + if (SimulatedInstallationState == eSimulatedInstallationState.NONVIDIAHARD || SimulatedInstallationState == eSimulatedInstallationState.NAIVE) + return false; + if (SimulatedInstallationState == eSimulatedInstallationState.NONVIDIASOFT) //Also assume we have NVIDIA card when we assume we don't have NVIDIA software + return true; + + bool? nvidiaGpu = null; + try + { + // Query for video controllers using WMI + ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"SELECT * FROM Win32_VideoController"); + + foreach (ManagementObject obj in searcher.Get()) + { + // GPU information + nvidiaGpu = obj[@"Name"].ToString().StartsWith(@"NVIDIA"); + if (nvidiaGpu != false) break; + } + } + catch (ManagementException e) + { + Console.WriteLine(@"An error occurred while querying for WMI data: " + e.Message); + } + return nvidiaGpu; + } + + public PythonInstaller(ProgramPathContainer pythonPathContainer, IEnumerable packages, + TextWriter writer, IPythonInstallerTaskValidator taskValidator, string virtualEnvironmentName) + { + // SimulatedInstallationState = eSimulatedInstallationState.NONE; + PythonVersion = pythonPathContainer.ProgramVersion; + Writer = writer; + TaskValidator = taskValidator; + VirtualEnvironmentName = virtualEnvironmentName; + PendingTasks = new List(); + Directory.CreateDirectory(PythonRootDir); + Directory.CreateDirectory(PythonVersionDir); + + NvidiaGpuAvailable = TestForNvidiaGPU(); + PythonPackages = packages.ToList(); + + if (NvidiaGpuAvailable == true) + { + PythonPackages.Add(new PythonPackage { Name = @"wheel", Version = null }); + PythonPackages.Add(new PythonPackage { Name = @"nvidia-cudnn-cu12", Version = null }); + PythonPackages.Add(new PythonPackage { Name = @"torch --extra-index-url https://download.pytorch.org/whl/cu118 --upgrade", Version = null }); + } + } + + + public bool IsNvidiaEnvironmentReady(List abortedTasks = null) + { + if (SimulatedInstallationState == eSimulatedInstallationState.NONVIDIAHARD) + return true; + + if (abortedTasks == null && SimulatedInstallationState == eSimulatedInstallationState.NONVIDIASOFT) + return false; + + var tasks = PendingTasks.IsNullOrEmpty() ? ValidatePythonVirtualEnvironment() : PendingTasks; + + if (abortedTasks != null && abortedTasks.Count > 0) tasks = abortedTasks; + + if (NumTotalTasks == NumCompletedTasks && NumCompletedTasks > 0) + return true; + + return !tasks.Any(task => + (task.Name == PythonTaskName.setup_nvidia_libraries + || task.Name == PythonTaskName.download_cuda_library + || task.Name == PythonTaskName.install_cuda_library + || task.Name == PythonTaskName.download_cudnn_library + || task.Name == PythonTaskName.install_cudnn_library)); + } + + public bool IsPythonVirtualEnvironmentReady(List abortedTasks = null) + { + if (SimulatedInstallationState == eSimulatedInstallationState.NAIVE) + return false; + if (abortedTasks == null && SimulatedInstallationState == eSimulatedInstallationState.NONVIDIASOFT) + return true; + + var tasks = PendingTasks.IsNullOrEmpty() ? ValidatePythonVirtualEnvironment() : PendingTasks; + + if (abortedTasks != null && abortedTasks.Count > 0) tasks = abortedTasks; + + if (NumTotalTasks == NumCompletedTasks && NumCompletedTasks > 0) + return true; + + return !tasks.Any(task => + (task.Name != PythonTaskName.setup_nvidia_libraries + && task.Name != PythonTaskName.download_cuda_library + && task.Name != PythonTaskName.install_cuda_library + && task.Name != PythonTaskName.download_cudnn_library + && task.Name != PythonTaskName.install_cudnn_library)); + } + + public void ClearPendingTasks() + { + PendingTasks.Clear(); + } + + public void CheckPendingTasks() + { + var tasks = PendingTasks; + HavePythonTasks = false; + HaveNvidiaTasks = false; + + if (tasks.Any(task => + (task.Name != PythonTaskName.setup_nvidia_libraries + && task.Name != PythonTaskName.download_cuda_library + && task.Name != PythonTaskName.install_cuda_library + && task.Name != PythonTaskName.download_cudnn_library + && task.Name != PythonTaskName.install_cudnn_library))) + { + HavePythonTasks = true; + } + + if (tasks.Any(task => + (task.Name == PythonTaskName.setup_nvidia_libraries + || task.Name == PythonTaskName.download_cuda_library + || task.Name == PythonTaskName.install_cuda_library + || task.Name == PythonTaskName.download_cudnn_library + || task.Name == PythonTaskName.install_cudnn_library))) + { + HaveNvidiaTasks = true; + } + } + + + public List ValidatePythonVirtualEnvironment() + { + var tasks = new List(); + if (!TestPythonVirtualEnvironmentTaskNames.IsNullOrEmpty()) + { + foreach (var taskName in TestPythonVirtualEnvironmentTaskNames) + { + tasks.Add(GetPythonTask(taskName)); + return tasks; + } + } + + var taskNodes = PythonInstallerUtil.GetPythonTaskNodes(this.NvidiaGpuAvailable); + var hasSeenFailure = false; + foreach (var taskNode in taskNodes) + { + bool? isTaskValid = TaskValidator.Validate(taskNode.PythonTaskName, this); + if (hasSeenFailure) + { + if ( (isTaskValid == true || isTaskValid == null) && null == taskNode.ParentNodes) { continue; } + + bool havePrerequisite = false; + + if (taskNode.ParentNodes == null) + { + tasks.Add(GetPythonTask(taskNode.PythonTaskName)); + continue; + } + else if (tasks.Count > 0) + { + foreach (var parentTask in taskNode.ParentNodes) + { + if (tasks.Count > 0 && tasks.Where(p => p.Name == parentTask.PythonTaskName).ToArray().Length > 0) + { + havePrerequisite = true; + break; + } + } + + } + + if (!havePrerequisite) { + continue; + } + } + else + { + if (isTaskValid == true || isTaskValid == null) { continue; } + hasSeenFailure = true; + } + + PythonTask nextTask = GetPythonTask(taskNode.PythonTaskName); + if (nextTask != null) tasks.Add(nextTask); + } + PendingTasks = tasks; + return tasks; + } + + private PythonTask GetPythonTask(PythonTaskName pythonTaskName) + { + switch (pythonTaskName) + { + case PythonTaskName.download_python_embeddable_package: + var task1 = new PythonTask(DownloadPythonEmbeddablePackage); + task1.InProgressMessage = ToolsResources.PythonInstaller_GetPythonTask_Downloading_Python_embeddable_package; + task1.FailureMessage = ToolsResources.PythonInstaller_GetPythonTask_Failed_to_download_Python_embeddable_package; + task1.Name = pythonTaskName; + return task1; + case PythonTaskName.unzip_python_embeddable_package: + var task2 = new PythonTask(UnzipPythonEmbeddablePackage); + task2.InProgressMessage = ToolsResources.PythonInstaller_GetPythonTask_Unzipping_Python_embeddable_package; + task2.FailureMessage = ToolsResources.PythonInstaller_GetPythonTask_Failed_to_unzip_Python_embeddable_package; + task2.Name = pythonTaskName; + return task2; + case PythonTaskName.enable_search_path_in_python_embeddable_package: + var task3 = new PythonTask(EnableSearchPathInPythonEmbeddablePackage); + task3.InProgressMessage = ToolsResources.PythonInstaller_GetPythonTask_Enabling_search_path_in_Python_embeddable_package; + task3.FailureMessage = ToolsResources.PythonInstaller_GetPythonTask_Failed_to_enable_search_path_in_Python_embeddable_package; + task3.Name = pythonTaskName; + return task3; + case PythonTaskName.enable_longpaths: + var task4 = new PythonTask((broker) => EnableWindowsLongPaths(broker)); + task4.InProgressMessage = string.Format(ToolsResources.PythonInstaller_GetPythonTask_Enable_Long_Paths_For_Python_packages_in_virtual_environment__0_, VirtualEnvironmentName); + task4.FailureMessage = string.Format(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_enable_long_paths_Python_packages_in_virtual_environment__0_, VirtualEnvironmentName); + task4.Name = pythonTaskName; + return task4; + case PythonTaskName.download_getpip_script: + var task5 = new PythonTask(DownloadGetPipScript); + task5.InProgressMessage = ToolsResources.PythonInstaller_GetPythonTask_Downloading_the_get_pip_py_script; + task5.FailureMessage = ToolsResources.PythonInstaller_GetPythonTask_Failed_to_download_the_get_pip_py_script; + task5.Name = pythonTaskName; + return task5; + case PythonTaskName.run_getpip_script: + var task6 = new PythonTask((broker) => RunGetPipScript(broker)); + task6.InProgressMessage = ToolsResources.PythonInstaller_GetPythonTask_Running_the_get_pip_py_script; + task6.FailureMessage = ToolsResources.PythonInstaller_GetPythonTask_Failed_to_run_the_get_pip_py_script; + task6.Name = pythonTaskName; + return task6; + case PythonTaskName.pip_install_virtualenv: + var virtualEnvPackage = new PythonPackage { Name = VIRTUALENV, Version = null }; + var task7 = new PythonTask((broker) => PipInstall(BasePythonExecutablePath, new[] { virtualEnvPackage }, broker)); + task7.InProgressMessage = string.Format(ToolsResources.PythonInstaller_GetPythonTask_Running_pip_install__0_, VIRTUALENV); + task7.FailureMessage = string.Format(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_run_pip_install__0_, VIRTUALENV); + task7.Name = pythonTaskName; + return task7; + case PythonTaskName.create_virtual_environment: + var task8 = new PythonTask((longWaitBroker) => RunPythonModule( + BasePythonExecutablePath, PythonVersionDir, VIRTUALENV, new[] { VirtualEnvironmentName }, longWaitBroker)); + task8.InProgressMessage = string.Format(ToolsResources.PythonInstaller_GetPythonTask_Creating_virtual_environment__0_, VirtualEnvironmentName); + task8.FailureMessage = string.Format(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_create_virtual_environment__0_, VirtualEnvironmentName); + task8.Name = pythonTaskName; + return task8; + case PythonTaskName.pip_install_packages: + var task9= new PythonTask((longWaitBroker) => PipInstall(VirtualEnvironmentPythonExecutablePath, PythonPackages, longWaitBroker)); + task9.InProgressMessage = string.Format(ToolsResources.PythonInstaller_GetPythonTask_Installing_Python_packages_in_virtual_environment__0_, VirtualEnvironmentName); + task9.FailureMessage = string.Format(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_install_Python_packages_in_virtual_environment__0_, VirtualEnvironmentName); + task9.Name = pythonTaskName; + return task9; + case PythonTaskName.setup_nvidia_libraries: + if (NvidiaGpuAvailable == true) + { + Directory.CreateDirectory(CudaVersionDir); + var task10 = new PythonTask((longWaitBroker) => SetupNvidiaLibraries(longWaitBroker)); + task10.InProgressMessage = ToolsResources.NvidiaInstaller_Setup_Nvidia_Libraries; + task10.FailureMessage = ToolsResources.NvidiaInstaller_Failed_Setup_Nvidia_Libraries; + task10.Name = pythonTaskName; + return task10; + } + else + { + return null; + } + + /* ************************************************************************************************************************************************************ + case PythonTaskName.download_cuda_library: + if (NvidiaGpuAvailable == true) + { + Directory.CreateDirectory(CudaVersionDir); + var task10 = new PythonTask(DownloadCudaLibrary); + task10.InProgressMessage = ToolsResources.PythonInstaller_GetPythonTask_Downloading_Cuda_Installer; + task10.FailureMessage = ToolsResources.PythonInstaller_GetPythonTask_Failed_to_download_Cuda_Installer; + task10.Name = pythonTaskName; + return task10; + } + else + { + return null; + } + case PythonTaskName.install_cuda_library: + if (NvidiaGpuAvailable == true) + { + Directory.CreateDirectory(CudaVersionDir); + var task11 = new PythonTask(InstallCudaLibrary); + task11.InProgressMessage = ToolsResources.PythonInstaller_GetPythonTask_Installing_Cuda; + task11.FailureMessage = ToolsResources.PythonInstaller_GetPythonTask_Failed_to_install_Cuda; + task11.Name = pythonTaskName; + return task11; + } + else + { + return null; + } + case PythonTaskName.download_cudnn_library: + if (NvidiaGpuAvailable == true) + { + Directory.CreateDirectory(CuDNNVersionDir); + var task12 = new PythonTask(DownloadCuDNNLibrary); + task12.InProgressMessage = ToolsResources.PythonInstaller_GetPythonTask_Downloading_CuDNN_Installer; + task12.FailureMessage = ToolsResources.PythonInstaller_GetPythonTask_Failed_to_download_CuDNN_Installer; + task12.Name = pythonTaskName; + return task12; + } + else + { + return null; + } + case PythonTaskName.install_cudnn_library: + if (NvidiaGpuAvailable == true) + { + Directory.CreateDirectory(CuDNNVersionDir); + var task13 = new PythonTask(InstallCuDNNLibrary); + task13.InProgressMessage = ToolsResources.PythonInstaller_GetPythonTask_Installing_CuDNN; + task13.FailureMessage = ToolsResources.PythonInstaller_GetPythonTask_Failed_to_install_CuDNN; + task13.Name = pythonTaskName; + return task13; + } + else + { + return null; + } + **************************************************************************************************************************************************** */ + + default: + throw new PythonInstallerUnsupportedTaskNameException(pythonTaskName); + } + } + + public void EnableWindowsLongPaths(ILongWaitBroker broker = null) + { + var cmdBuilder = new StringBuilder(); + cmdBuilder.Append(REG_ADD_COMMAND) + .Append(SPACE) + .Append(TextUtil.Quote(REG_FILESYSTEM_KEY)) + .Append(SPACE) + .Append(REG_LONGPATH_NAME) + .Append(SPACE) + .Append(REG_LONGPATH_TYPE) + .Append(SPACE) + .Append(REG_LONGPATH_VALUE) + .Append(SPACE) + .Append(REG_LONGPATH_FORCE); + + var cmd = string.Format(ToolsResources.PythonInstaller__0__Running_command____1____2__, ECHO, cmdBuilder, CMD_PROCEEDING_SYMBOL); + cmd += cmdBuilder; + + var processRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + + CancellationToken cancelToken = CancellationToken.None; + if (broker != null) cancelToken = broker.CancellationToken; + + if (processRunner.RunProcess(cmd, true, Writer, true, cancelToken) != 0) + throw new ToolExecutionException(string.Format(ToolsResources.PythonInstaller_Failed_to_execute_command____0__, cmdBuilder)); + + } + public void DisableWindowsLongPaths(ILongWaitBroker broker = null) + { + var cmdBuilder = new StringBuilder(); + cmdBuilder.Append(REG_ADD_COMMAND) + .Append(SPACE) + .Append(TextUtil.Quote(REG_FILESYSTEM_KEY)) + .Append(SPACE) + .Append(REG_LONGPATH_NAME) + .Append(SPACE) + .Append(REG_LONGPATH_TYPE) + .Append(SPACE) + .Append(REG_LONGPATH_ZERO) + .Append(SPACE) + .Append(REG_LONGPATH_FORCE); + + var cmd = string.Format(ToolsResources.PythonInstaller__0__Running_command____1____2__, ECHO, cmdBuilder, CMD_PROCEEDING_SYMBOL); + cmd += cmdBuilder; + + var processRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + + CancellationToken cancelToken = CancellationToken.None; + if (broker != null) cancelToken = broker.CancellationToken; + + + if (processRunner.RunProcess(cmd, true, Writer, true, cancelToken) != 0) + throw new ToolExecutionException(string.Format(ToolsResources.PythonInstaller_Failed_to_execute_command____0__, cmdBuilder)); + + } + private void SetupNvidiaLibraries(ILongWaitBroker broker = null) + { + CancellationToken cancelToken = CancellationToken.None; + var cmdBuilder = new StringBuilder(); + cmdBuilder.Append(InstallNvidiaLibrariesBat); + var cmd = string.Format(ToolsResources.PythonInstaller__0__Running_command____1____2__, ECHO, cmdBuilder, CMD_PROCEEDING_SYMBOL); + cmd += cmdBuilder; + var pipedProcessRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + if (broker != null) cancelToken = broker.CancellationToken; + if (pipedProcessRunner.RunProcess(cmd, true, Writer, true, cancelToken) != 0) + throw new ToolExecutionException(string.Format(ToolsResources.PythonInstaller_Failed_to_execute_command____0__, cmdBuilder)); + } + private void DownloadCudaLibrary(IProgressMonitor progressMonitor) + { + using var webClient = TestDownloadClient ?? new MultiFileAsynchronousDownloadClient(progressMonitor, 1); + if (!webClient.DownloadFileAsync(CudaDownloadUri, CudaDownloadPath, out var downloadException)) + throw new ToolExecutionException( + ToolsResources.PythonInstaller_Cuda_Download_failed__Check_your_network_connection_or_contact_Skyline_team_for_help_, downloadException); + } + private void InstallCudaLibrary(IProgressMonitor progressMonitor) + { + var cmdBuilder = new StringBuilder(); + cmdBuilder.Append(CudaDownloadPath); + var cmd = string.Format(ToolsResources.PythonInstaller__0__Running_command____1____2__, ECHO, cmdBuilder, CMD_PROCEEDING_SYMBOL); + cmd += cmdBuilder; + var pipedProcessRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + if (pipedProcessRunner.RunProcess(cmd, false, Writer, true) != 0) + throw new ToolExecutionException(string.Format(ToolsResources.PythonInstaller_Failed_to_execute_command____0__, cmdBuilder)); + } + + private void DownloadCuDNNLibrary(IProgressMonitor progressMonitor) + { + using var webClient = TestDownloadClient ?? new MultiFileAsynchronousDownloadClient(progressMonitor, 1); + if (!webClient.DownloadFileAsync(CuDNNDownloadUri, CuDNNDownloadPath, out var downloadException)) + throw new ToolExecutionException( + ToolsResources.PythonInstaller_CuDNN_Download_failed__Check_your_network_connection_or_contact_Skyline_team_for_help_, downloadException); + } + private void InstallCuDNNLibrary(IProgressMonitor progressMonitor) + { + using var zipFile = ZipFile.Read(CuDNNDownloadPath); + if (!PythonInstallerUtil.IsSignedFileOrDirectory(CuDNNVersionDir)) + zipFile.ExtractAll(CuDNNVersionDir); + + var cmdBuilder = new StringBuilder(); + string binDirectory = CuDNNInstallDir + @"\bin"; + string includeDirectory = CuDNNInstallDir + @"\include"; + string libDirectory = CuDNNInstallDir + @"\lib"; + + string [] directories = { binDirectory, includeDirectory, libDirectory }; + + PythonInstallerUtil.CreateDirectoriesElevated(directories, TestPipeSkylineProcessRunner, Writer); + + try + { + string sourceDirectory = Path.Combine(CuDNNVersionDir, CuDNNArchive) + @"\bin"; + string searchPattern = @"cudnn*.dll"; + + PythonInstallerUtil.CopyFilesElevated(sourceDirectory, binDirectory, searchPattern, TestPipeSkylineProcessRunner, Writer); + PythonInstallerUtil.SignDirectory(binDirectory); + + sourceDirectory = Path.Combine(CuDNNVersionDir, CuDNNArchive) + @"\include"; + searchPattern = @"cudnn*.h"; + + PythonInstallerUtil.CopyFilesElevated(sourceDirectory, includeDirectory, searchPattern, TestPipeSkylineProcessRunner, Writer); + PythonInstallerUtil.SignDirectory(includeDirectory); + + sourceDirectory = Path.Combine(CuDNNVersionDir, CuDNNArchive) + @"\lib\x64"; + searchPattern = @"cudnn*.lib"; + + PythonInstallerUtil.CopyFilesElevated(sourceDirectory, libDirectory, searchPattern, TestPipeSkylineProcessRunner, Writer); + PythonInstallerUtil.SignDirectory(libDirectory); + + + } + catch (Exception) + { + //exception + } + + + } + private void DownloadPythonEmbeddablePackage(IProgressMonitor progressMonitor) + { + using var webClient = TestDownloadClient ?? new MultiFileAsynchronousDownloadClient(progressMonitor, 1); + if (!webClient.DownloadFileAsync(PythonEmbeddablePackageUri, PythonEmbeddablePackageDownloadPath, out var downloadException)) + throw new ToolExecutionException( + ToolsResources.PythonInstaller_Download_failed__Check_your_network_connection_or_contact_Skyline_team_for_help_, downloadException); + } + + private void UnzipPythonEmbeddablePackage(IProgressMonitor progressMonitor) + { + //PythonInstallerUtil.UnblockFile(PythonEmbeddablePackageDownloadPath); + using var zipFile = ZipFile.Read(PythonEmbeddablePackageDownloadPath); + DirectoryEx.SafeDeleteLongPath(PythonEmbeddablePackageExtractDir); + zipFile.ExtractAll(PythonEmbeddablePackageExtractDir); + } + + private void EnableSearchPathInPythonEmbeddablePackage() + { + var files = Directory.GetFiles(PythonEmbeddablePackageExtractDir, @"python*._pth"); + Assume.IsTrue(files.Length == 1, ToolsResources.PythonInstaller_EnableSearchPathInPythonEmbeddablePackage_Found_0_or_more_than_one_files_with__pth_extension__this_is_unexpected); + var oldFilePath = files[0]; + var newFilePath = Path.ChangeExtension(oldFilePath, @".pth"); + File.Move(oldFilePath, newFilePath); + } + private void DownloadGetPipScript(IProgressMonitor progressMonitor) + { + using var webClient = TestPipDownloadClient ?? new MultiFileAsynchronousDownloadClient(progressMonitor, 1); + if (!webClient.DownloadFileAsync(GetPipScriptDownloadUri, GetPipScriptDownloadPath, out var downloadException)) + { + throw new ToolExecutionException( + ToolsResources.PythonInstaller_Download_failed__Check_your_network_connection_or_contact_Skyline_team_for_help_, downloadException); + } + PythonInstallerUtil.SignFile(GetPipScriptDownloadPath); + } + + private void RunGetPipScript(ILongWaitBroker broker = null) + { + var cmdBuilder = new StringBuilder(); + cmdBuilder.Append(BasePythonExecutablePath) + .Append(SPACE) + .Append(GetPipScriptDownloadPath); + var cmd = string.Format(ToolsResources.PythonInstaller__0__Running_command____1____2__, ECHO, cmdBuilder, CMD_PROCEEDING_SYMBOL); + cmd += cmdBuilder; + var pipedProcessRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + CancellationToken cancelToken = CancellationToken.None; + if (broker != null) cancelToken = broker.CancellationToken; + if (pipedProcessRunner.RunProcess(cmd, false, Writer, true, cancelToken) != 0) + throw new ToolExecutionException(string.Format(ToolsResources.PythonInstaller_Failed_to_execute_command____0__, cmdBuilder)); + + var filePath = Path.Combine(PythonEmbeddablePackageExtractDir, SCRIPTS, PIP_EXE); + PythonInstallerUtil.SignFile(filePath); + + } + + private void PipInstall(string pythonExecutablePath, IEnumerable packages, ILongWaitBroker broker = null) + { + var cmdBuilder = new StringBuilder(); + CancellationToken cancelToken = CancellationToken.None; + + try + { + foreach (var package in packages) + { + string arg; + cmdBuilder.Clear(); + cmdBuilder.Append(pythonExecutablePath) + .Append(SPACE) + .Append(PYTHON_MODULE_OPTION) + .Append(SPACE) + .Append(PIP) + .Append(SPACE) + .Append(INSTALL) + .Append(SPACE); + if (package.Version.IsNullOrEmpty()) + { + arg = package.Name; + } + else if (package.Version.StartsWith(GIT)) + { + arg = package.Version; + arg = TextUtil.Quote(arg); + } + else + { + arg = package.Name + EQUALS + package.Version; + arg = TextUtil.Quote(arg); + } + + + cmdBuilder.Append(arg).Append(SPACE); + + var cmd = string.Format(ToolsResources.PythonInstaller__0__Running_command____1____2__, ECHO, + cmdBuilder, CMD_PROCEEDING_SYMBOL); + cmd += string.Format( + ToolsResources + .PythonInstaller_PipInstall__0__This_sometimes_could_take_3_5_minutes__Please_be_patient___1__, + ECHO, CMD_PROCEEDING_SYMBOL); + cmd += cmdBuilder; + var pipedProcessRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + + if (broker != null) cancelToken = broker.CancellationToken; + if (pipedProcessRunner.RunProcess(cmd, false, Writer, true, cancelToken) != 0) + { + throw new ToolExecutionException(string.Format(ToolsResources.PythonInstaller_Failed_to_execute_command____0__, + cmdBuilder)); + + } + + if (cancelToken.IsCancellationRequested) + break; + } + } + catch + { + return; + } + + var filePath = Path.Combine(PythonEmbeddablePackageExtractDir, SCRIPTS, VIRTUALENV); + PythonInstallerUtil.SignFile(filePath+DOT_EXE); + PythonInstallerUtil.SignDirectory(VirtualEnvironmentDir); + + } + + private void RunPythonModule(string pythonExecutablePath, string changeDir, string moduleName, string[] arguments, ILongWaitBroker broker = null) + { + var cmdBuilder = new StringBuilder(); + if (changeDir != null) + { + cmdBuilder.Append(CD) + .Append(SPACE) + .Append(changeDir) + .Append(SPACE) + .Append(CONDITIONAL_CMD_PROCEEDING_SYMBOL) + .Append(SPACE); + } + cmdBuilder.Append(pythonExecutablePath) + .Append(SPACE) + .Append(PYTHON_MODULE_OPTION) + .Append(SPACE) + .Append(moduleName) + .Append(SPACE); + foreach (var argument in arguments) + { + cmdBuilder.Append(argument) + .Append(SPACE); + } + var cmd = string.Format(ToolsResources.PythonInstaller__0__Running_command____1____2__, ECHO, GetEscapedCmdString(cmdBuilder.ToString()), CMD_PROCEEDING_SYMBOL); + cmd += cmdBuilder; + var pipedProcessRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + CancellationToken cancelToken = CancellationToken.None; + if (broker != null) cancelToken = broker.CancellationToken; + if (pipedProcessRunner.RunProcess(cmd, false, Writer, true, cancelToken) != 0) + throw new ToolExecutionException(string.Format(ToolsResources.PythonInstaller_Failed_to_execute_command____0__, cmdBuilder)); + + PythonInstallerUtil.SignDirectory(PythonEmbeddablePackageExtractDir); + + } + + private string GetEscapedCmdString(string cmdString) + { + var specialChars = new[] { CMD_PROCEEDING_SYMBOL }; + foreach (var specialChar in specialChars) + { + cmdString = cmdString.Replace(specialChar, CMD_ESCAPE_SYMBOL + specialChar); + } + return cmdString; + } + + public void CleanUpPythonEnvironment(string name) + { + if (!PythonInstallerUtil.IsSignedFileOrDirectory(CudaVersionDir)) + DirectoryEx.SafeDeleteLongPath(CudaVersionDir); + if (!PythonInstallerUtil.IsSignedFileOrDirectory(CuDNNVersionDir)) + DirectoryEx.SafeDeleteLongPath(CuDNNVersionDir); + if (!PythonInstallerUtil.IsSignedFileOrDirectory(Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), name))) + DirectoryEx.SafeDeleteLongPath(Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), name)); + } + + public static bool DeleteToolsPythonDirectory() + { + DirectoryEx.SafeDeleteLongPath(Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), @"Python")); + if (Directory.Exists(Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), @"Python"))) return false; + return true; + } + + public void SignPythonEnvironment(string name) + { + PythonInstallerUtil.SignDirectory(CudaVersionDir); + PythonInstallerUtil.SignDirectory(CuDNNVersionDir); + PythonInstallerUtil.SignDirectory(Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), name)); + } + } + + public static class PythonInstallerUtil + { + private const string PYTHON = @"Python"; + private const string SCRIPTS = @"Scripts"; + private const string PYTHON_EXECUTABLE = @"python.exe"; + private const string ACTIVATE_SCRIPT_FILE_NAME = @"activate.bat"; + private const string SIGNATURE_EXTENSION = ".skysign"; + /// + /// This directory is used to store python executables and virtual environments. + /// + public static string PythonRootDir + { + get + { + var toolsDir = ToolDescriptionHelpers.GetToolsDirectory(); + var dir = toolsDir + @"\" + PYTHON; + return dir; + } + } + + /// + /// When downloading a python binary or embedded package from the python ftp, the file name contains + /// a substring that represents the CPU architecture, e.g. amd64, arm64, win32, etc. This function + /// figures out the current CPU architecture based on the C# runtime, then maps it to that in + /// the python package names. + /// + public static string GetPythonPackageArchitectureSubstring(Architecture architecture) + { + var map = new Dictionary + { + { Architecture.Arm, null }, + { Architecture.Arm64, @"arm64" }, + { Architecture.X64, @"amd64" }, + { Architecture.X86, @"win32" } + }; + return map[architecture]; + } + + + /// + /// Get python virtual environment directory. + /// + /// + /// + /// + public static string GetPythonVirtualEnvironmentDir(string pythonVersion, string virtualEnvironmentName) + { + return Path.Combine(ToolDescriptionHelpers.GetToolsDirectory(), PYTHON, pythonVersion, + virtualEnvironmentName); + } + + public static string GetPythonVirtualEnvironmentScriptsDir(string pythonVersion, string virtualEnvironmentName) + { + return Path.Combine(GetPythonVirtualEnvironmentDir(pythonVersion, virtualEnvironmentName), SCRIPTS); + } + + /// + /// Get Python executable path. + /// + /// + /// + /// + public static string GetPythonExecutablePath(string pythonVersion, string virtualEnvironmentName) + { + return Path.Combine(GetPythonVirtualEnvironmentScriptsDir(pythonVersion, virtualEnvironmentName), + PYTHON_EXECUTABLE); + } + + /// + /// Get Python virtual environment activate.bat script path. + /// + /// + /// + /// + public static string GetPythonVirtualEnvironmentActivationScriptPath(string pythonVersion, string virtualEnvironmentName) + { + return Path.Combine(GetPythonVirtualEnvironmentScriptsDir(pythonVersion, virtualEnvironmentName), + ACTIVATE_SCRIPT_FILE_NAME); + } + + /// + /// Returns a list of Python installation tasks in topological order, with each task as a PythonTaskNode. + /// + internal static List GetPythonTaskNodes(bool? haveNvidiaGpu = false) + { + var node1 = new PythonTaskNode { PythonTaskName = PythonTaskName.download_python_embeddable_package, ParentNodes = null }; + var node2 = new PythonTaskNode { PythonTaskName = PythonTaskName.unzip_python_embeddable_package, ParentNodes = new List { node1 } }; + var node3 = new PythonTaskNode { PythonTaskName = PythonTaskName.enable_search_path_in_python_embeddable_package, ParentNodes = new List { node2 } }; + var node4 = new PythonTaskNode { PythonTaskName = PythonTaskName.enable_longpaths, ParentNodes = null }; + var node5 = new PythonTaskNode { PythonTaskName = PythonTaskName.download_getpip_script, ParentNodes = new List { node3 } }; + var node6 = new PythonTaskNode { PythonTaskName = PythonTaskName.run_getpip_script, ParentNodes = new List { node3, node5 } }; + var node7 = new PythonTaskNode { PythonTaskName = PythonTaskName.pip_install_virtualenv, ParentNodes = new List { node6 } }; + var node8 = new PythonTaskNode { PythonTaskName = PythonTaskName.create_virtual_environment, ParentNodes = new List { node7 } }; + var node9 = new PythonTaskNode { PythonTaskName = PythonTaskName.pip_install_packages, ParentNodes = new List { node8 } }; + var node10 = new PythonTaskNode { PythonTaskName = PythonTaskName.setup_nvidia_libraries, ParentNodes = null }; + +// var node10 = new PythonTaskNode { PythonTaskName = PythonTaskName.download_cuda_library, ParentNodes = null }; +// var node11 = new PythonTaskNode { PythonTaskName = PythonTaskName.install_cuda_library, ParentNodes = new List { node10 } }; +// var node12 = new PythonTaskNode { PythonTaskName = PythonTaskName.download_cudnn_library, ParentNodes = new List { node11 } }; +// var node13 = new PythonTaskNode { PythonTaskName = PythonTaskName.install_cudnn_library, ParentNodes = new List { node12 } }; + + if (haveNvidiaGpu == true) + return new List + { node1, node2, node3, node4, node5, node6, node7, node8, node9, node10 }; // node11, node12, node13 }; + + return new List { node1, node2, node3, node4, node5, node6, node7, node8, node9 }; + + + } + + public static void CopyFiles(string fromDirectory, string toDirectory, string pattern = @"*") + { + // Ensure the target directory exists + Directory.CreateDirectory(toDirectory); + + // Get all files matching the pattern + string[] files = Directory.GetFiles(fromDirectory, pattern); + + foreach (string file in files) + { + string fileName = Path.GetFileName(file); + string destFile = Path.Combine(toDirectory, fileName); + File.Copy(file, destFile, true); // true for overwrite existing file + } + } + public static void CopyFilesElevated(string fromDirectory, string toDirectory, string pattern, ISkylineProcessRunnerWrapper TestPipeSkylineProcessRunner, TextWriter Writer, ILongWaitBroker broker = null) + { + if (pattern.IsNullOrEmpty()) pattern = @"*"; + + if (!Directory.Exists(toDirectory)) + { + string[] destination = { toDirectory }; + CreateDirectoriesElevated(destination, TestPipeSkylineProcessRunner, Writer); + } + + string[] files = Directory.GetFiles(fromDirectory, pattern); + CancellationToken cancelToken = CancellationToken.None; + if (broker != null) cancelToken = broker.CancellationToken; + + for (int i = 0; i < files.Length; i++) + { + var file = files[i]; + var command = $@"copy {TextUtil.Quote(file)} {TextUtil.Quote(toDirectory)} /y "; + var cmd = string.Format(ToolsResources.PythonInstaller__0__Running_command____1____2__, @"echo", command, TextUtil.AMPERSAND); + var processRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + cmd += command; + if (processRunner.RunProcess(cmd, true, Writer, true, cancelToken) != 0) + throw new ToolExecutionException(string.Format(ToolsResources.PythonInstaller_Failed_to_execute_command____0__, command)); + if (cancelToken.IsCancellationRequested) break; + } + } + public static void CreateDirectoriesElevated(string [] directories, ISkylineProcessRunnerWrapper TestPipeSkylineProcessRunner, TextWriter Writer, ILongWaitBroker broker = null) + { + CancellationToken cancelToken = CancellationToken.None; + if (broker != null) cancelToken = broker.CancellationToken; + for (int i = 0; i < directories.Length; i++) + { + var directory = directories[i]; + if (Directory.Exists(directory)) + continue; + var command = $@"mkdir {TextUtil.Quote(directory)}"; + var cmd = string.Format(ToolsResources.PythonInstaller__0__Running_command____1____2__, @"echo", command, TextUtil.AMPERSAND); + var processRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + cmd += command; + if (processRunner.RunProcess(cmd, true, Writer, true, cancelToken) != 0) + throw new ToolExecutionException(string.Format(ToolsResources.PythonInstaller_Failed_to_execute_command____0__, command)); + if (cancelToken.IsCancellationRequested) break; + } + } + public static string GetFileHash(string filePath) + { + using (var sha256 = SHA256.Create()) + { + byte[] hash = { }; + RetryAction(() => + { + + using (var stream = File.OpenRead($@"\\?\{filePath}")) + { + hash = sha256.ComputeHash(stream); + + } + }); + + if (!hash.IsNullOrEmpty()) + return BitConverter.ToString(hash).Replace(@"-", "").ToLowerInvariant(); + + return @"F00F00F00"; + } + } + + public static void RetryAction([InstantHandle] Action act, int maxRetries = 100) + { + int retry = 0; + + while (retry++ < maxRetries) + { + try + { + act(); + break; + } + catch (IOException) + { + + } + } + } + + public static bool IsRunningElevated() + { + // Get current user's Windows identity + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + + // Convert identity to WindowsPrincipal to check for roles + WindowsPrincipal principal = new WindowsPrincipal(identity); + + // Check if the current user is in the Administrators role + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + /// + /// Return false if skysign file doesn't exist or it exists and is invalid, return true is skysign file matches provide signature, + /// return null if signature provided is null or when signature is not null but user not running as administrator + /// + /// path to file or directory + /// key + /// + public static bool? IsSignatureValid(string path, string signature) + { + string filePath = Path.GetFullPath(path) + SIGNATURE_EXTENSION; + if (!IsSignedFileOrDirectory(path)) + return false; + if (signature.IsNullOrEmpty()) + return null; + if (IsRunningElevated()) + return signature == File.ReadAllText(filePath); + else + { + try + { + FileInfo fileInfo = new FileInfo(filePath); + var stream = fileInfo.OpenRead(); + } + catch (UnauthorizedAccessException) + { + return null; + } + return signature == File.ReadAllText(filePath); + } + + } + public static bool IsSignedFileOrDirectory(string path) + { + return File.Exists(Path.GetFullPath(path) + SIGNATURE_EXTENSION); + } + public static void SignFile(string filePath) + { + if (!File.Exists(filePath)) return; + string signatureFile = Path.GetFullPath(filePath) + SIGNATURE_EXTENSION; + using (FileSaver file = new FileSaver(signatureFile)) + { + File.WriteAllText(file.SafeName, GetFileHash(filePath)); + RetryAction(() => { file.Commit(); }); + } + } + public static void SignDirectory(string dirPath) + { + if (!Directory.Exists(dirPath)) return; + + string signatureFile = Path.GetFullPath(dirPath) + SIGNATURE_EXTENSION; + using (FileSaver file = new FileSaver(signatureFile)) + { + File.WriteAllText(file.SafeName, GetDirectoryHash(dirPath)); + RetryAction(() => { file.Commit(); }); + } + } + public static string GetDirectoryHash(string directoryPath) + { + string[] filesArray = Directory.GetFiles(directoryPath, @"*", SearchOption.AllDirectories); + return GetFilesArrayHash(filesArray); + } + public static string GetFilesArrayHash(string[] filesArray, int maxFilesToCheck = 1000) + { + // Use SHA256 for hashing + using (var sha = SHA256.Create()) + { + // Create a new hash for all files combined + using (var combinedStream = new MemoryStream()) + { + Array.Sort(filesArray); // Ensure consistent order + int fileCount = 0; + for (fileCount = 0; fileCount < Math.Min(filesArray.Length, maxFilesToCheck); fileCount++) + { + //Sometimes the file is locked by another process so we retry up to 100 times + RetryAction(() => { + using (var fileStream = new FileStream($@"\\?\{filesArray[fileCount]}", FileMode.Open)) + { + // Copy file contents to the combined stream + fileStream.CopyTo(combinedStream); + // Add a separator or file name to differentiate between files + + var separator = Encoding.UTF8.GetBytes(Path.GetFileName(filesArray[fileCount]) ?? string.Empty); + combinedStream.Write(separator, 0, separator.Length); + } + }); + } + + combinedStream.Seek(0, SeekOrigin.Begin); // Reset stream position + // Compute hash of combined stream + var hashBytes = sha.ComputeHash(combinedStream); + return BitConverter.ToString(hashBytes).Replace(@"-", @"").ToLower(); + } + } + } + } + + public interface IPythonInstallerTaskValidator + { + bool? Validate(PythonTaskName pythonTaskName, PythonInstaller pythonInstaller); + } + + public enum PythonTaskName + { + download_python_embeddable_package, + unzip_python_embeddable_package, + enable_search_path_in_python_embeddable_package, + enable_longpaths, + download_getpip_script, + run_getpip_script, + pip_install_virtualenv, + create_virtual_environment, + pip_install_packages, + download_cuda_library, + install_cuda_library, + download_cudnn_library, + install_cudnn_library, + setup_nvidia_libraries //Write batch script and execute it with elevation + } + + public class PythonPackage + { + public string Name { get; set; } + public string Version { get; set; } + } + + public class TestPythonInstallerTaskValidator : IPythonInstallerTaskValidator + { + private List TaskNodes => PythonInstallerUtil.GetPythonTaskNodes(); + private Dictionary TaskValidationResult { get; } = new Dictionary + { + { PythonTaskName.download_python_embeddable_package, false }, + { PythonTaskName.unzip_python_embeddable_package, false }, + { PythonTaskName.enable_search_path_in_python_embeddable_package, false }, + { PythonTaskName.enable_longpaths, false }, + { PythonTaskName.download_getpip_script, false }, + { PythonTaskName.run_getpip_script, false }, + { PythonTaskName.pip_install_virtualenv, false }, + { PythonTaskName.create_virtual_environment, false }, + { PythonTaskName.pip_install_packages, false }, + { PythonTaskName.download_cuda_library, false }, + { PythonTaskName.install_cuda_library, false }, + { PythonTaskName.download_cudnn_library, false }, + { PythonTaskName.install_cudnn_library, false }, + { PythonTaskName.setup_nvidia_libraries, false } + + }; + + public bool? Validate(PythonTaskName pythonTaskName, PythonInstaller pythonInstaller) + { + switch (pythonTaskName) + { + case PythonTaskName.download_python_embeddable_package: + return ValidateDownloadPythonEmbeddablePackage(); + case PythonTaskName.unzip_python_embeddable_package: + return ValidateUnzipPythonEmbeddablePackage(); + case PythonTaskName.enable_search_path_in_python_embeddable_package: + return ValidateEnableSearchPathInPythonEmbeddablePackage(); + case PythonTaskName.enable_longpaths: + return ValidateEnableLongpaths(); + case PythonTaskName.download_getpip_script: + return ValidateDownloadGetPipScript(); + case PythonTaskName.run_getpip_script: + return ValidateRunGetPipScript(); + case PythonTaskName.pip_install_virtualenv: + return ValidatePipInstallVirtualenv(); + case PythonTaskName.create_virtual_environment: + return ValidateCreateVirtualEnvironment(); + case PythonTaskName.pip_install_packages: + return ValidatePipInstallPackages(); + case PythonTaskName.download_cuda_library: + return ValidateDownloadCudaLibrary(); + case PythonTaskName.install_cuda_library: + return ValidateInstallCudaLibrary(); + case PythonTaskName.download_cudnn_library: + return ValidateDownloadCuDNNLibrary(); + case PythonTaskName.install_cudnn_library: + return ValidateInstallCuDNNLibrary(); + case PythonTaskName.setup_nvidia_libraries: + return ValidateSetupNvidiaLibraries(); + default: + throw new PythonInstallerUnsupportedTaskNameException(pythonTaskName); + } + } + + public void SetSuccessUntil(PythonTaskName pythonTaskName) + { + var seenTask = false; + foreach (var taskNode in TaskNodes) + { + if (!seenTask) + { + if (taskNode.PythonTaskName.Equals(pythonTaskName)) + { + seenTask = true; + } + TaskValidationResult[taskNode.PythonTaskName] = true; + } + else + { + TaskValidationResult[taskNode.PythonTaskName] = false; + } + } + } + private bool ValidateSetupNvidiaLibraries() + { + return GetTaskValidationResult(PythonTaskName.setup_nvidia_libraries); + } + private bool ValidateDownloadCudaLibrary() + { + return GetTaskValidationResult(PythonTaskName.download_cuda_library); + } + private bool ValidateInstallCudaLibrary() + { + return GetTaskValidationResult(PythonTaskName.install_cuda_library); + } + private bool ValidateDownloadCuDNNLibrary() + { + return GetTaskValidationResult(PythonTaskName.download_cuda_library); + } + private bool ValidateInstallCuDNNLibrary() + { + return GetTaskValidationResult(PythonTaskName.install_cuda_library); + } + private bool ValidateDownloadPythonEmbeddablePackage() + { + return GetTaskValidationResult(PythonTaskName.download_python_embeddable_package); + } + + private bool ValidateUnzipPythonEmbeddablePackage() + { + return GetTaskValidationResult(PythonTaskName.unzip_python_embeddable_package); + } + + private bool ValidateEnableSearchPathInPythonEmbeddablePackage() + { + return GetTaskValidationResult(PythonTaskName.enable_search_path_in_python_embeddable_package); + } + + private bool ValidateDownloadGetPipScript() + { + return GetTaskValidationResult(PythonTaskName.download_getpip_script); + } + + private bool ValidateRunGetPipScript() + { + return GetTaskValidationResult(PythonTaskName.run_getpip_script); + } + + private bool ValidatePipInstallVirtualenv() + { + return GetTaskValidationResult(PythonTaskName.pip_install_virtualenv); + } + + private bool ValidateCreateVirtualEnvironment() + { + return GetTaskValidationResult(PythonTaskName.create_virtual_environment); + } + internal bool ValidateEnableLongpaths() + { + return GetTaskValidationResult(PythonTaskName.enable_longpaths); + } + private bool ValidatePipInstallPackages() + { + return GetTaskValidationResult(PythonTaskName.pip_install_packages); + } + + private bool GetTaskValidationResult(PythonTaskName pythonTaskName) + { + return TaskValidationResult[pythonTaskName]; + } + } + + public class PythonInstallerTaskValidator : IPythonInstallerTaskValidator + { + private const string SCRIPTS = @"Scripts"; + private const string PIP_DOT_EXE = @"pip.exe"; + private const string VIRTUALENV_DOT_EXE = @"virtualenv.exe"; + private const string SPACE = TextUtil.SPACE; + private const string PYTHON_MODULE_OPTION = @"-m"; + private const string PIP = @"pip"; + private const string FREEZE = @"freeze"; + private const string EQUALS = TextUtil.EQUAL + TextUtil.EQUAL; + private const string AT_SPLITTER = @" @ "; + private const string GIT = @"git"; + + private static PythonInstaller _pythonInstaller { get; set; } + + public static void DisableWindowsLongPaths() + { + _pythonInstaller.DisableWindowsLongPaths(); + } + public bool? Validate(PythonTaskName pythonTaskName, PythonInstaller pythonInstaller) + { + _pythonInstaller = pythonInstaller; + switch (pythonTaskName) + { + case PythonTaskName.download_python_embeddable_package: + return ValidateDownloadPythonEmbeddablePackage(); + case PythonTaskName.unzip_python_embeddable_package: + return ValidateUnzipPythonEmbeddablePackage(); + case PythonTaskName.enable_search_path_in_python_embeddable_package: + return ValidateEnableSearchPathInPythonEmbeddablePackage(); + case PythonTaskName.enable_longpaths: + return ValidateEnableLongpaths(); + case PythonTaskName.download_getpip_script: + return ValidateDownloadGetPipScript(); + case PythonTaskName.run_getpip_script: + return ValidateRunGetPipScript(); + case PythonTaskName.pip_install_virtualenv: + return ValidatePipInstallVirtualenv(); + case PythonTaskName.create_virtual_environment: + return ValidateCreateVirtualEnvironment(); + case PythonTaskName.pip_install_packages: + return ValidatePipInstallPackages(); + case PythonTaskName.download_cuda_library: + return ValidateDownloadCudaLibrary(); + case PythonTaskName.install_cuda_library: + return ValidateInstallCudaLibrary(); + case PythonTaskName.download_cudnn_library: + return ValidateDownloadCuDNNLibrary(); + case PythonTaskName.install_cudnn_library: + return ValidateInstallCuDNNLibrary(); + case PythonTaskName.setup_nvidia_libraries: + return ValidateSetupNvidiaLibraries(); + default: + throw new PythonInstallerUnsupportedTaskNameException(pythonTaskName); + } + } + public class PythonTaskAndHash + { + public PythonTaskAndHash(PythonTaskName task, string hash) + { + Task = task; + Hash = hash; + } + public PythonTaskName Task { get; private set; } + public string Hash { get; private set; } + public override string ToString() { return TextUtil.ColonSeparate(Task.ToString(),Hash); } + } + private IList TargetsAndHashes => + new[] + { + new PythonTaskAndHash(PythonTaskName.download_python_embeddable_package, @"938a1f3b80d580320836260612084d74ce094a261e36f9ff3ac7b9463df5f5e4"), + new PythonTaskAndHash(PythonTaskName.unzip_python_embeddable_package, @"1ac00589117bc386ded40a44b99cb357c15f3f35443d3599370e4f750cdba678"), + new PythonTaskAndHash(PythonTaskName.enable_search_path_in_python_embeddable_package, @"95f29168dc5cf35585a501bf35ec865383300bfac0e2222c7ec7c02ca7bde475"), + new PythonTaskAndHash(PythonTaskName.download_getpip_script, @"96e58b5962f307566141ea9b393e136cbdf811db9f02968dc5bc88f43989345c"), + new PythonTaskAndHash(PythonTaskName.download_cuda_library, @"05cd1a726216d83d124b2139464e998520384585d4e9f45bd7ffd902635aab07"), + new PythonTaskAndHash(PythonTaskName.install_cuda_library, @"ABCD123"), + new PythonTaskAndHash(PythonTaskName.download_cudnn_library, @"65ca0f2d77a46de1def35e289780b8d8729ef2fa39cf8dd0c8448e381dd2978c"), + new PythonTaskAndHash(PythonTaskName.install_cudnn_library, @"ABCD123") + + }; + + private bool ValidateDownloadCudaLibrary() + { + if (PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NONVIDIAHARD) + return true; + if (PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NONVIDIASOFT || + PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NAIVE) + return false; + if (PythonInstaller.CudaLibraryInstalled()) + return true; + if (!File.Exists(_pythonInstaller.CudaInstallerDownloadPath)) + return false; + + var computeHash = + PythonInstallerUtil.GetFileHash(_pythonInstaller.CudaInstallerDownloadPath); + var storedHash = TargetsAndHashes.Where(m => m.Task == PythonTaskName.download_cuda_library).ToArray()[0].Hash; + return computeHash == storedHash; + } + + private bool ValidateSetupNvidiaLibraries() + { + return PythonInstaller.NvidiaLibrariesInstalled(); + } + + private bool ValidateInstallCudaLibrary() + { + if (PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NONVIDIAHARD) + return true; + if (PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NONVIDIASOFT || + PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NAIVE) + return false; + + return PythonInstaller.CudaLibraryInstalled(); + } + + private bool ValidateDownloadCuDNNLibrary() + { + if (PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NONVIDIAHARD) + return true; + if (PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NONVIDIASOFT || + PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NAIVE) + return false; + if (PythonInstaller.CuDNNLibraryInstalled() != false) + return true; + if (!File.Exists(_pythonInstaller.CuDNNInstallerDownloadPath)) + return false; + + var computeHash = + PythonInstallerUtil.GetFileHash(_pythonInstaller.CuDNNInstallerDownloadPath); + var storedHash = TargetsAndHashes.Where(m => m.Task == PythonTaskName.download_cudnn_library).ToArray()[0].Hash; + return computeHash == storedHash; + } + + private bool? ValidateInstallCuDNNLibrary() + { + if (PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NONVIDIAHARD) + return true; + if (PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NONVIDIASOFT || + PythonInstaller.SimulatedInstallationState == PythonInstaller.eSimulatedInstallationState.NAIVE) + return false; + + return PythonInstaller.CuDNNLibraryInstalled(); + } + + private bool ValidateDownloadPythonEmbeddablePackage() + { + var pythonFilePath = _pythonInstaller.PythonEmbeddablePackageDownloadPath; + if (!File.Exists(pythonFilePath)) + return false; + var computeHash = PythonInstallerUtil.GetFileHash(pythonFilePath); + var storedHash = TargetsAndHashes.Where(m => m.Task == PythonTaskName.download_python_embeddable_package).ToArray()[0].Hash; + return computeHash == storedHash; + } + + private bool? ValidateUnzipPythonEmbeddablePackage(bool longValidate = false) + { + if (!Directory.Exists(_pythonInstaller.PythonEmbeddablePackageExtractDir)) + return false; + + if (longValidate) + return + PythonInstallerUtil.IsSignatureValid(_pythonInstaller.PythonEmbeddablePackageExtractDir, + PythonInstallerUtil.GetDirectoryHash(_pythonInstaller.PythonEmbeddablePackageExtractDir)); + + return PythonInstallerUtil.IsSignedFileOrDirectory(_pythonInstaller.PythonEmbeddablePackageExtractDir); + } + + private bool ValidateEnableSearchPathInPythonEmbeddablePackage() + { + if (!Directory.Exists(_pythonInstaller.PythonEmbeddablePackageExtractDir)) + return false; + + var disabledPathFiles = Directory.GetFiles(_pythonInstaller.PythonEmbeddablePackageExtractDir, @"python*._pth"); + var enabledPathFiles = Directory.GetFiles(_pythonInstaller.PythonEmbeddablePackageExtractDir, @"python*.pth"); + var computeHash = + PythonInstallerUtil.GetFilesArrayHash(enabledPathFiles); + var storedHash = TargetsAndHashes.Where(m => m.Task == PythonTaskName.enable_search_path_in_python_embeddable_package).ToArray()[0].Hash; + return computeHash == storedHash; + } + + private bool? ValidateDownloadGetPipScript() + { + var filePath = _pythonInstaller.GetPipScriptDownloadPath; + + if (!File.Exists(filePath)) + return false; + + return + PythonInstallerUtil.IsSignatureValid(filePath, PythonInstallerUtil.GetFileHash(filePath)); + } + + private bool? ValidateRunGetPipScript() + { + var filePath = Path.Combine(_pythonInstaller.PythonEmbeddablePackageExtractDir, SCRIPTS, PIP_DOT_EXE); + + if (!File.Exists(filePath)) + return false; + + return + PythonInstallerUtil.IsSignatureValid(filePath, PythonInstallerUtil.GetFileHash(filePath)); + } + + private bool? ValidatePipInstallVirtualenv() + { + var filePath = Path.Combine(_pythonInstaller.PythonEmbeddablePackageExtractDir, SCRIPTS, VIRTUALENV_DOT_EXE); + + if (!File.Exists(filePath)) + return false; + + return + PythonInstallerUtil.IsSignatureValid(filePath, PythonInstallerUtil.GetFileHash(filePath)); + } + + private bool? ValidateCreateVirtualEnvironment(bool longValidate = false) + { + if (!Directory.Exists(_pythonInstaller.VirtualEnvironmentDir)) + return false; + if (longValidate) + return + PythonInstallerUtil.IsSignatureValid(_pythonInstaller.VirtualEnvironmentDir, PythonInstallerUtil.GetDirectoryHash(_pythonInstaller.VirtualEnvironmentDir)); + return true; +// return PythonInstallerUtil.IsSignedFileOrDirectory(_pythonInstaller.VirtualEnvironmentDir); + } + internal static bool ValidateEnableLongpaths() + { + return PythonInstaller.SimulatedInstallationState != PythonInstaller.eSimulatedInstallationState.NAIVE && + (int)Registry.GetValue(PythonInstaller.REG_FILESYSTEM_KEY, PythonInstaller.REG_LONGPATHS_ENABLED,0) == 1; + } + private bool? ValidatePipInstallPackages(bool longValidate = false) + { + if (!File.Exists(_pythonInstaller.VirtualEnvironmentPythonExecutablePath)) + { + return false; + } + + if (!Directory.Exists(_pythonInstaller.VirtualEnvironmentDir)) + return false; + + bool? signatureValid; + + if (!longValidate) + signatureValid = PythonInstallerUtil.IsSignedFileOrDirectory(_pythonInstaller.VirtualEnvironmentDir); + else + signatureValid = PythonInstallerUtil.IsSignatureValid(_pythonInstaller.VirtualEnvironmentDir, + PythonInstallerUtil.GetDirectoryHash(_pythonInstaller.VirtualEnvironmentDir)); + + if (signatureValid == true || signatureValid == null) + return true; + + var argumentsBuilder = new StringBuilder(); + argumentsBuilder.Append(PYTHON_MODULE_OPTION) + .Append(SPACE) + .Append(PIP) + .Append(SPACE) + .Append(FREEZE); + var processStartInfo = new ProcessStartInfo + { + FileName = _pythonInstaller.VirtualEnvironmentPythonExecutablePath, + Arguments = argumentsBuilder.ToString(), + RedirectStandardOutput = true, + CreateNoWindow = true, + UseShellExecute = false + }; + var process = Process.Start(processStartInfo); + if (process == null) + { + return false; + } + var output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + var lines = output.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + var packageToVersionMap = new Dictionary(); + + foreach (var line in lines) + { + var words = line.Split(new [] {EQUALS}, StringSplitOptions.RemoveEmptyEntries); + if (words.Length != 2) + { + words = line.Split(new [] {AT_SPLITTER}, StringSplitOptions.RemoveEmptyEntries); + } + Assume.IsTrue(words.Length.Equals(2), string.Format(ToolsResources.PythonInstallerTaskValidator_ValidatePipInstallPackages_Failed_to_parse_package_name_and_version_from_entry___0__, line)); + packageToVersionMap.Add(words[0], words[1]); + } + + foreach (var package in _pythonInstaller.PythonPackages) + { + if (!packageToVersionMap.ContainsKey(package.Name)) + { + return false; + } + if (!package.Version.IsNullOrEmpty()) + { + if (package.Version.StartsWith(GIT)) + { + if (!packageToVersionMap[package.Name].StartsWith(package.Version)) + { + return false; + } + } + else + { + if (!packageToVersionMap[package.Name].Equals(package.Version)) + { + return false; + } + + } + } + } + return true; + } + } + + public class PythonTask + { + private OneOf, Action> _action; + public bool IsAction => IsActionWithNoArg || IsActionWithProgressMonitor || IsActionWithLongWaitBroker; + public bool IsActionWithNoArg => _action.IsT0; + public bool IsActionWithProgressMonitor => _action.IsT1; + public bool IsActionWithLongWaitBroker => _action.IsT2; + public Action AsActionWithNoArg => _action.AsT0; + public Action AsActionWithProgressMonitor => _action.AsT1; + public Action AsActionWithLongWaitBroker => _action.AsT2; + + public Type ActionType + { + get + { + return _action.Match( + t0 => t0.GetType(), + t1 => t1.GetType(), + t2 => t2.GetType()); + } + } + + public PythonTaskName Name { get; set; } + + public string InProgressMessage { get; set; } + public string SuccessMessage { get; set; } + public string FailureMessage { get; set; } + + public PythonTask(Action action) + { + _action = action; + } + + public PythonTask(Action action) + { + _action = action; + } + + public PythonTask(Action action) + { + _action = action; + } + } + + internal class PythonTaskNode + { + public PythonTaskName PythonTaskName { get; set; } + public List ParentNodes { get; set; } + } + + internal class PythonInstallerException : Exception + { + public PythonInstallerException(string message, Exception inner = null) : base(message, inner) + { + } + } + + internal class PythonInstallerUnsupportedTaskException : PythonInstallerException + { + public PythonInstallerUnsupportedTaskException(PythonTask pythonTask) + : base(string.Format(ToolsResources.PythonInstallerUnsupportedTaskException_Task_with_action_type__0__is_not_supported_by_PythonInstaller_yet, pythonTask.ActionType)) { } + } + + internal class PythonInstallerUnsupportedTaskNameException : PythonInstallerException + { + public PythonInstallerUnsupportedTaskNameException(PythonTaskName pythonTaskName) + : base(string.Format(ToolsResources.PythonInstallerUnsupportedTaskNameException_Task_with_task_name__0__is_not_supported_by_PythonInstaller_yet, pythonTaskName)) { } + } +} + diff --git a/pwiz_tools/Skyline/Model/Tools/ToolDescription.cs b/pwiz_tools/Skyline/Model/Tools/ToolDescription.cs index ac83dd2c01..cdc75ce1d3 100644 --- a/pwiz_tools/Skyline/Model/Tools/ToolDescription.cs +++ b/pwiz_tools/Skyline/Model/Tools/ToolDescription.cs @@ -28,7 +28,6 @@ using System.Xml.Serialization; using pwiz.Common.DataBinding; using pwiz.Common.SystemUtil; -using pwiz.Skyline.Controls.Databinding; using pwiz.Skyline.Model.Databinding; using pwiz.Skyline.Model.DocSettings; using pwiz.Skyline.Properties; @@ -440,7 +439,7 @@ public static void GetReport(SrmDocument doc, string reportTitle, string toolTit var container = new MemoryDocumentContainer(); container.SetDocument(doc, container.Document); var dataSchema = new SkylineDataSchema(container, DataSchemaLocalizer.INVARIANT); - var viewContext = new DocumentGridViewContext(dataSchema); + var viewContext = new pwiz.Skyline.Controls.Databinding.DocumentGridViewContext(dataSchema); ViewInfo viewInfo = viewContext.GetViewInfo(PersistedViews.ExternalToolsGroup.Id.ViewName(reportTitle)); if (null == viewInfo) { diff --git a/pwiz_tools/Skyline/Model/Tools/ToolsResources.designer.cs b/pwiz_tools/Skyline/Model/Tools/ToolsResources.designer.cs index 33e9d55261..a3d9ef70e2 100644 --- a/pwiz_tools/Skyline/Model/Tools/ToolsResources.designer.cs +++ b/pwiz_tools/Skyline/Model/Tools/ToolsResources.designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -19,7 +19,7 @@ namespace pwiz.Skyline.Model.Tools { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class ToolsResources { @@ -88,6 +88,24 @@ public static string ConfigureToolsDlg_unpackZipTool_Invalid_file_selected__No_t } } + /// + /// Looks up a localized string similar to Failed setting up Nvidia GPU Libraries. + /// + public static string NvidiaInstaller_Failed_Setup_Nvidia_Libraries { + get { + return ResourceManager.GetString("NvidiaInstaller_Failed_Setup_Nvidia_Libraries", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Setting up Nvidia GPU Libraries. + /// + public static string NvidiaInstaller_Setup_Nvidia_Libraries { + get { + return ResourceManager.GetString("NvidiaInstaller_Setup_Nvidia_Libraries", resourceCulture); + } + } + /// /// Looks up a localized string similar to ProgramPathCollectors must have a program name. /// @@ -97,6 +115,499 @@ public static string ProgramPathContainer_Validate_ProgramPathCollectors_must_ha } } + /// + /// Looks up a localized string similar to {0} Running command: [{1}] {2} . + /// + public static string PythonInstaller__0__Running_command____1____2__ { + get { + return ResourceManager.GetString("PythonInstaller__0__Running_command____1____2__", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cuda library download failed. Check your network connection or contact Skyline team for help.. + /// + public static string PythonInstaller_Cuda_Download_failed__Check_your_network_connection_or_contact_Skyline_team_for_help_ { + get { + return ResourceManager.GetString("PythonInstaller_Cuda_Download_failed__Check_your_network_connection_or_contact_Sk" + + "yline_team_for_help_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CuDNN library download failed. Check your network connection or contact Skyline team for help.. + /// + public static string PythonInstaller_CuDNN_Download_failed__Check_your_network_connection_or_contact_Skyline_team_for_help_ { + get { + return ResourceManager.GetString("PythonInstaller_CuDNN_Download_failed__Check_your_network_connection_or_contact_S" + + "kyline_team_for_help_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Python download failed. Check your network connection or contact Skyline team for help.. + /// + public static string PythonInstaller_Download_failed__Check_your_network_connection_or_contact_Skyline_team_for_help_ { + get { + return ResourceManager.GetString("PythonInstaller_Download_failed__Check_your_network_connection_or_contact_Skyline" + + "_team_for_help_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expecting one and only one file with _pth extension. + /// + public static string PythonInstaller_EnableSearchPathInPythonEmbeddablePackage_Found_0_or_more_than_one_files_with__pth_extension__this_is_unexpected { + get { + return ResourceManager.GetString("PythonInstaller_EnableSearchPathInPythonEmbeddablePackage_Found_0_or_more_than_on" + + "e_files_with__pth_extension__this_is_unexpected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to execute command: [{0}]. + /// + public static string PythonInstaller_Failed_to_execute_command____0__ { + get { + return ResourceManager.GetString("PythonInstaller_Failed_to_execute_command____0__", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Installing Packages. + /// + public static string PythonInstaller_GetPackages_Installing_Packages { + get { + return ResourceManager.GetString("PythonInstaller_GetPackages_Installing_Packages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Installing Pip. + /// + public static string PythonInstaller_GetPip_Installing_Pip { + get { + return ResourceManager.GetString("PythonInstaller_GetPip_Installing_Pip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Installing Python. + /// + public static string PythonInstaller_GetPython_Installing_Python { + get { + return ResourceManager.GetString("PythonInstaller_GetPython_Installing_Python", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creating virtual environment {0}. + /// + public static string PythonInstaller_GetPythonTask_Creating_virtual_environment__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Creating_virtual_environment__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading Cuda Installer. + /// + public static string PythonInstaller_GetPythonTask_Downloading_Cuda_Installer { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Downloading_Cuda_Installer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading CuDNN Installer. + /// + public static string PythonInstaller_GetPythonTask_Downloading_CuDNN_Installer { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Downloading_CuDNN_Installer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading Python embeddable package. + /// + public static string PythonInstaller_GetPythonTask_Downloading_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Downloading_Python_embeddable_package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading the get-pip.py script. + /// + public static string PythonInstaller_GetPythonTask_Downloading_the_get_pip_py_script { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Downloading_the_get_pip_py_script", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabling Windows Long Paths for python packages installation in virtual environment [{0}]. + /// + public static string PythonInstaller_GetPythonTask_Enable_Long_Paths_For_Python_packages_in_virtual_environment__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Enable_Long_Paths_For_Python_packages_in_virtual_en" + + "vironment__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabling search path in Python embeddable package. + /// + public static string PythonInstaller_GetPythonTask_Enabling_search_path_in_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Enabling_search_path_in_Python_embeddable_package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to create virtual environment {0}. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_create_virtual_environment__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_create_virtual_environment__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to download Cuda Installer. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_download_Cuda_Installer { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_download_Cuda_Installer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to download CuDNN Installer. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_download_CuDNN_Installer { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_download_CuDNN_Installer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to download Python embeddable package. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_download_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_download_Python_embeddable_package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to download the get-pip.py script. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_download_the_get_pip_py_script { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_download_the_get_pip_py_script", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to enable Windows Long Paths for python installation in virtual environment [{0}]. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_enable_long_paths_Python_packages_in_virtual_environment__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_enable_long_paths_Python_packages_in_virt" + + "ual_environment__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to enable search path in Python embeddable package. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_enable_search_path_in_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_enable_search_path_in_Python_embeddable_p" + + "ackage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to install Cuda Library. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_install_Cuda { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_install_Cuda", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to install CuDNN Library. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_install_CuDNN { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_install_CuDNN", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to install Python packages in virtual environment {0}. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_install_Python_packages_in_virtual_environment__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_install_Python_packages_in_virtual_enviro" + + "nment__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to run pip install {0}. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_run_pip_install__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_run_pip_install__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to run the get-pip.py script. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_run_the_get_pip_py_script { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_run_the_get_pip_py_script", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to unzip Python embeddable package. + /// + public static string PythonInstaller_GetPythonTask_Failed_to_unzip_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Failed_to_unzip_Python_embeddable_package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Installing Cuda Library. + /// + public static string PythonInstaller_GetPythonTask_Installing_Cuda { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Installing_Cuda", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Installing CuDNN Library. + /// + public static string PythonInstaller_GetPythonTask_Installing_CuDNN { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Installing_CuDNN", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Installing Python packages in virtual environment {0}. First installs can take up to 10-15 minutes, please be patient .... + /// + public static string PythonInstaller_GetPythonTask_Installing_Python_packages_in_virtual_environment__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Installing_Python_packages_in_virtual_environment__" + + "0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Running pip install {0}. + /// + public static string PythonInstaller_GetPythonTask_Running_pip_install__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Running_pip_install__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Running the get-pip.py script. + /// + public static string PythonInstaller_GetPythonTask_Running_the_get_pip_py_script { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Running_the_get_pip_py_script", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unzipping Python embeddable package. + /// + public static string PythonInstaller_GetPythonTask_Unzipping_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonTask_Unzipping_Python_embeddable_package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading Python embeddable package. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Downloading_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Downloading_Python_embeddable_pac" + + "kage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading the get-pip.py script. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Downloading_the_get_pip_py_script { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Downloading_the_get_pip_py_script" + + "", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabling search path in Python embeddable package. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Enabling_search_path_in_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Enabling_search_path_in_Python_em" + + "beddable_package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to create virtual environment {0}. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_create_virtual_environment__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_create_virtual_environm" + + "ent__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to download Python embeddable package. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_download_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_download_Python_embedda" + + "ble_package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to download the get-pip.py script. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_download_the_get_pip_py_script { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_download_the_get_pip_py" + + "_script", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to enable search path in Python embeddable package. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_enable_search_path_in_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_enable_search_path_in_P" + + "ython_embeddable_package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to install Python packages in virtual environment {0}. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_install_Python_packages_in_virtual_environment__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_install_Python_packages" + + "_in_virtual_environment__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to run the get-pip.py script. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_run_the_get_pip_py_script { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_run_the_get_pip_py_scri" + + "pt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to unzip Python embeddable package. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_unzip_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Failed_to_unzip_Python_embeddable" + + "_package", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Installing Python packages in virtual environment {0}. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Installing_Python_packages_in_virtual_environment__0_ { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Installing_Python_packages_in_vir" + + "tual_environment__0_", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unzipping Python embeddable package. + /// + public static string PythonInstaller_GetPythonVirtualEnvironmentTask_Unzipping_Python_embeddable_package { + get { + return ResourceManager.GetString("PythonInstaller_GetPythonVirtualEnvironmentTask_Unzipping_Python_embeddable_packa" + + "ge", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Install. + /// + public static string PythonInstaller_InstallPackages_Install { + get { + return ResourceManager.GetString("PythonInstaller_InstallPackages_Install", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} This sometimes could take 3-5 minutes. Please be patient. {1} . + /// + public static string PythonInstaller_PipInstall__0__This_sometimes_could_take_3_5_minutes__Please_be_patient___1__ { + get { + return ResourceManager.GetString("PythonInstaller_PipInstall__0__This_sometimes_could_take_3_5_minutes__Please_be_p" + + "atient___1__", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to parse package name and version from entry [{0}]. + /// + public static string PythonInstallerTaskValidator_ValidatePipInstallPackages_Failed_to_parse_package_name_and_version_from_entry___0__ { + get { + return ResourceManager.GetString("PythonInstallerTaskValidator_ValidatePipInstallPackages_Failed_to_parse_package_n" + + "ame_and_version_from_entry___0__", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task with action type {0} is not supported by PythonInstaller yet. + /// + public static string PythonInstallerUnsupportedTaskException_Task_with_action_type__0__is_not_supported_by_PythonInstaller_yet { + get { + return ResourceManager.GetString("PythonInstallerUnsupportedTaskException_Task_with_action_type__0__is_not_supporte" + + "d_by_PythonInstaller_yet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task with task name {0} is not supported by PythonInstaller yet. + /// + public static string PythonInstallerUnsupportedTaskNameException_Task_with_task_name__0__is_not_supported_by_PythonInstaller_yet { + get { + return ResourceManager.GetString("PythonInstallerUnsupportedTaskNameException_Task_with_task_name__0__is_not_suppor" + + "ted_by_PythonInstaller_yet", resourceCulture); + } + } + /// /// Looks up a localized string similar to Tools must have a command line. /// diff --git a/pwiz_tools/Skyline/Model/Tools/ToolsResources.resx b/pwiz_tools/Skyline/Model/Tools/ToolsResources.resx index 7e383fcc26..03478aa0d2 100644 --- a/pwiz_tools/Skyline/Model/Tools/ToolsResources.resx +++ b/pwiz_tools/Skyline/Model/Tools/ToolsResources.resx @@ -243,5 +243,167 @@ This tool requires an Active Replicate Name to run + + + Setting up Nvidia GPU Libraries + + + Failed setting up Nvidia GPU Libraries + + + Installing Packages + + + Installing Pip + + + Installing Python + + + Install + + + Python download failed. Check your network connection or contact Skyline team for help. + + + Cuda library download failed. Check your network connection or contact Skyline team for help. + + + CuDNN library download failed. Check your network connection or contact Skyline team for help. + + + Downloading Python embeddable package + + + Failed to download Python embeddable package + + + Unzipping Python embeddable package + + + Failed to unzip Python embeddable package + + + Enabling search path in Python embeddable package + + + Failed to enable search path in Python embeddable package + + + Downloading the get-pip.py script + + + Failed to download the get-pip.py script + + + Failed to run the get-pip.py script + + + Failed to create virtual environment {0} + + + Installing Python packages in virtual environment {0} + + + Failed to install Python packages in virtual environment {0} + + + Failed to execute command: [{0}] + + + {0} Running command: [{1}] {2} + + + {0} This sometimes could take 3-5 minutes. Please be patient. {1} + + + Task with action type {0} is not supported by PythonInstaller yet + + + Task with task name {0} is not supported by PythonInstaller yet + + + Downloading Python embeddable package + + + Failed to download Python embeddable package + + + Unzipping Python embeddable package + + + Failed to unzip Python embeddable package + + + Enabling search path in Python embeddable package + + + Failed to enable search path in Python embeddable package + + + Downloading the get-pip.py script + + + Failed to download the get-pip.py script + + + Downloading Cuda Installer + + + Failed to download Cuda Installer + + + Installing Cuda Library + + + Failed to install Cuda Library + + + Downloading CuDNN Installer + + + Failed to download CuDNN Installer + + + Installing CuDNN Library + + + Failed to install CuDNN Library + + + Running the get-pip.py script + + + Failed to run the get-pip.py script + + + Running pip install {0} + + + Failed to run pip install {0} + + + Creating virtual environment {0} + + + Failed to create virtual environment {0} + + + Installing Python packages in virtual environment {0}. First installs can take up to 10-15 minutes, please be patient ... + + + Failed to install Python packages in virtual environment {0} + + + Expecting one and only one file with _pth extension + + + Failed to parse package name and version from entry [{0}] + + + Enabling Windows Long Paths for python packages installation in virtual environment [{0}] + + + Failed to enable Windows Long Paths for python installation in virtual environment [{0}] \ No newline at end of file diff --git a/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.Designer.cs b/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.Designer.cs index 9af59c691e..0b0f6f007e 100644 --- a/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.Designer.cs +++ b/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.Designer.cs @@ -63,11 +63,19 @@ private void InitializeComponent() this.tabProperties = new System.Windows.Forms.TabPage(); this.tabControlDataSource = new pwiz.Skyline.Controls.WizardPages(); this.tabFilesSource = new System.Windows.Forms.TabPage(); + this.tabAlphaSource = new System.Windows.Forms.TabPage(); this.tabCarafeSource = new System.Windows.Forms.TabPage(); this.tabKoinaSource = new System.Windows.Forms.TabPage(); this.tabFiles = new System.Windows.Forms.TabPage(); this.tabLearn = new System.Windows.Forms.TabPage(); this.tabControlLearning = new pwiz.Skyline.Controls.WizardPages(); + this.tabWithFiles = new System.Windows.Forms.TabPage(); + this.labelTrainingData = new System.Windows.Forms.Label(); + this.buttonTrainingData = new System.Windows.Forms.Button(); + this.textBoxTrainingData = new System.Windows.Forms.TextBox(); + this.labelMsMsData = new System.Windows.Forms.Label(); + this.buttonMsMsData = new System.Windows.Forms.Button(); + this.textBoxMsMsData = new System.Windows.Forms.TextBox(); this.tabPageDocument = new System.Windows.Forms.TabPage(); this.btnLearningDocBrowse = new System.Windows.Forms.Button(); this.textLearningDoc = new System.Windows.Forms.TextBox(); @@ -76,8 +84,19 @@ private void InitializeComponent() this.listLibraries = new System.Windows.Forms.CheckedListBox(); this.label3 = new System.Windows.Forms.Label(); this.comboLearnFrom = new System.Windows.Forms.ComboBox(); - this.label1 = new System.Windows.Forms.Label(); - this.tabAlphaSource = new System.Windows.Forms.TabPage(); + this.labelLearnFrom = new System.Windows.Forms.Label(); + this.tabControlBuildLibraryTarget = new pwiz.Skyline.Controls.WizardPages(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.labelProteinDatabase = new System.Windows.Forms.Label(); + this.buttonProteinDatabase = new System.Windows.Forms.Button(); + this.textBoxProteinDatabase = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.comboBuildLibraryTarget = new System.Windows.Forms.ComboBox(); + this.labelBuildLibraryTarget = new System.Windows.Forms.Label(); + this.toolTipProteinDatabase = new System.Windows.Forms.ToolTip(this.components); + this.toolTipTrainingData = new System.Windows.Forms.ToolTip(this.components); + this.toolTipMsMsData = new System.Windows.Forms.ToolTip(this.components); this.dataSourceGroupBox.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.gridInputFiles)).BeginInit(); this.tabControlMain.SuspendLayout(); @@ -88,8 +107,11 @@ private void InitializeComponent() this.tabFiles.SuspendLayout(); this.tabLearn.SuspendLayout(); this.tabControlLearning.SuspendLayout(); + this.tabWithFiles.SuspendLayout(); this.tabPageDocument.SuspendLayout(); this.tabPageLibraries.SuspendLayout(); + this.tabControlBuildLibraryTarget.SuspendLayout(); + this.tabPage2.SuspendLayout(); this.SuspendLayout(); // // textName @@ -347,6 +369,12 @@ private void InitializeComponent() resources.ApplyResources(this.tabFilesSource, "tabFilesSource"); this.tabFilesSource.Name = "tabFilesSource"; // + // tabAlphaSource + // + this.tabAlphaSource.BackColor = System.Drawing.SystemColors.Control; + resources.ApplyResources(this.tabAlphaSource, "tabAlphaSource"); + this.tabAlphaSource.Name = "tabAlphaSource"; + // // tabCarafeSource // this.tabCarafeSource.BackColor = System.Drawing.SystemColors.Control; @@ -377,19 +405,69 @@ private void InitializeComponent() this.tabLearn.BackColor = System.Drawing.SystemColors.Control; this.tabLearn.Controls.Add(this.tabControlLearning); this.tabLearn.Controls.Add(this.comboLearnFrom); - this.tabLearn.Controls.Add(this.label1); + this.tabLearn.Controls.Add(this.labelLearnFrom); + this.tabLearn.Controls.Add(this.tabControlBuildLibraryTarget); + this.tabLearn.Controls.Add(this.comboBuildLibraryTarget); + this.tabLearn.Controls.Add(this.labelBuildLibraryTarget); resources.ApplyResources(this.tabLearn, "tabLearn"); this.tabLearn.Name = "tabLearn"; // // tabControlLearning // resources.ApplyResources(this.tabControlLearning, "tabControlLearning"); + this.tabControlLearning.Controls.Add(this.tabWithFiles); this.tabControlLearning.Controls.Add(this.tabPageDocument); this.tabControlLearning.Controls.Add(this.tabPageLibraries); this.tabControlLearning.Name = "tabControlLearning"; this.tabControlLearning.SelectedIndex = 0; this.tabControlLearning.TabStop = false; // + // tabWithFiles + // + this.tabWithFiles.BackColor = System.Drawing.SystemColors.Control; + this.tabWithFiles.Controls.Add(this.labelTrainingData); + this.tabWithFiles.Controls.Add(this.buttonTrainingData); + this.tabWithFiles.Controls.Add(this.textBoxTrainingData); + this.tabWithFiles.Controls.Add(this.labelMsMsData); + this.tabWithFiles.Controls.Add(this.buttonMsMsData); + this.tabWithFiles.Controls.Add(this.textBoxMsMsData); + resources.ApplyResources(this.tabWithFiles, "tabWithFiles"); + this.tabWithFiles.Name = "tabWithFiles"; + // + // labelTrainingData + // + resources.ApplyResources(this.labelTrainingData, "labelTrainingData"); + this.labelTrainingData.Name = "labelTrainingData"; + // + // buttonTrainingData + // + resources.ApplyResources(this.buttonTrainingData, "buttonTrainingData"); + this.buttonTrainingData.Name = "buttonTrainingData"; + this.buttonTrainingData.UseVisualStyleBackColor = true; + this.buttonTrainingData.Click += new System.EventHandler(this.buttonTrainingData_Click); + // + // textBoxTrainingData + // + resources.ApplyResources(this.textBoxTrainingData, "textBoxTrainingData"); + this.textBoxTrainingData.Name = "textBoxTrainingData"; + // + // labelMsMsData + // + resources.ApplyResources(this.labelMsMsData, "labelMsMsData"); + this.labelMsMsData.Name = "labelMsMsData"; + // + // buttonMsMsData + // + resources.ApplyResources(this.buttonMsMsData, "buttonMsMsData"); + this.buttonMsMsData.Name = "buttonMsMsData"; + this.buttonMsMsData.UseVisualStyleBackColor = true; + this.buttonMsMsData.Click += new System.EventHandler(this.buttonMsMsData_Click); + // + // textBoxMsMsData + // + resources.ApplyResources(this.textBoxMsMsData, "textBoxMsMsData"); + this.textBoxMsMsData.Name = "textBoxMsMsData"; + // // tabPageDocument // this.tabPageDocument.BackColor = System.Drawing.SystemColors.Control; @@ -440,22 +518,77 @@ private void InitializeComponent() this.comboLearnFrom.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboLearnFrom.FormattingEnabled = true; this.comboLearnFrom.Items.AddRange(new object[] { - resources.GetString("comboLearnFrom.Items"), - resources.GetString("comboLearnFrom.Items1")}); + resources.GetString("comboLearnFrom.Items")}); resources.ApplyResources(this.comboLearnFrom, "comboLearnFrom"); this.comboLearnFrom.Name = "comboLearnFrom"; this.comboLearnFrom.SelectedIndexChanged += new System.EventHandler(this.comboLearnFrom_SelectedIndexChanged); // - // label1 + // labelLearnFrom // - resources.ApplyResources(this.label1, "label1"); - this.label1.Name = "label1"; + resources.ApplyResources(this.labelLearnFrom, "labelLearnFrom"); + this.labelLearnFrom.Name = "labelLearnFrom"; // - // tabAlphaSource + // tabControlBuildLibraryTarget // - this.tabAlphaSource.BackColor = System.Drawing.SystemColors.Control; - resources.ApplyResources(this.tabAlphaSource, "tabAlphaSource"); - this.tabAlphaSource.Name = "tabAlphaSource"; + resources.ApplyResources(this.tabControlBuildLibraryTarget, "tabControlBuildLibraryTarget"); + this.tabControlBuildLibraryTarget.Controls.Add(this.tabPage1); + this.tabControlBuildLibraryTarget.Controls.Add(this.tabPage2); + this.tabControlBuildLibraryTarget.Name = "tabControlBuildLibraryTarget"; + this.tabControlBuildLibraryTarget.SelectedIndex = 0; + this.tabControlBuildLibraryTarget.TabStop = false; + // + // tabPage1 + // + this.tabPage1.BackColor = System.Drawing.SystemColors.Control; + resources.ApplyResources(this.tabPage1, "tabPage1"); + this.tabPage1.Name = "tabPage1"; + // + // tabPage2 + // + this.tabPage2.BackColor = System.Drawing.SystemColors.Control; + this.tabPage2.Controls.Add(this.labelProteinDatabase); + this.tabPage2.Controls.Add(this.buttonProteinDatabase); + this.tabPage2.Controls.Add(this.textBoxProteinDatabase); + this.tabPage2.Controls.Add(this.label6); + resources.ApplyResources(this.tabPage2, "tabPage2"); + this.tabPage2.Name = "tabPage2"; + // + // labelProteinDatabase + // + resources.ApplyResources(this.labelProteinDatabase, "labelProteinDatabase"); + this.labelProteinDatabase.Name = "labelProteinDatabase"; + // + // buttonProteinDatabase + // + resources.ApplyResources(this.buttonProteinDatabase, "buttonProteinDatabase"); + this.buttonProteinDatabase.Name = "buttonProteinDatabase"; + this.buttonProteinDatabase.UseVisualStyleBackColor = true; + this.buttonProteinDatabase.Click += new System.EventHandler(this.buttonProteinDatabase_Click); + // + // textBoxProteinDatabase + // + resources.ApplyResources(this.textBoxProteinDatabase, "textBoxProteinDatabase"); + this.textBoxProteinDatabase.Name = "textBoxProteinDatabase"; + // + // label6 + // + resources.ApplyResources(this.label6, "label6"); + this.label6.Name = "label6"; + // + // comboBuildLibraryTarget + // + this.comboBuildLibraryTarget.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBuildLibraryTarget.FormattingEnabled = true; + this.comboBuildLibraryTarget.Items.AddRange(new object[] { + resources.GetString("comboBuildLibraryTarget.Items")}); + resources.ApplyResources(this.comboBuildLibraryTarget, "comboBuildLibraryTarget"); + this.comboBuildLibraryTarget.Name = "comboBuildLibraryTarget"; + this.comboBuildLibraryTarget.SelectedIndexChanged += new System.EventHandler(this.comboBuildLibraryTarget_SelectedIndexChanged); + // + // labelBuildLibraryTarget + // + resources.ApplyResources(this.labelBuildLibraryTarget, "labelBuildLibraryTarget"); + this.labelBuildLibraryTarget.Name = "labelBuildLibraryTarget"; // // BuildLibraryDlg // @@ -488,10 +621,15 @@ private void InitializeComponent() this.tabLearn.ResumeLayout(false); this.tabLearn.PerformLayout(); this.tabControlLearning.ResumeLayout(false); + this.tabWithFiles.ResumeLayout(false); + this.tabWithFiles.PerformLayout(); this.tabPageDocument.ResumeLayout(false); this.tabPageDocument.PerformLayout(); this.tabPageLibraries.ResumeLayout(false); this.tabPageLibraries.PerformLayout(); + this.tabControlBuildLibraryTarget.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); this.ResumeLayout(false); } @@ -535,7 +673,7 @@ private void InitializeComponent() private System.Windows.Forms.RadioButton radioAlphaSource; private System.Windows.Forms.TabPage tabLearn; private System.Windows.Forms.ComboBox comboLearnFrom; - private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label labelLearnFrom; private pwiz.Skyline.Controls.WizardPages tabControlLearning; private System.Windows.Forms.TabPage tabPageLibraries; private System.Windows.Forms.TabPage tabPageDocument; @@ -546,5 +684,35 @@ private void InitializeComponent() private System.Windows.Forms.Label label5; private System.Windows.Forms.RadioButton radioCarafeSource; private System.Windows.Forms.TabPage tabAlphaSource; + private System.Windows.Forms.Label labelBuildLibraryTarget; + private System.Windows.Forms.ComboBox comboBuildLibraryTarget; + private Controls.WizardPages tabControlBuildLibraryTarget; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox textBoxProteinDatabase; + private System.Windows.Forms.Button buttonProteinDatabase; + private System.Windows.Forms.TabPage tabWithFiles; + private System.Windows.Forms.Button buttonTrainingData; + private System.Windows.Forms.TextBox textBoxTrainingData; + private System.Windows.Forms.Button buttonMsMsData; + private System.Windows.Forms.TextBox textBoxMsMsData; + private System.Windows.Forms.Label labelProteinDatabase; + private System.Windows.Forms.Label labelTrainingData; + private System.Windows.Forms.Label labelMsMsData; + private System.Windows.Forms.ToolTip toolTipProteinDatabase; + private System.Windows.Forms.ToolTip toolTipTrainingData; + private System.Windows.Forms.ToolTip toolTipMsMsData; + + internal string TextBoxTrainingDataFile + { + get => textBoxTrainingData.Text; + set => textBoxTrainingData.Text = value; + } + internal string TextBoxMsMsDataFile + { + get => textBoxMsMsData.Text; + set => textBoxMsMsData.Text = value; + } } } diff --git a/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.cs b/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.cs index e62cd13607..801ecbfbe5 100644 --- a/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.cs +++ b/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.cs @@ -28,6 +28,8 @@ using pwiz.Skyline.Controls; using pwiz.Skyline.FileUI.PeptideSearch; using pwiz.Skyline.Model; +using pwiz.Skyline.Model.AlphaPeptDeep; +using pwiz.Skyline.Model.Carafe; using pwiz.Skyline.Model.DocSettings; using pwiz.Skyline.Model.Irt; using pwiz.Skyline.Model.Lib; @@ -36,6 +38,11 @@ using pwiz.Skyline.ToolsUI; using pwiz.Skyline.Util; using pwiz.Skyline.Util.Extensions; +using pwiz.Skyline.Model.Tools; +using pwiz.Skyline.EditUI; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("TestFunctional")] namespace pwiz.Skyline.SettingsUI { @@ -78,22 +85,51 @@ public partial class BuildLibraryDlg : FormEx, IMultipleViewProvider BiblioSpecLiteBuilder.EXT_SSL, }; + public void ResetBuilder() => Builder = null; + + public MultiButtonMsgDlg PythonDlg { get; private set; } + public enum Pages { properties, files, learning } public class PropertiesPage : IFormView { } public class FilesPage : IFormView { } public class LearningPage : IFormView { } + private const string PYTHON = @"Python"; + public const string ALPHAPEPTDEEP_PYTHON_VERSION = @"3.9.13"; + private const string ALPHAPEPTDEEP = @"alphapeptdeep"; + private const string ALPHAPEPTDEEP_DIA = @"alphapeptdeep_dia"; + internal const string CARAFE_PYTHON_VERSION = @"3.9.13"; + private const string CARAFE = @"carafe"; + private const string WORKSPACES = @"workspaces"; + private const string PEPTDEEP = @"peptdeep"; + private static readonly IFormView[] TAB_PAGES = { new PropertiesPage(), new FilesPage(), new LearningPage(), }; - - private bool IsAlphaEnabled => false; // TODO: Implement and enable - private bool IsCarafeEnabled => false; // TODO: Implement and enable - public enum DataSourcePages { files, alpha, carafe, koina } - public enum LearningOptions { none, libraries, document } + public enum BuildLibraryTargetOptions { currentSkylineDocument } + // TODO: After supporting LearningOptions.document, add "Skyline Document" option to the comboLearnFrom dropdown + // TODO: After supporting LearningOptions.libraries, add "Libraries" option to the comboLearnFrom dropdown + public enum LearningOptions { files, libraries, document } + private bool IsAlphaEnabled => true; + private bool IsCarafeEnabled => false; + private string AlphapeptdeepPythonVirtualEnvironmentDir => + PythonInstallerUtil.GetPythonVirtualEnvironmentScriptsDir(ALPHAPEPTDEEP_PYTHON_VERSION, ALPHAPEPTDEEP); + private string CarafePythonVirtualEnvironmentDir => + PythonInstallerUtil.GetPythonVirtualEnvironmentScriptsDir(CARAFE_PYTHON_VERSION, CARAFE); + private string UserDir => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + // TODO(xgwang): update this to the ssh link to the remote repo + private string AlphapeptdeepDiaRepo => Path.Combine(UserDir, WORKSPACES, ALPHAPEPTDEEP_DIA); + // TODO(xgwang): update this to user input value from the dlg + private string ProteinDatabaseFilePath => Path.Combine(UserDir, @"Downloads", @"UP000005640_9606.fasta"); + private string ExperimentDataFilePath => Path.Combine(UserDir, @"Downloads", @"LFQ_Orbitrap_AIF_Human_01.mzML"); + private string ExperimentDataSearchResultFilePath => Path.Combine(UserDir, @"Downloads", @"report.tsv"); + + private string _productPath; + + public string ProductPath { get => _productPath; private set => _productPath = value; } private readonly MessageBoxHelper _helper; private readonly IDocumentUIContainer _documentUiContainer; @@ -102,6 +138,9 @@ public enum LearningOptions { none, libraries, document } private readonly SettingsListComboDriver _driverStandards; private SettingsListBoxDriver _driverLibrary; + private string _lastUpdatedFileName; + private string _lastUpdatedLibName; + public PythonInstaller pythonInstaller { get; private set; } public BuildLibraryDlg(SkylineWindow skylineWindow) { InitializeComponent(); @@ -126,8 +165,13 @@ public BuildLibraryDlg(SkylineWindow skylineWindow) Enumerable.Range(KoinaConstants.MIN_NCE, KoinaConstants.MAX_NCE - KoinaConstants.MIN_NCE + 1).Select(c => (object)c) .ToArray()); ceCombo.SelectedItem = Settings.Default.KoinaNCE; + comboBuildLibraryTarget.SelectedIndex = 0; comboLearnFrom.SelectedIndex = 0; + toolTipProteinDatabase.SetToolTip(labelProteinDatabase, @"A protein database in FASTA format to build library for, this should be a superset of your experiment data samples."); + toolTipTrainingData.SetToolTip(labelTrainingData, @"Peptide detection result from DIA-NN (DIA-NN's main report)."); + toolTipMsMsData.SetToolTip(labelMsMsData, @"DIA-MS data in mzML format used to generate the training data."); + _helper = new MessageBoxHelper(this); _driverStandards = new SettingsListComboDriver(comboStandards, Settings.Default.IrtStandardList); @@ -182,7 +226,7 @@ private void BuildLibraryDlg_FormClosing(object sender, FormClosingEventArgs e) } } - public ILibraryBuilder Builder { get; private set; } + public static ILibraryBuilder Builder { get; internal set; } public IEnumerable InputFileNames { @@ -192,13 +236,24 @@ public IEnumerable InputFileNames public string AddLibraryFile { get; private set; } - private bool ValidateBuilder(bool validateInputFiles) + internal SrmDocument DocumentUI + { + get => _documentUiContainer.DocumentUI; + } + + private bool ValidateBuilder(bool validateInputFiles, bool createDlg = false, bool cleanUp = false, bool newBuilder = false) { string name; if (!_helper.ValidateNameTextBox(textName, out name)) return false; string outputPath = textPath.Text; + + if (newBuilder && Builder != null) + { + ResetBuilder(); + } + if (string.IsNullOrEmpty(outputPath)) { _helper.ShowTextBoxError(textPath, SettingsUIResources.BuildLibraryDlg_ValidateBuilder_You_must_specify_an_output_file_path, outputPath); @@ -247,8 +302,6 @@ private bool ValidateBuilder(bool validateInputFiles) return false; } - var libraryBuildAction = LibraryBuildAction; - if (validateInputFiles) { if (radioKoinaSource.Checked) @@ -258,37 +311,93 @@ private bool ValidateBuilder(bool validateInputFiles) } else if (radioAlphaSource.Checked) { - // TODO: Replace with working AlphaPeptDeep implementation - if (!CreateKoinaBuilder(name, outputPath)) + if (!SetupPythonEnvironmentForAlpha(createDlg)) + { + if (cleanUp) pythonInstaller.CleanUpPythonEnvironment(ALPHAPEPTDEEP); return false; + + } + + if (Builder == null && newBuilder) + { + Builder = new AlphapeptdeepLibraryBuilder(name, outputPath, + AlphapeptdeepPythonVirtualEnvironmentDir, DocumentUI); + BuilderLibFilepath = Builder.BuilderLibraryPath; + } } else if (radioCarafeSource.Checked) { + // handle options for comboBuildLibraryTarget dropdown + if (comboBuildLibraryTarget.SelectedIndex == (int)BuildLibraryTargetOptions.currentSkylineDocument) + { + //TODO throw new NotImplementedException(@"Need to implement skyline document support."); + } + else if (comboBuildLibraryTarget.SelectedIndex != (int)BuildLibraryTargetOptions.currentSkylineDocument) + { + throw new NotSupportedException(@$"Index {comboBuildLibraryTarget.SelectedIndex} of comboBuildLibraryTarget is not yet supported."); + } + else + { + throw new NotSupportedException( + @$"Index {comboBuildLibraryTarget.SelectedIndex} of comboBuildLibraryTarget is not yet supported."); + } + + // handle options for comboLearnFrom dropdown string learningDocPath = string.Empty; IList learningLibraries = new List(); - if (comboLearnFrom.SelectedIndex == (int)LearningOptions.document) + string trainingDataFilePath = string.Empty; + string msMsDataFilePath = string.Empty; + switch (comboLearnFrom.SelectedIndex) { - learningDocPath = textLearningDoc.Text; - if (!PathEx.HasExtension(learningDocPath, SrmDocument.EXT) || !File.Exists(learningDocPath)) - { - _helper.ShowTextBoxError(textPath, SettingsUIResources.BuildLibraryDlg_ValidateBuilder_You_must_specify_a_valid_path_to_a_Skyline_document_to_learn_from_, learningDocPath); - return false; - } - - // CONSIDER: Could also check for the ChromatogramCache.EXT file as a short-cut for full results checking + case (int)LearningOptions.files: + trainingDataFilePath = textBoxTrainingData.Text; + msMsDataFilePath = textBoxMsMsData.Text; + if (!File.Exists(trainingDataFilePath)) + { + _helper.ShowTextBoxError(textBoxTrainingData, @$"{trainingDataFilePath} does not exist."); + return false; + } + if (!File.Exists(msMsDataFilePath)) + { + _helper.ShowTextBoxError(textBoxMsMsData, @$"{msMsDataFilePath} does not exist."); + return false; + } + break; + + case (int)LearningOptions.libraries: + learningLibraries.AddRange(_driverLibrary.GetChosen(null)); + // TODO: Probably need to validate that all the libraries can be loaded into memory with progress UI + goto default; + + case (int)LearningOptions.document: + learningDocPath = textLearningDoc.Text; + if (!PathEx.HasExtension(learningDocPath, SrmDocument.EXT) || !File.Exists(learningDocPath)) + { + _helper.ShowTextBoxError(textPath, SettingsUIResources.BuildLibraryDlg_ValidateBuilder_You_must_specify_a_valid_path_to_a_Skyline_document_to_learn_from_, learningDocPath); + return false; + } + // CONSIDER: Could also check for the ChromatogramCache.EXT file as a short-cut for full results checking + // TODO: Probably need to load the document int memory with progress UI and validate that it has results + goto default; + + default: + throw new NotSupportedException( + @$"Index {comboLearnFrom.SelectedIndex} of comboLearnFrom dropdown is not yet supported."); + } - // TODO: Probably need to load the document int memory with progress UI and validate that it has results + if (!SetupPythonEnvironmentForCarafe(createDlg)) + { + pythonInstaller.CleanUpPythonEnvironment(CARAFE); + return false; } - else if (comboLearnFrom.SelectedIndex == (int)LearningOptions.libraries) + + if (Builder == null && newBuilder) { - learningLibraries.AddRange(_driverLibrary.GetChosen(null)); + Builder = new CarafeLibraryBuilder(name, outputPath, CARAFE_PYTHON_VERSION, CARAFE, + msMsDataFilePath, trainingDataFilePath, DocumentUI); + BuilderLibFilepath = Builder.BuilderLibraryPath; - // TODO: Probably need to validate that all the libraries can be loaded into memory with progress UI } - - // TODO: Create CarafeLibraryBuilder class with everything necessary to build a library - if (!CreateKoinaBuilder(name, outputPath)) - return false; } else { @@ -317,21 +426,33 @@ private bool ValidateBuilder(bool validateInputFiles) } } - Builder = new BiblioSpecLiteBuilder(name, outputPath, InputFileNames.ToArray(), targetPeptidesChosen) + if (Builder == null && newBuilder) { - Action = libraryBuildAction, - IncludeAmbiguousMatches = cbIncludeAmbiguousMatches.Checked, - KeepRedundant = LibraryKeepRedundant, - ScoreThresholdsByFile = thresholdsByFile, - Id = Helpers.MakeId(textName.Text), - IrtStandard = _driverStandards.SelectedItem, - PreferEmbeddedSpectra = PreferEmbeddedSpectra - }; + Builder = new BiblioSpecLiteBuilder(name, outputPath, InputFileNames.ToArray(), targetPeptidesChosen) + { + Action = LibraryBuildAction, + IncludeAmbiguousMatches = cbIncludeAmbiguousMatches.Checked, + KeepRedundant = LibraryKeepRedundant, + ScoreThresholdsByFile = thresholdsByFile, + Id = Helpers.MakeId(textName.Text), + IrtStandard = _driverStandards.SelectedItem, + PreferEmbeddedSpectra = PreferEmbeddedSpectra + }; + BuilderLibFilepath = Builder.BuilderLibraryPath; + } } } return true; } + private string _libFilepath; + + public string BuilderLibFilepath + { + get => _libFilepath; + set => _libFilepath = value; + } + private bool CreateKoinaBuilder(string name, string outputPath, int nce = 27) { // TODO: Need to figure out a better way to do this, use KoinaPeptidePrecursorPair? @@ -362,8 +483,18 @@ private bool CreateKoinaBuilder(string name, string outputPath, int nce = 27) KoinaUIHelpers.CheckKoinaSettings(this, _skylineWindow); // Still construct the library builder, otherwise a user might configure Koina // incorrectly, causing the build to silently fail + if (Builder != null) + { + ResetBuilder(); + } + Builder = new KoinaLibraryBuilder(doc, name, outputPath, () => true, IrtStandard, - peptidesPerPrecursor, precursors, nce); + peptidesPerPrecursor, precursors, nce); + + BuilderLibFilepath = Builder.BuilderLibraryPath; + + + } catch (Exception ex) { @@ -373,6 +504,141 @@ private bool CreateKoinaBuilder(string name, string outputPath, int nce = 27) return true; } + + + private bool SetupPythonEnvironmentForAlpha(bool createDlg = true) + { + var programPathContainer = new ProgramPathContainer(PYTHON, ALPHAPEPTDEEP_PYTHON_VERSION); + var packages = new List() + { + new PythonPackage {Name = @"peptdeep", Version = null }, + // We manually set numpy to the latest version before 2.0 because of a backward incompatibility issue + // See details for tracking issue in AlphaPeptDeep repo: https://github.com/MannLabs/alphapeptdeep/issues/190 + // TODO: delete the following line after the issue above is resolved + new PythonPackage {Name = @"numpy", Version = @"1.26.4" } + }; + + if (pythonInstaller == null) + pythonInstaller = new PythonInstaller(programPathContainer, packages, new TextBoxStreamWriterHelper(), new PythonInstallerTaskValidator(), ALPHAPEPTDEEP); + else + pythonInstaller.ClearPendingTasks(); + + + Cursor = Cursors.WaitCursor; + btnNext.Enabled = false; + + if (pythonInstaller.IsPythonVirtualEnvironmentReady() && pythonInstaller.IsNvidiaEnvironmentReady()) + { + Cursor = Cursors.Default; + btnNext.Enabled = true; + return true; + } + else if (!createDlg) + { + Cursor = Cursors.Default; + btnNext.Enabled = true; + return false; + } + + if (!pythonInstaller.IsPythonVirtualEnvironmentReady()) + { + + PythonDlg = new MultiButtonMsgDlg( + string.Format( + ToolsUIResources.PythonInstaller_BuildPrecursorTable_Python_0_installation_is_required, + ALPHAPEPTDEEP_PYTHON_VERSION, @"AlphaPeptDeep"), string.Format(Resources.OK)); + if (PythonDlg.ShowDialog(this) == DialogResult.Cancel) + { + PythonDlg.Dispose(); + PythonInstallerUI.Dispose(); + Cursor = Cursors.Default; + btnNext.Enabled = true; + return false; + } + if (DialogResult.Cancel == PythonInstallerUI.InstallPythonVirtualEnvironment(this, pythonInstaller)) + { + if (!PythonDlg.IsDisposed) PythonDlg.Dispose(); + PythonInstallerUI.Dispose(); + Cursor = Cursors.Default; + btnNext.Enabled = true; + return false; + } + PythonInstallerUI.Dispose(); + + } + else if (!pythonInstaller.IsNvidiaEnvironmentReady()) + { + if (DialogResult.Cancel == PythonInstallerUI.InstallPythonVirtualEnvironment(this, pythonInstaller)) + { + PythonInstallerUI.Dispose(); + Cursor = Cursors.Default; + btnNext.Enabled = true; + return false; + } + PythonInstallerUI.Dispose(); + + } + Cursor = Cursors.Default; + btnNext.Enabled = true; + return true; + } + private bool SetupPythonEnvironmentForCarafe(bool createDlg = true) + { + var programPathContainer = new ProgramPathContainer(PYTHON, CARAFE_PYTHON_VERSION); + var packages = new List() + { + new PythonPackage + { Name = PEPTDEEP, Version = @$"git+file:///{AlphapeptdeepDiaRepo.Replace('\\', '/')}" }, + new PythonPackage { Name = @"alphabase", Version = @"1.2.1" }, + new PythonPackage { Name = @"numpy", Version = @"1.26.4" }, + new PythonPackage { Name = @"transformers", Version = @"4.36.1" }, + new PythonPackage { Name = @"wheel", Version = null } + }; + + if (pythonInstaller == null) + pythonInstaller = new PythonInstaller(programPathContainer, packages, new TextBoxStreamWriterHelper(), new PythonInstallerTaskValidator(), CARAFE); + else + pythonInstaller.ClearPendingTasks(); + + if (pythonInstaller.IsPythonVirtualEnvironmentReady() && pythonInstaller.IsNvidiaEnvironmentReady()) + { + return true; + } + else if (!createDlg) + { + return false; + } + + if (!pythonInstaller.IsPythonVirtualEnvironmentReady()) + { + + PythonDlg = new MultiButtonMsgDlg( + string.Format( + ToolsUIResources.PythonInstaller_BuildPrecursorTable_Python_0_installation_is_required, + CARAFE_PYTHON_VERSION, @"Carafe"), string.Format(Resources.OK)); + if (PythonDlg.ShowDialog(this) == DialogResult.Cancel) + { + PythonDlg.Dispose(); + PythonInstallerUI.Dispose(); + return false; + } + } + else if (!pythonInstaller.IsNvidiaEnvironmentReady()) + { + if (DialogResult.Cancel == PythonInstallerUI.InstallPythonVirtualEnvironment(this, pythonInstaller)) + { + PythonDlg.Dispose(); + PythonInstallerUI.Dispose(); + return false; + } + PythonInstallerUI.Dispose(); + + } + if (!PythonDlg.IsDisposed) PythonDlg.Dispose(); + return true; + + } + private void textName_TextChanged(object sender, EventArgs e) { @@ -391,9 +657,17 @@ private void textName_TextChanged(object sender, EventArgs e) } } string id = (name.Length == 0 ? string.Empty : Helpers.MakeId(textName.Text)); - textPath.Text = id.Length == 0 - ? outputPath - : Path.Combine(outputPath, id + BiblioSpecLiteSpec.EXT); + + if (_lastUpdatedFileName.IsNullOrEmpty() || _lastUpdatedFileName == _lastUpdatedLibName) + { + textPath.Text = id.Length == 0 + ? outputPath + : Path.Combine(outputPath, id + BiblioSpecLiteSpec.EXT); + _lastUpdatedFileName = id; + _lastUpdatedLibName = id; + + } + } private void btnBrowse_Click(object sender, EventArgs e) @@ -418,8 +692,8 @@ private void btnBrowse_Click(object sender, EventArgs e) if (dlg.ShowDialog(this) == DialogResult.OK) { Settings.Default.LibraryDirectory = Path.GetDirectoryName(dlg.FileName); - textPath.Text = dlg.FileName; + _lastUpdatedFileName = Path.GetFileNameWithoutExtension(dlg.FileName); } } } @@ -429,18 +703,29 @@ private void btnNext_Click(object sender, EventArgs e) OkWizardPage(); } + public bool PythonRequirementMet() + { + if (radioCarafeSource.Checked || radioAlphaSource.Checked || radioKoinaSource.Checked) + { + return ValidateBuilder(true); + } + + return ValidateBuilder(false); + + } public void OkWizardPage() { + Cursor.Current = Cursors.WaitCursor; if (tabControlMain.SelectedIndex != (int)Pages.properties || radioAlphaSource.Checked || radioKoinaSource.Checked) { - if (ValidateBuilder(true)) + if (ValidateBuilder(true, true, true, true)) { Settings.Default.LibraryFilterDocumentPeptides = LibraryFilterPeptides; Settings.Default.LibraryKeepRedundant = LibraryKeepRedundant; DialogResult = DialogResult.OK; } } - else if (ValidateBuilder(false)) + else if (ValidateBuilder(false, true, false, true)) { Settings.Default.LibraryDirectory = Path.GetDirectoryName(LibraryPath); @@ -454,7 +739,8 @@ public void OkWizardPage() btnNext.Enabled = Grid.IsReady; else btnNext.Enabled = true; - } + } + Cursor.Current = Cursors.Default; } private void btnPrevious_Click(object sender, EventArgs e) @@ -777,6 +1063,16 @@ public bool Koina get { return radioKoinaSource.Checked; } set { radioKoinaSource.Checked = value; } } + public bool AlphaPeptDeep + { + get { return radioAlphaSource.Checked; } + set { radioAlphaSource.Checked = value; } + } + public bool Carafe + { + get { return radioCarafeSource.Checked; } + set { radioCarafeSource.Checked = value; } + } public int NCE { @@ -942,5 +1238,58 @@ private void btnLearningDocBrowse_Click(object sender, EventArgs e) } } + + private void comboBuildLibraryTarget_SelectedIndexChanged(object sender, EventArgs e) + { + tabControlBuildLibraryTarget.SelectedIndex = comboBuildLibraryTarget.SelectedIndex; + } + + private void buttonProteinDatabase_Click(object sender, EventArgs e) + { + using var dlg = new OpenFileDialog(); + dlg.Title = @"Select Protein Database File"; + dlg.InitialDirectory = Settings.Default.ActiveDirectory; + dlg.CheckPathExists = true; + dlg.Multiselect = false; + dlg.SupportMultiDottedExtensions = true; + dlg.DefaultExt = DataSourceUtil.EXT_FASTA[0]; + dlg.Filter = TextUtil.FileDialogFiltersAll(TextUtil.FileDialogFilter(EditUIResources.OpenFileDialog_FASTA_files, DataSourceUtil.EXT_FASTA)); + if (dlg.ShowDialog(this) == DialogResult.OK) + { + textBoxProteinDatabase.Text = dlg.FileName; + } + } + + private void buttonTrainingData_Click(object sender, EventArgs e) + { + using var dlg = new OpenFileDialog(); + dlg.Title = @"Select Training Data File"; + dlg.InitialDirectory = Settings.Default.ActiveDirectory; + dlg.CheckPathExists = true; + dlg.Multiselect = false; + dlg.SupportMultiDottedExtensions = true; + dlg.DefaultExt = TextUtil.EXT_TSV; + dlg.Filter = TextUtil.FileDialogFiltersAll(TextUtil.FILTER_TSV); + if (dlg.ShowDialog(this) == DialogResult.OK) + { + textBoxTrainingData.Text = dlg.FileName; + } + } + + private void buttonMsMsData_Click(object sender, EventArgs e) + { + using var dlg = new OpenFileDialog(); + dlg.Title = @"Select Ms/Ms Data File"; + dlg.InitialDirectory = Settings.Default.ActiveDirectory; + dlg.CheckPathExists = true; + dlg.Multiselect = false; + dlg.SupportMultiDottedExtensions = true; + dlg.DefaultExt = DataSourceUtil.EXT_MZML; + dlg.Filter = TextUtil.FileDialogFiltersAll(TextUtil.FileDialogFilter(@"mzML files", DataSourceUtil.EXT_MZML)); + if (dlg.ShowDialog(this) == DialogResult.OK) + { + textBoxMsMsData.Text = dlg.FileName; + } + } } } diff --git a/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.resx b/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.resx index bf817b0335..0ae9ba0d64 100644 --- a/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.resx +++ b/pwiz_tools/Skyline/SettingsUI/BuildLibraryDlg.resx @@ -181,7 +181,7 @@ Bottom, Right - 492, 339 + 492, 361 75, 23 @@ -208,7 +208,7 @@ Bottom, Right - 411, 339 + 411, 361 75, 23 @@ -682,6 +682,18 @@ you can append more spectra in the future. Info/Settings + + koinaInfoSettingsBtn + + + System.Windows.Forms.LinkLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dataSourceGroupBox + + + 2 + True @@ -700,6 +712,18 @@ you can append more spectra in the future. &Koina + + radioKoinaSource + + + System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dataSourceGroupBox + + + 3 + True @@ -715,6 +739,18 @@ you can append more spectra in the future. &Files + + radioFilesSource + + + System.Windows.Forms.RadioButton, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + dataSourceGroupBox + + + 4 + Top, Right @@ -749,11 +785,23 @@ you can append more spectra in the future. 6, 25 - 460, 273 + 460, 353 1 + + gridInputFiles + + + pwiz.Skyline.FileUI.PeptideSearch.BuildLibraryGridView, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null + + + tabFiles + + + 0 + Top, Right @@ -769,6 +817,18 @@ you can append more spectra in the future. Add Pat&hs... + + btnAddPaths + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabFiles + + + 1 + True @@ -784,6 +844,18 @@ you can append more spectra in the future. &Input Files: + + label7 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabFiles + + + 3 + Top, Right @@ -803,6 +875,18 @@ you can append more spectra in the future. Add all acceptable spectrum files found below a root directory. You can uncheck ones you do not want added to the library. + + btnAddDirectory + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabFiles + + + 4 + Top, Right @@ -821,6 +905,18 @@ You can uncheck ones you do not want added to the library. Add a file or multiple files from a single directory. + + btnAddFile + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabFiles + + + 2 + Bottom, Right @@ -828,7 +924,7 @@ You can uncheck ones you do not want added to the library. False - 330, 339 + 330, 361 75, 23 @@ -854,71 +950,11 @@ You can uncheck ones you do not want added to the library. Top, Bottom, Left, Right - - Top, Left, Right - - - tabFilesSource - - - System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - tabControlDataSource - - - 0 - - - tabAlphaSource - - - System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - tabControlDataSource - - - 1 - - - tabCarafeSource - - - System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - tabControlDataSource - - - 2 - - - tabKoinaSource - - - System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - tabControlDataSource - - - 3 - - - 226, 124 - - - 334, 156 - - - 6 - tabControlDataSource - pwiz.Skyline.Controls.WizardPages, Skyline-daily, Version=23.1.1.555, Culture=neutral, PublicKeyToken=null + pwiz.Skyline.Controls.WizardPages, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null tabProperties @@ -933,7 +969,7 @@ You can uncheck ones you do not want added to the library. 3, 3, 3, 3 - 571, 304 + 571, 326 0 @@ -960,7 +996,7 @@ You can uncheck ones you do not want added to the library. 3, 3, 3, 3 - 571, 304 + 571, 326 1 @@ -980,215 +1016,710 @@ You can uncheck ones you do not want added to the library. 1 - - tabControlLearning - - - pwiz.Skyline.Controls.WizardPages, Skyline-daily, Version=23.1.1.555, Culture=neutral, PublicKeyToken=null + + Top, Bottom, Left, Right - - tabLearn + + True - - 0 + + NoControl - - comboLearnFrom + + 4, 7 - - System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 72, 13 - - tabLearn + + 8 - - 1 + + &Tuning data: - - label1 + + labelTrainingData - + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - tabLearn + + tabWithFiles - - 2 + + 0 - - 4, 22 + + Top, Right - - 571, 304 + + NoControl - - 2 + + 468, 21 - - Learn + + 75, 23 - - tabLearn + + 7 - - System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + &Browse... - - tabControlMain + + buttonTrainingData - - 2 + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 3, 3 + + tabWithFiles - - 579, 330 + + 1 - - 0 + + Top, Left, Right - - tabControlMain + + 7, 23 - - pwiz.Skyline.Controls.WizardPages, Skyline-daily, Version=23.1.1.555, Culture=neutral, PublicKeyToken=null + + 455, 20 - - $this + + 6 - - 0 + + textBoxTrainingData - - 4, 22 + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 3, 3, 3, 3 + + tabWithFiles - - 326, 130 + + 2 - - 0 + + True - - Files + + NoControl - - 4, 22 + + 4, 61 - - 326, 130 + + 71, 13 - - 2 + + 9 - - Carafe + + &MS/MS data: - - 4, 22 + + labelMsMsData - - 3, 3, 3, 3 + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 326, 130 + + tabWithFiles - - 1 + + 3 - - Koina + + Top, Right - - Top, Bottom, Left, Right + + NoControl - - tabPageDocument + + 468, 75 - - System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 75, 23 - - tabControlLearning + + 5 - - 0 + + &Browse... - - tabPageLibraries + + buttonMsMsData - - System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - tabControlLearning + + tabWithFiles - - 1 + + 4 - - 1, 56 + + Top, Left, Right - - 559, 252 + + 7, 77 - - 2 + + 455, 20 + + + 4 + + + textBoxMsMsData + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabWithFiles + + + 5 + + + 4, 22 + + + 3, 3, 3, 3 + + + 551, 110 + + + 2 + + + Files + + + tabWithFiles + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlLearning + + + 0 btnLearningDocBrowse - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPageDocument + + + 0 + + + textLearningDoc + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPageDocument + + + 1 + + + label5 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPageDocument + + + 2 + + + 4, 22 + + + 3, 3, 3, 3 + + + 551, 110 + + + 1 + + + Results + + + tabPageDocument + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlLearning + + + 1 + + + listLibraries + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPageLibraries + + + 0 + + + label3 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPageLibraries + + + 1 + + + 4, 22 + + + 3, 3, 3, 3 + + + 551, 110 + + + 0 + + + Libraries + + + tabPageLibraries + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlLearning + + + 2 + + + 3, 187 + + + 559, 136 + + + 2 + + + tabControlLearning + + + pwiz.Skyline.Controls.WizardPages, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null + + + tabLearn + + + 0 + + + Files + + + 9, 155 + + + 273, 21 + + + 1 + + + comboLearnFrom + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabLearn + + + 1 + + + True + + + 6, 139 + + + 76, 13 + + + 0 + + + &Fine-tune with: + + + labelLearnFrom + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabLearn + + + 2 + + + Top, Bottom, Left, Right + + + tabPage1 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlBuildLibraryTarget + + + 0 + + + tabPage2 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlBuildLibraryTarget + + + 1 + + + 3, 60 + + + 559, 75 + + + 5 + + + tabControlBuildLibraryTarget + + + pwiz.Skyline.Controls.WizardPages, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null + + + tabLearn + + + 3 + + + Current Skyline Document + + + 9, 28 + + + 273, 21 + + + 4 + + + comboBuildLibraryTarget + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabLearn + + + 4 + + + True + + + NoControl + + + 6, 12 + + + 78, 13 + + + 3 + + + &Build library for: + + + labelBuildLibraryTarget + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabLearn + + + 5 + + + 4, 22 + + + 571, 326 + + + 2 + + + Learn + + + tabLearn + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlMain + + + 2 + + + 3, 3 + + + 579, 352 + + + 0 + + + tabControlMain + + + pwiz.Skyline.Controls.WizardPages, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null + + + $this + + + 0 + + + Top, Left, Right + + + tabFilesSource + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlDataSource + + + 0 + + + tabAlphaSource + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlDataSource + + + 1 + + + tabCarafeSource + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlDataSource + + + 2 + + + tabKoinaSource + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlDataSource + + + 3 + + + 226, 124 + + + 334, 156 + + + 6 + + + tabControlDataSource + + + pwiz.Skyline.Controls.WizardPages, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null + + + tabProperties + + + 0 + + + 4, 22 + + + 3, 3, 3, 3 + + + 326, 130 + + + 0 + + + Files + + + tabFilesSource + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlDataSource + + + 0 + + + 4, 22 + + + 326, 130 + + + 3 + + + AlphaPeptDeep + + + tabAlphaSource - - tabPageDocument + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 0 + + tabControlDataSource - - textLearningDoc + + 1 - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 4, 22 - - tabPageDocument + + 326, 130 - - 1 + + 2 - - label5 + + Carafe - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + tabCarafeSource - - tabPageDocument + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + tabControlDataSource + + 2 - + 4, 22 - + 3, 3, 3, 3 - - 551, 226 + + 326, 130 - + 1 - - Results + + Koina + + + tabKoinaSource + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlDataSource + + + 3 Top, Right @@ -1205,6 +1736,18 @@ You can uncheck ones you do not want added to the library. &Browse... + + btnLearningDocBrowse + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPageDocument + + + 0 + Top, Left, Right @@ -1217,6 +1760,18 @@ You can uncheck ones you do not want added to the library. 1 + + textLearningDoc + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPageDocument + + + 1 + True @@ -1232,6 +1787,30 @@ You can uncheck ones you do not want added to the library. L&earning document: + + label5 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPageDocument + + + 2 + + + Top, Bottom, Left, Right + + + 7, 24 + + + 537, 109 + + + 1 + listLibraries @@ -1244,6 +1823,21 @@ You can uncheck ones you do not want added to the library. 0 + + True + + + 7, 7 + + + 93, 13 + + + 0 + + + L&earning Libraries: + label3 @@ -1256,90 +1850,228 @@ You can uncheck ones you do not want added to the library. 1 - + 4, 22 - + 3, 3, 3, 3 - - 551, 226 + + 551, 49 - + + 1 + + + CurrentSkylineDocument + + + tabPage1 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlBuildLibraryTarget + + 0 - - Libraries + + labelProteinDatabase - - Top, Bottom, Left, Right + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 7, 24 + + tabPage2 - - 537, 184 + + 0 - + + buttonProteinDatabase + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + 1 - + + textBoxProteinDatabase + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 2 + + + label6 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 3 + + + 4, 22 + + + 3, 3, 3, 3 + + + 551, 49 + + + 0 + + + ProteinDatabase + + + tabPage2 + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabControlBuildLibraryTarget + + + 1 + + True - - 7, 7 + + NoControl - - 93, 13 + + 7, 3 - + + 90, 13 + + + 4 + + + &Protein database: + + + labelProteinDatabase + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + 0 - - L&earning Libraries: + + Top, Right - - Skyline Results + + NoControl - - Libraries + + 468, 17 - - 9, 21 + + 75, 23 - - 273, 21 + + 3 - + + &Browse... + + + buttonProteinDatabase + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + 1 - + + Top, Left, Right + + + 7, 19 + + + 455, 20 + + + 2 + + + textBoxProteinDatabase + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tabPage2 + + + 2 + + True - - 6, 4 + + NoControl + + + 7, 7 - - 60, 13 + + 0, 13 - + 0 - - &Learn from: + + label6 - - 4, 22 + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 326, 130 + + tabPage2 - + 3 - - AlphaPeptDeep - + + 248, 17 + + + 425, 17 + + + 582, 17 + True @@ -1347,10 +2079,7 @@ You can uncheck ones you do not want added to the library. 6, 13 - 579, 374 - - - NoControl + 579, 396 CenterParent @@ -1362,7 +2091,7 @@ You can uncheck ones you do not want added to the library. modeUIHandler - pwiz.Skyline.Util.Helpers+ModeUIExtender, Skyline-daily, Version=23.1.1.555, Culture=neutral, PublicKeyToken=null + pwiz.Skyline.Util.Helpers+ModeUIExtender, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null helpTip @@ -1370,10 +2099,28 @@ You can uncheck ones you do not want added to the library. System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + toolTipProteinDatabase + + + System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolTipTrainingData + + + System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolTipMsMsData + + + System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + BuildLibraryDlg - pwiz.Skyline.Util.FormEx, Skyline-daily, Version=23.1.1.555, Culture=neutral, PublicKeyToken=null + pwiz.Skyline.Util.FormEx, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null \ No newline at end of file diff --git a/pwiz_tools/Skyline/SettingsUI/PeptideSettingsUI.cs b/pwiz_tools/Skyline/SettingsUI/PeptideSettingsUI.cs index 77882a01af..718bf267a9 100644 --- a/pwiz_tools/Skyline/SettingsUI/PeptideSettingsUI.cs +++ b/pwiz_tools/Skyline/SettingsUI/PeptideSettingsUI.cs @@ -32,6 +32,7 @@ using pwiz.Skyline.Model.DocSettings.AbsoluteQuantification; using pwiz.Skyline.Model.GroupComparison; using pwiz.Skyline.Model.Irt; +using pwiz.Skyline.Model.Koina.Models; using pwiz.Skyline.Model.Lib; using pwiz.Skyline.Model.Lib.Midas; using pwiz.Skyline.Model.Proteome; @@ -736,6 +737,10 @@ public void ShowBuildLibraryDlg() dlg.LibraryKeepRedundant = _parent.DocumentUI.Settings.TransitionSettings.FullScan.IsEnabled; if (dlg.ShowDialog(this) == DialogResult.OK) { + + Cursor.Current = Cursors.WaitCursor; + + if (!string.IsNullOrEmpty(dlg.AddLibraryFile)) { using var editLibDlg = new EditLibraryDlg(Settings.Default.SpectralLibraryList); @@ -749,7 +754,9 @@ public void ShowBuildLibraryDlg() return; } - BuildLibrary(dlg.Builder); + Cursor.Current = Cursors.Default; + + BuildLibrary(BuildLibraryDlg.Builder); } } @@ -758,12 +765,31 @@ private void BuildLibrary(ILibraryBuilder builder) IsBuildingLibrary = true; var buildState = new BuildState(builder.LibrarySpec, _libraryManager.BuildLibraryBackground); + + var warningMods = builder.LibraryHelper.GetWarningMods(builder.Document, builder.ToolName); + if (warningMods.Count > 0) + { + string warningModString = string.Join(Environment.NewLine, warningMods); + AlertDlg warnMessageDlg = + new AlertDlg( + string.Format(ModelResources.Alphapeptdeep_Warn_unknown_modification, + warningModString), MessageBoxButtons.OKCancel); + var warnModChoice = warnMessageDlg.ShowDialog(); + + if (warnModChoice == DialogResult.Cancel) + { + return; + } + } + + //_libraryManager.TestModifications(); bool retry; do { using (var longWaitDlg = new LongWaitDlg(_parent)) { + longWaitDlg.Text = string.Format(ModelsResources.BuildingPrecursorTable_Building_library); var status = longWaitDlg.PerformWork(_parent, 500, progressMonitor => _libraryManager.BuildLibraryBackground(_parent, builder, progressMonitor, buildState)); @@ -792,6 +818,11 @@ private void BuildLibrary(ILibraryBuilder builder) MessageDlg.ShowException(this, status.ErrorException); } } + else if (status.IsCanceled) + { + return; + } + retry = false; } } while (retry); diff --git a/pwiz_tools/Skyline/Skyline.cs b/pwiz_tools/Skyline/Skyline.cs index 450957d27e..9673f83c2c 100644 --- a/pwiz_tools/Skyline/Skyline.cs +++ b/pwiz_tools/Skyline/Skyline.cs @@ -4360,7 +4360,7 @@ public string InstallProgram(ProgramPathContainer programPathContainer, ICollect // Here we just ignore all the versions attached to packages. IEnumerable pythonPackages = packages.Select(p => p.Name); - using (var dlg = new PythonInstaller(programPathContainer, pythonPackages, _skylineTextBoxStreamWriterHelper)) + using (var dlg = new PythonInstallerLegacyDlg(programPathContainer, pythonPackages, _skylineTextBoxStreamWriterHelper)) { if (dlg.ShowDialog(this) == DialogResult.Cancel) return null; diff --git a/pwiz_tools/Skyline/Skyline.csproj b/pwiz_tools/Skyline/Skyline.csproj index 98f473f44d..16ea7df016 100644 --- a/pwiz_tools/Skyline/Skyline.csproj +++ b/pwiz_tools/Skyline/Skyline.csproj @@ -104,6 +104,7 @@ false + False ..\Shared\Lib\DigitalRune.Windows.Docking.dll @@ -518,6 +519,7 @@ UserControl + True True @@ -548,6 +550,7 @@ True PropertyNames.resx + @@ -783,6 +786,12 @@ + + + True + True + ToolsResources.resx + @@ -873,11 +882,6 @@ True ListsResources.resx - - True - True - ToolsResources.resx - True True @@ -888,6 +892,18 @@ True FileUIResources.resx + + Form + + + PythonInstallerDlg.cs + + + + True + True + ToolsUIResources.resx + True @@ -944,11 +960,6 @@ True EntitiesResources.resx - - True - True - ToolsUIResources.resx - True True @@ -4893,14 +4904,17 @@ EditServerDlg.cs - - PythonInstaller.cs + + PythonInstallerDlg.cs + + + PythonInstallerLegacyDlg.cs - - PythonInstaller.cs + + PythonInstallerLegacyDlg.cs - - PythonInstaller.cs + + PythonInstallerLegacyDlg.cs RInstaller.cs @@ -5023,11 +5037,11 @@ EditServerDlg.cs - + Form - - PythonInstaller.cs + + PythonInstallerLegacyDlg.cs Form diff --git a/pwiz_tools/Skyline/SkylineNightly/SkylineNightly.cs b/pwiz_tools/Skyline/SkylineNightly/SkylineNightly.cs index b340b183f7..f1f91d3162 100644 --- a/pwiz_tools/Skyline/SkylineNightly/SkylineNightly.cs +++ b/pwiz_tools/Skyline/SkylineNightly/SkylineNightly.cs @@ -22,6 +22,7 @@ using System.IO; using System.Reflection; using System.Windows.Forms; +using Microsoft.Win32; using Microsoft.Win32.TaskScheduler; using SkylineNightly.Properties; @@ -29,6 +30,10 @@ namespace SkylineNightly { public partial class SkylineNightly : Form { + private static string REG_FILESYSTEM_KEY = @"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem"; + private static string REG_LONGPATHS_ENABLED = @"LongPathsEnabled"; + private static int REG_LONGPATH_VALUE = 1; + public static readonly Nightly.RunMode[] RunModes = { Nightly.RunMode.trunk, Nightly.RunMode.perf, Nightly.RunMode.release, Nightly.RunMode.stress, Nightly.RunMode.integration, Nightly.RunMode.release_perf, Nightly.RunMode.integration_perf }; @@ -133,6 +138,9 @@ private void OK(object sender, EventArgs e) // Register the task in the root folder ts.RootFolder.RegisterTaskDefinition(Nightly.NightlyTaskNameWithUser, td); + + // Registry setting LongPathsEnabled here for python to install pip and configure packages required to run AlphaPeptDeep + Registry.SetValue(REG_FILESYSTEM_KEY, REG_LONGPATHS_ENABLED, REG_LONGPATH_VALUE); } } } diff --git a/pwiz_tools/Skyline/Test/AlphapeptdeepLibraryBuilderTest.cs b/pwiz_tools/Skyline/Test/AlphapeptdeepLibraryBuilderTest.cs new file mode 100644 index 0000000000..0d88783501 --- /dev/null +++ b/pwiz_tools/Skyline/Test/AlphapeptdeepLibraryBuilderTest.cs @@ -0,0 +1,40 @@ +/* + * Author: David Shteynberg , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2025 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; +using pwiz.Skyline.Model.AlphaPeptDeep; +using pwiz.SkylineTestUtil; + +namespace pwiz.SkylineTest +{ + [TestClass] + public class AlphapeptdeepLibraryBuilderTest : AbstractUnitTest + { + [TestMethod] + public void TestModificationInfo() + { + var groupedByAccession = LibraryHelper.AlphapeptdeepModificationNames.GroupBy(item => item.Accession); + foreach (var group in groupedByAccession) + { + Assert.AreEqual(1, group.Count(), "Duplicate accession {0}", group.Key); + } + } + } +} diff --git a/pwiz_tools/Skyline/Test/CodeInspectionTest.cs b/pwiz_tools/Skyline/Test/CodeInspectionTest.cs index 7e6e16e7ae..d476cdf3bf 100644 --- a/pwiz_tools/Skyline/Test/CodeInspectionTest.cs +++ b/pwiz_tools/Skyline/Test/CodeInspectionTest.cs @@ -184,7 +184,7 @@ void AddForbiddenUIInspection(string fileMask, string cue, string why, int numbe numberToleratedAsWarnings); // Number of existing known failures that we'll tolerate as warnings instead of errors, so no more get added while we wait to fix the rest } - AddForbiddenUIInspection(@"*.cs", @"namespace pwiz.Skyline.Model", @"Skyline model code must not depend on UI code", 37); + AddForbiddenUIInspection(@"*.cs", @"namespace pwiz.Skyline.Model", @"Skyline model code must not depend on UI code", 36); // Looking for CommandLine.cs and CommandArgs.cs code depending on UI code AddForbiddenUIInspection(@"CommandLine.cs", @"namespace pwiz.Skyline", @"CommandLine code must not depend on UI code", 2); AddForbiddenUIInspection(@"CommandArgs.cs", @"namespace pwiz.Skyline", @"CommandArgs code must not depend on UI code"); diff --git a/pwiz_tools/Skyline/Test/Test.csproj b/pwiz_tools/Skyline/Test/Test.csproj index 0c009f967e..707c6d5aa9 100644 --- a/pwiz_tools/Skyline/Test/Test.csproj +++ b/pwiz_tools/Skyline/Test/Test.csproj @@ -159,6 +159,7 @@ + diff --git a/pwiz_tools/Skyline/TestData/ResourcesTest.cs b/pwiz_tools/Skyline/TestData/ResourcesTest.cs index 77e83b0ce6..5ba38faa4c 100644 --- a/pwiz_tools/Skyline/TestData/ResourcesTest.cs +++ b/pwiz_tools/Skyline/TestData/ResourcesTest.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Reflection; using System.Resources; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -28,6 +29,7 @@ using pwiz.ProteomeDatabase.API; using pwiz.ProteowizardWrapper; using pwiz.Skyline; +using pwiz.Skyline.Util.Extensions; using pwiz.SkylineTestUtil; namespace pwiz.SkylineTestData @@ -57,7 +59,18 @@ public void CheckForMissingResources() typeof(BiblioSpec.BlibBuild).Assembly // BiblioSpec }) { - foreach (var type in assembly.GetTypes()) + Type[] assemblyTypes; + try + { + assemblyTypes = assembly.GetTypes(); + } + catch (ReflectionTypeLoadException reflectionTypeLoadException) + { + // Write out the "LoaderExceptions" property because it is not included in this exception's ToString() + Console.Out.WriteLine("Exception getting types for assembly {0}:\r\n{1}\r\nLoaderExceptions property:{2}", assembly.FullName, reflectionTypeLoadException, TextUtil.LineSeparate(reflectionTypeLoadException.LoaderExceptions?.Select(e=>e.ToString())) ?? "null"); + throw; + } + foreach (var type in assemblyTypes) { if (!IsResourceType(type)) { diff --git a/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest.cs b/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest.cs new file mode 100644 index 0000000000..d0fe3f22fc --- /dev/null +++ b/pwiz_tools/Skyline/TestFunctional/CarafeBuildLibraryTest.cs @@ -0,0 +1,98 @@ +/* + * Author: David Shteynberg , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2025 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using pwiz.Common.SystemUtil; +using pwiz.Skyline.Model.Tools; +using pwiz.Skyline.SettingsUI; +using pwiz.Skyline.Util; +using pwiz.SkylineTestUtil; + +namespace pwiz.SkylineTestFunctional +{ + + [TestClass] + public class CarafeBuildLibraryTest : AbstractFunctionalTestEx + { + + public IAsynchronousDownloadClient TestDownloadClient { get; set; } + private const string TESTDATA_URL = @"https://skyline.ms/_webdav/home/support/file%20sharing/%40files/"; + private const string TESTDATA_FILE = @"CarafeBuildLibraryTest.zip"; + private const string TESTDATA_DIR = @"TestFunctional"; + + + public Uri TestDataPackageUri => new Uri($@"{TESTDATA_URL}{TESTDATA_FILE}"); + //[TestMethod] + public void TestCarafeBuildLibrary() + { + Directory.CreateDirectory(TESTDATA_DIR); + string currentDirectory = Directory.GetCurrentDirectory(); + TestFilesZip = $@"{currentDirectory}\\{TESTDATA_DIR}\\{TESTDATA_FILE}"; + IProgressMonitor progressMonitor = new SilentProgressMonitor(); + DownloadTestDataPackage(progressMonitor); + RunFunctionalTest(); + } + + private void DownloadTestDataPackage(IProgressMonitor progressMonitor) + { + using var webClient = TestDownloadClient ?? new MultiFileAsynchronousDownloadClient(progressMonitor, 1); + if (!webClient.DownloadFileAsync(TestDataPackageUri, TestFilesZip, out var downloadException)) + throw new ToolExecutionException(@"TestData package download failed...", downloadException); + } + + private string LibraryPathWithoutIrt => TestContext.GetTestPath("TestCarafeBuildLibrary\\LibraryWithoutIrt.blib"); + protected override void DoTest() + { + PythonTestUtil pythonUtil = new PythonTestUtil(BuildLibraryDlg.CARAFE_PYTHON_VERSION, @"Carafe", true); + var peptideSettings = ShowPeptideSettings(PeptideSettingsUI.TABS.Library); + var buildLibraryDlg = ShowDialog(peptideSettings.ShowBuildLibraryDlg); + const string libraryWithoutIrt = @"CarafeLibraryWithoutIrt"; + string fineTuningFile = TestFilesDir.GetTestPath(@"report.tsv"); + string mzMLFile = TestFilesDir.GetTestPath(@"LFQ_Orbitrap_AIF_Human_01.mzML"); + + OpenDocument(TestFilesDir.GetTestPath(@"Test-imported.sky/Test-imported.sky")); + RunUI(() => + { + buildLibraryDlg.LibraryName = libraryWithoutIrt; + buildLibraryDlg.LibraryPath = LibraryPathWithoutIrt; + buildLibraryDlg.Carafe = true; + buildLibraryDlg.OkWizardPage(); + buildLibraryDlg.TextBoxMsMsDataFile = mzMLFile; + buildLibraryDlg.TextBoxTrainingDataFile = fineTuningFile; + }); + pythonUtil.InstallPython(buildLibraryDlg); + OkDialog(buildLibraryDlg, buildLibraryDlg.OkWizardPage); + OkDialog(peptideSettings, peptideSettings.OkDialog); + + var spectralLibraryViewer = ShowDialog(SkylineWindow.ViewSpectralLibraries); + RunUI(() => + { + spectralLibraryViewer.ChangeSelectedLibrary(libraryWithoutIrt); + }); + OkDialog(spectralLibraryViewer, spectralLibraryViewer.Close); + } + protected override void Cleanup() + { + DirectoryEx.SafeDelete("TestCarafeBuildLibrary"); + } + } +} + diff --git a/pwiz_tools/Skyline/TestFunctional/PythonInstallerLegacyDlgTest.cs b/pwiz_tools/Skyline/TestFunctional/PythonInstallerLegacyDlgTest.cs new file mode 100644 index 0000000000..ddaec534e4 --- /dev/null +++ b/pwiz_tools/Skyline/TestFunctional/PythonInstallerLegacyDlgTest.cs @@ -0,0 +1,543 @@ +/* + * Original author: Trevor Killeen , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2013 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Windows.Forms; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using pwiz.Skyline.Alerts; +using pwiz.Skyline.Model.Tools; +using pwiz.Skyline.Properties; +using pwiz.Skyline.ToolsUI; +using pwiz.Skyline.Util; +using pwiz.Skyline.Util.Extensions; +using pwiz.SkylineTestUtil; + +namespace pwiz.SkylineTestFunctional +{ + /// + /// Functional test for the Python & Python package installer + /// + [TestClass] + public class PythonInstallerLegacyDlgTest : AbstractFunctionalTest + { + //[TestMethod] + public void TestPythonInstallerLegacyDlg() + { + RunFunctionalTest(); + } + + private const string PYTHON = "Python"; + private const string VERSION_27 = "2.7"; + private const string EXE_PACKAGE = "http://test.com/package.exe"; + private const string TARGZ_PACKAGE = "http://test.com/package.tar.gz"; + private const string ZIP_PACKAGE = "http://test.com/package.zip"; + private const string LOCAL_ZIP_PACKAGE = "C:\\localpackage.zip"; + private const string LOCAL_TARGZ_PACKAGE = "C:\\localpackage.tar.gz"; + private const string LOCAL_EXE_PACKAGE = "C:\\localpackage.exe"; + + protected override void DoTest() + { + TestDlgLoad(); + TestProperPopulation(); + TestGetPython(); + TestGetPackages(); + TestStartToFinish(); + } + + // Tests that the form loads with the proper display based on whether Python is installed or not, as + // well as if there are packages to download + private static void TestDlgLoad() + { + TestDlgLoadBoth(); + TestDlgLoadNotInstalled(); + TestDlgLoadPackagesOnly(); + } + + // Python is not installed and there are packages to install + private static void TestDlgLoadBoth() + { + var ppc = new ProgramPathContainer(PYTHON, VERSION_27); + var packageUris = new Collection {EXE_PACKAGE, TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_EXE_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE}; + var pythonInstaller = ShowDialog(() => InstallProgram(ppc, packageUris, false)); + WaitForConditionUI(5*1000, () => pythonInstaller.IsLoaded); + RunUI(() => Assert.AreEqual(string.Format(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_Python__0__and_the_following_packages__Select_packages_to_install_and_then_click_Install_to_begin_the_installation_process_, VERSION_27), pythonInstaller.Message)); + OkDialog(pythonInstaller, () => Cancel(pythonInstaller)); + } + + // Python is not installed and there are no packages to install + public static void TestDlgLoadNotInstalled() + { + var ppc = new ProgramPathContainer(PYTHON, VERSION_27); + var pythonInstaller = ShowDialog(() => InstallProgram(ppc, new String[0], false)); + WaitForConditionUI(5 * 1000, () => pythonInstaller.IsLoaded); + RunUI(() => Assert.AreEqual(string.Format(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_Python__0___Click_install_to_begin_the_installation_process_, VERSION_27), pythonInstaller.Message)); + OkDialog(pythonInstaller, () => Cancel(pythonInstaller)); + } + + // Python is installed and there are packages to install + private static void TestDlgLoadPackagesOnly() + { + var ppc = new ProgramPathContainer(PYTHON, VERSION_27); + var packageUris = new Collection { EXE_PACKAGE, TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_EXE_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE }; + var pythonInstaller = ShowDialog(() => InstallProgram(ppc, packageUris, true)); + WaitForConditionUI(5 * 1000, () => pythonInstaller.IsLoaded); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_the_following_Python_packages__Select_packages_to_install_and_then_click_Install_to_begin_the_installation_process_, pythonInstaller.Message)); + OkDialog(pythonInstaller, () => Cancel(pythonInstaller)); + } + + // Tests that the form properly populates the checkbox + private static void TestProperPopulation() + { + var PPC = new ProgramPathContainer(PYTHON, VERSION_27); + var PackageUris = new Collection { EXE_PACKAGE, TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_EXE_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE }; + var pythonInstaller = ShowDialog(() => InstallProgram(PPC, PackageUris, false)); + WaitForConditionUI(5 * 1000, () => pythonInstaller.IsLoaded); + + RunUI(() => Assert.AreEqual(PackageUris.Count, pythonInstaller.PackagesListCount)); + + // ensure that packages default to checked upon load + RunUI(() => Assert.AreEqual(PackageUris.Count, pythonInstaller.CheckedPackagesCount)); + OkDialog(pythonInstaller, () => Cancel(pythonInstaller)); + } + + private static void TestGetPython() + { + TestPythonDownloadCanceled(); + TestPythonDownloadFailed(); + TestPythonInstallFailed(); + TestPythonInstallSucceeded(); + } + + // Test canceling the Python download + private static void TestPythonDownloadCanceled() + { + var pythonInstaller = FormatPythonInstaller(true, false, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.MultiFileAsynchronousDownloadClient_DownloadFileAsyncWithBroker_Download_canceled_, messageDlg.Message)); + OkDialog(messageDlg, messageDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test Python download failure + private static void TestPythonDownloadFailed() + { + var pythonInstaller = FormatPythonInstaller(false, false, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(TextUtil.LineSeparate( + Resources.PythonInstaller_DownloadPython_Download_failed_, + Resources.PythonInstaller_DownloadPython_Check_your_network_connection_or_contact_the_tool_provider_for_installation_support_), messageDlg.Message)); + OkDialog(messageDlg, messageDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test Python installation failure + private static void TestPythonInstallFailed() + { + var pythonInstaller = FormatPythonInstaller(false, true, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPython_Python_installation_failed__Canceling_tool_installation_, messageDlg.Message)); + OkDialog(messageDlg, messageDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test Python installation success + private static void TestPythonInstallSucceeded() + { + var pythonInstaller = FormatPythonInstaller(false, true, true); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPython_Python_installation_completed_, messageDlg.Message)); + OkDialog(messageDlg, messageDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + private static PythonInstallerLegacyDlg FormatPythonInstaller(bool cancelDownload, bool downloadSuccess, + bool installSuccess) + { + var ppc = new ProgramPathContainer(PYTHON, VERSION_27); + // ReSharper disable once CollectionNeverUpdated.Local + var packageUris = new Collection(); + var pythonInstaller = ShowDialog(() => InstallProgram(ppc, packageUris, false)); + WaitForConditionUI(() => pythonInstaller.IsLoaded); + RunUI(() => + { + pythonInstaller.TestDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = cancelDownload, + DownloadSuccess = downloadSuccess + }; + pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = installSuccess ? 0 : 1}; + }); + return pythonInstaller; + } + + private static void TestGetPackages() + { + TestPackagesDownloadCanceled(); + TestPackagesDownloadFailed(); + TestPackagesUnselected(); + TestPackagesSourceOnly(); + TestPackagesExecutablesOnly(); + TestPackagesBothFileTypes(); + } + + // Test canceling the package download + private static void TestPackagesDownloadCanceled() + { + var pythonInstaller = FormatPackageInstallerBothTypes(true, false, false, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.MultiFileAsynchronousDownloadClient_DownloadFileAsyncWithBroker_Download_canceled_, messageDlg.Message)); + OkDialog(messageDlg, messageDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test failing the package download + private static void TestPackagesDownloadFailed() + { + var pythonInstaller = FormatPackageInstallerOnlyExes(false, false, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + var packages = new Collection {EXE_PACKAGE}; + RunUI(() => Assert.AreEqual(TextUtil.LineSeparate(Resources.PythonInstaller_DownloadPackages_Failed_to_download_the_following_packages_, + string.Empty, + TextUtil.LineSeparate(packages), + string.Empty, + Resources.PythonInstaller_DownloadPython_Check_your_network_connection_or_contact_the_tool_provider_for_installation_support_), messageDlg.Message)); + OkDialog(messageDlg, messageDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test that tool installation works properly when no packages are selected + private static void TestPackagesUnselected() + { + var pythonInstaller = FormatPackageInstallerBothTypes(false, true, true, true); + RunUI(() => pythonInstaller.UncheckAllPackages()); + OkDialog(pythonInstaller, pythonInstaller.OkDialog); + } + + private static void TestPackagesSourceOnly() + { + TestGetPipCancel(); + TestGetPipCancelDownload(); + TestGetPipFailed(); + TestInstallPipFailed(); + TestInstallPipConnectFailed(); + TestSourceConnectFailure(); + TestSourceInstallFailure(); + TestSourceInstallSuccess(); + } + + // Test canceling the pip installation dialog + private static void TestGetPipCancel() + { + var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); + SetPipInstallResults(pythonInstaller, true, false, false, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); + var errorDlg = ShowDialog(messageDlg.BtnCancelClick); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, errorDlg.Message)); + OkDialog(errorDlg, errorDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test canceling the pip download + private static void TestGetPipCancelDownload() + { + var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); + SetPipInstallResults(pythonInstaller, true, false, false, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); + var pipErrorDlg = ShowDialog(() => Accept(messageDlg)); + RunUI(() => Assert.AreEqual(Resources.MultiFileAsynchronousDownloadClient_DownloadFileAsyncWithBroker_Download_canceled_, pipErrorDlg.Message)); + OkDialog(pipErrorDlg, pipErrorDlg.OkDialog); + var packageErrorDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, packageErrorDlg.Message)); + OkDialog(packageErrorDlg, packageErrorDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test failing the pip download + private static void TestGetPipFailed() + { + var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); + SetPipInstallResults(pythonInstaller, false, false, false, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); + var pipErrorDlg = ShowDialog(() => Accept(messageDlg)); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_DownloadPip_Download_failed__Check_your_network_connection_or_contact_Skyline_developers_, pipErrorDlg.Message)); + OkDialog(pipErrorDlg, pipErrorDlg.OkDialog); + var packageErrorDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, packageErrorDlg.Message)); + OkDialog(packageErrorDlg, packageErrorDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test failing to connect in the pip installation + private static void TestInstallPipConnectFailed() + { + var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); + SetPipInstallResults(pythonInstaller, false, true, false, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); + var pipErrorDlg = ShowDialog(() => Accept(messageDlg)); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPip_Unknown_error_installing_pip_, pipErrorDlg.Message)); + OkDialog(pipErrorDlg, pipErrorDlg.OkDialog); + var packageErrorDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, packageErrorDlg.Message)); + OkDialog(packageErrorDlg, packageErrorDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test failing the pip installation + private static void TestInstallPipFailed() + { + var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); + SetPipInstallResults(pythonInstaller, false, true, true, false); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); + var pipErrorDlg = ShowDialog(() => Accept(messageDlg)); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPip_Pip_installation_failed__Error_log_output_in_immediate_window__, pipErrorDlg.Message)); + OkDialog(pipErrorDlg, pipErrorDlg.OkDialog); + var packageErrorDlg = FindOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, packageErrorDlg.Message)); + OkDialog(packageErrorDlg, packageErrorDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + private static void TestSourceConnectFailure() + { + var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); + SetPipInstallResults(pythonInstaller, false, true, true, true); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); + var pipMessageDlg = ShowDialog(() => Accept(messageDlg)); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipMessageDlg.Message)); + OkDialog(pipMessageDlg, pipMessageDlg.OkDialog); + var errorDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Unknown_error_installing_packages_, errorDlg.Message)); + OkDialog(errorDlg, errorDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test failing to install the packages from source + private static void TestSourceInstallFailure() + { + var pythonInstaller = FormatPackageInstallerOnlySources(false, true, true, false); + SetPipInstallResults(pythonInstaller, false, true, true, true); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); + var pipMessageDlg = ShowDialog(() => Accept(messageDlg)); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipMessageDlg.Message)); + OkDialog(pipMessageDlg, pipMessageDlg.OkDialog); + var errorDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Package_installation_failed__Error_log_output_in_immediate_window_, errorDlg.Message)); + OkDialog(errorDlg, errorDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test successfully installing packages from source only + private static void TestSourceInstallSuccess() + { + var pythonInstaller = FormatPackageInstallerOnlySources(false, true, true, true); + SetPipInstallResults(pythonInstaller, false, true, true, true); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); + var pipMessageDlg = ShowDialog(() => Accept(messageDlg)); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipMessageDlg.Message)); + OkDialog(pipMessageDlg, pipMessageDlg.OkDialog); + var installSuccessDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPackages_Package_installation_completed_, installSuccessDlg.Message)); + OkDialog(installSuccessDlg, installSuccessDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + private static void SetPipInstallResults(PythonInstallerLegacyDlg pythonInstallerLegacyDlg, bool cancelDownload, + bool downloadSuccess, bool connectSuccess, bool installSuccess) + { + RunUI(() => + { + pythonInstallerLegacyDlg.TestPipDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = cancelDownload, + DownloadSuccess = downloadSuccess + }; + pythonInstallerLegacyDlg.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = connectSuccess, + ExitCode = installSuccess ? 0 : 1 + }; + pythonInstallerLegacyDlg.TestingPip = true; + }); + } + + private static void TestPackagesExecutablesOnly() + { + TestExecutableInstallFailure(); + TestExecutableInstallSuccess(); + } + + // Test failing to install executable packages + private static void TestExecutableInstallFailure() + { + var pythonInstaller = FormatPackageInstallerOnlyExes(false, true, false); + RunUI(() => pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = 1}); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Package_Installation_was_not_completed__Canceling_tool_installation_, messageDlg.Message)); + OkDialog(messageDlg, messageDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test successfully installing packages from executables only + private static void TestExecutableInstallSuccess() + { + var pythonInstaller = FormatPackageInstallerOnlyExes(false, true, true); + RunUI(() => pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = 0}); + var installSuccessDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPackages_Package_installation_completed_, installSuccessDlg.Message)); + OkDialog(installSuccessDlg, installSuccessDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + // Test successfully installing packages from both source and executables + private static void TestPackagesBothFileTypes() + { + var pythonInstaller = FormatPackageInstallerBothTypes(false, true, true, true); + SetPipInstallResults(pythonInstaller, false, true, true, true); + RunUI(() => pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = 0}); + var messageDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); + var pipMessageDlg = ShowDialog(() => Accept(messageDlg)); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipMessageDlg.Message)); + OkDialog(pipMessageDlg, pipMessageDlg.OkDialog); + var installSuccessDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPackages_Package_installation_completed_, installSuccessDlg.Message)); + OkDialog(installSuccessDlg, installSuccessDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + private static PythonInstallerLegacyDlg FormatPackageInstallerOnlyExes(bool cancelDownload, bool downloadSuccess, bool installSuccess) + { + return FormatPackageInstaller(cancelDownload, downloadSuccess, true, installSuccess, + new Collection {EXE_PACKAGE, LOCAL_EXE_PACKAGE}); + } + + private static PythonInstallerLegacyDlg FormatPackageInstallerOnlySources(bool cancelDownload, bool downloadSuccess, + bool connectSuccess, bool installSuccess) + { + return FormatPackageInstaller(cancelDownload, downloadSuccess, connectSuccess, installSuccess, + new Collection {TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE}); + } + + private static PythonInstallerLegacyDlg FormatPackageInstallerBothTypes(bool cancelDownload, bool downloadSuccess, + bool connectSuccess, bool installSuccess) + { + return FormatPackageInstaller(cancelDownload, downloadSuccess, connectSuccess, installSuccess, + new Collection {ZIP_PACKAGE, TARGZ_PACKAGE, EXE_PACKAGE, LOCAL_ZIP_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_EXE_PACKAGE}); + } + + private static PythonInstallerLegacyDlg FormatPackageInstaller(bool cancelDownload, bool downloadSuccess, + bool connectSuccess, bool installSuccess, IEnumerable packageUris) + { + var ppc = new ProgramPathContainer(PYTHON, VERSION_27); + var pythonInstaller = ShowDialog(() => InstallProgram(ppc, packageUris, true)); + WaitForConditionUI(() => pythonInstaller.IsLoaded); + RunUI(() => + { + pythonInstaller.TestDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = cancelDownload, + DownloadSuccess = downloadSuccess + }; + pythonInstaller.TestSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = connectSuccess, + ExitCode = installSuccess ? 0 : 1 + }; + }); + return pythonInstaller; + } + + // Tests a start to finish installation of Python, and both executable and source packages + private static void TestStartToFinish() + { + var pythonInstaller = + ShowDialog( + () => + InstallProgram(new ProgramPathContainer(PYTHON, VERSION_27), + new Collection {EXE_PACKAGE, TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_EXE_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE}, false)); + + RunUI(() => + { + pythonInstaller.TestDownloadClient = pythonInstaller.TestPipDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = false, + DownloadSuccess = true + }; + pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = 0}; + pythonInstaller.TestPipDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = false, + DownloadSuccess = true + }; + pythonInstaller.TestPipeSkylineProcessRunner = pythonInstaller.TestSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 0 + }; + pythonInstaller.TestingPip = true; + }); + + var pythonInstallSuccessDlg = ShowDialog(pythonInstaller.OkDialog); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPython_Python_installation_completed_, pythonInstallSuccessDlg.Message)); + OkDialog(pythonInstallSuccessDlg, pythonInstallSuccessDlg.OkDialog); + var pipPromptDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, pipPromptDlg.Message)); + OkDialog(pipPromptDlg, () => Accept(pipPromptDlg)); + var pipInstallSuccessDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipInstallSuccessDlg.Message)); + OkDialog(pipInstallSuccessDlg, pipInstallSuccessDlg.OkDialog); + var packageInstallSuccessDlg = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPackages_Package_installation_completed_, packageInstallSuccessDlg.Message)); + OkDialog(packageInstallSuccessDlg, packageInstallSuccessDlg.OkDialog); + WaitForClosedForm(pythonInstaller); + } + + private static void InstallProgram(ProgramPathContainer ppc, IEnumerable packageUris, bool installed) + { + using (var dlg = new PythonInstallerLegacyDlg(ppc, packageUris, installed, null)) + { + // Keep OK button from doing anything ever + dlg.TestRunProcess = new TestRunProcess { ExitCode = 0 }; + dlg.ShowDialog(SkylineWindow); + } + } + + private static void Accept(Form form) + { + WaitForConditionUI(5000, () => form.AcceptButton != null); + form.AcceptButton.PerformClick(); + } + + private static void Cancel(Form form) + { + WaitForConditionUI(5000, () => form.CancelButton != null); + form.CancelButton.PerformClick(); + } + } +} diff --git a/pwiz_tools/Skyline/TestFunctional/PythonInstallerTest.cs b/pwiz_tools/Skyline/TestFunctional/PythonInstallerTest.cs index 378a708e6d..bf4fe47266 100644 --- a/pwiz_tools/Skyline/TestFunctional/PythonInstallerTest.cs +++ b/pwiz_tools/Skyline/TestFunctional/PythonInstallerTest.cs @@ -1,543 +1,830 @@ -/* - * Original author: Trevor Killeen , - * MacCoss Lab, Department of Genome Sciences, UW - * - * Copyright 2013 University of Washington - Seattle, WA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Windows.Forms; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using pwiz.Skyline.Alerts; -using pwiz.Skyline.Model.Tools; -using pwiz.Skyline.Properties; -using pwiz.Skyline.ToolsUI; -using pwiz.Skyline.Util; -using pwiz.Skyline.Util.Extensions; -using pwiz.SkylineTestUtil; - -namespace pwiz.SkylineTestFunctional -{ - /// - /// Functional test for the Python & Python package installer - /// - [TestClass] - public class PythonInstallerTest : AbstractFunctionalTest - { - [TestMethod] - public void TestPythonInstaller() - { - RunFunctionalTest(); - } - - private const string PYTHON = "Python"; - private const string VERSION_27 = "2.7"; - private const string EXE_PACKAGE = "http://test.com/package.exe"; - private const string TARGZ_PACKAGE = "http://test.com/package.tar.gz"; - private const string ZIP_PACKAGE = "http://test.com/package.zip"; - private const string LOCAL_ZIP_PACKAGE = "C:\\localpackage.zip"; - private const string LOCAL_TARGZ_PACKAGE = "C:\\localpackage.tar.gz"; - private const string LOCAL_EXE_PACKAGE = "C:\\localpackage.exe"; - - protected override void DoTest() - { - TestDlgLoad(); - TestProperPopulation(); - TestGetPython(); - TestGetPackages(); - TestStartToFinish(); - } - - // Tests that the form loads with the proper display based on whether Python is installed or not, as - // well as if there are packages to download - private static void TestDlgLoad() - { - TestDlgLoadBoth(); - TestDlgLoadNotInstalled(); - TestDlgLoadPackagesOnly(); - } - - // Python is not installed and there are packages to install - private static void TestDlgLoadBoth() - { - var ppc = new ProgramPathContainer(PYTHON, VERSION_27); - var packageUris = new Collection {EXE_PACKAGE, TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_EXE_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE}; - var pythonInstaller = ShowDialog(() => InstallProgram(ppc, packageUris, false)); - WaitForConditionUI(5*1000, () => pythonInstaller.IsLoaded); - RunUI(() => Assert.AreEqual(string.Format(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_Python__0__and_the_following_packages__Select_packages_to_install_and_then_click_Install_to_begin_the_installation_process_, VERSION_27), pythonInstaller.Message)); - OkDialog(pythonInstaller, () => Cancel(pythonInstaller)); - } - - // Python is not installed and there are no packages to install - public static void TestDlgLoadNotInstalled() - { - var ppc = new ProgramPathContainer(PYTHON, VERSION_27); - var pythonInstaller = ShowDialog(() => InstallProgram(ppc, new String[0], false)); - WaitForConditionUI(5 * 1000, () => pythonInstaller.IsLoaded); - RunUI(() => Assert.AreEqual(string.Format(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_Python__0___Click_install_to_begin_the_installation_process_, VERSION_27), pythonInstaller.Message)); - OkDialog(pythonInstaller, () => Cancel(pythonInstaller)); - } - - // Python is installed and there are packages to install - private static void TestDlgLoadPackagesOnly() - { - var ppc = new ProgramPathContainer(PYTHON, VERSION_27); - var packageUris = new Collection { EXE_PACKAGE, TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_EXE_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE }; - var pythonInstaller = ShowDialog(() => InstallProgram(ppc, packageUris, true)); - WaitForConditionUI(5 * 1000, () => pythonInstaller.IsLoaded); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_the_following_Python_packages__Select_packages_to_install_and_then_click_Install_to_begin_the_installation_process_, pythonInstaller.Message)); - OkDialog(pythonInstaller, () => Cancel(pythonInstaller)); - } - - // Tests that the form properly populates the checkbox - private static void TestProperPopulation() - { - var PPC = new ProgramPathContainer(PYTHON, VERSION_27); - var PackageUris = new Collection { EXE_PACKAGE, TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_EXE_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE }; - var pythonInstaller = ShowDialog(() => InstallProgram(PPC, PackageUris, false)); - WaitForConditionUI(5 * 1000, () => pythonInstaller.IsLoaded); - - RunUI(() => Assert.AreEqual(PackageUris.Count, pythonInstaller.PackagesListCount)); - - // ensure that packages default to checked upon load - RunUI(() => Assert.AreEqual(PackageUris.Count, pythonInstaller.CheckedPackagesCount)); - OkDialog(pythonInstaller, () => Cancel(pythonInstaller)); - } - - private static void TestGetPython() - { - TestPythonDownloadCanceled(); - TestPythonDownloadFailed(); - TestPythonInstallFailed(); - TestPythonInstallSucceeded(); - } - - // Test canceling the Python download - private static void TestPythonDownloadCanceled() - { - var pythonInstaller = FormatPythonInstaller(true, false, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.MultiFileAsynchronousDownloadClient_DownloadFileAsyncWithBroker_Download_canceled_, messageDlg.Message)); - OkDialog(messageDlg, messageDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test Python download failure - private static void TestPythonDownloadFailed() - { - var pythonInstaller = FormatPythonInstaller(false, false, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(TextUtil.LineSeparate( - Resources.PythonInstaller_DownloadPython_Download_failed_, - Resources.PythonInstaller_DownloadPython_Check_your_network_connection_or_contact_the_tool_provider_for_installation_support_), messageDlg.Message)); - OkDialog(messageDlg, messageDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test Python installation failure - private static void TestPythonInstallFailed() - { - var pythonInstaller = FormatPythonInstaller(false, true, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPython_Python_installation_failed__Canceling_tool_installation_, messageDlg.Message)); - OkDialog(messageDlg, messageDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test Python installation success - private static void TestPythonInstallSucceeded() - { - var pythonInstaller = FormatPythonInstaller(false, true, true); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPython_Python_installation_completed_, messageDlg.Message)); - OkDialog(messageDlg, messageDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - private static PythonInstaller FormatPythonInstaller(bool cancelDownload, bool downloadSuccess, - bool installSuccess) - { - var ppc = new ProgramPathContainer(PYTHON, VERSION_27); - // ReSharper disable once CollectionNeverUpdated.Local - var packageUris = new Collection(); - var pythonInstaller = ShowDialog(() => InstallProgram(ppc, packageUris, false)); - WaitForConditionUI(() => pythonInstaller.IsLoaded); - RunUI(() => - { - pythonInstaller.TestDownloadClient = new TestAsynchronousDownloadClient - { - CancelDownload = cancelDownload, - DownloadSuccess = downloadSuccess - }; - pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = installSuccess ? 0 : 1}; - }); - return pythonInstaller; - } - - private static void TestGetPackages() - { - TestPackagesDownloadCanceled(); - TestPackagesDownloadFailed(); - TestPackagesUnselected(); - TestPackagesSourceOnly(); - TestPackagesExecutablesOnly(); - TestPackagesBothFileTypes(); - } - - // Test canceling the package download - private static void TestPackagesDownloadCanceled() - { - var pythonInstaller = FormatPackageInstallerBothTypes(true, false, false, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.MultiFileAsynchronousDownloadClient_DownloadFileAsyncWithBroker_Download_canceled_, messageDlg.Message)); - OkDialog(messageDlg, messageDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test failing the package download - private static void TestPackagesDownloadFailed() - { - var pythonInstaller = FormatPackageInstallerOnlyExes(false, false, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - var packages = new Collection {EXE_PACKAGE}; - RunUI(() => Assert.AreEqual(TextUtil.LineSeparate(Resources.PythonInstaller_DownloadPackages_Failed_to_download_the_following_packages_, - string.Empty, - TextUtil.LineSeparate(packages), - string.Empty, - Resources.PythonInstaller_DownloadPython_Check_your_network_connection_or_contact_the_tool_provider_for_installation_support_), messageDlg.Message)); - OkDialog(messageDlg, messageDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test that tool installation works properly when no packages are selected - private static void TestPackagesUnselected() - { - var pythonInstaller = FormatPackageInstallerBothTypes(false, true, true, true); - RunUI(() => pythonInstaller.UncheckAllPackages()); - OkDialog(pythonInstaller, pythonInstaller.OkDialog); - } - - private static void TestPackagesSourceOnly() - { - TestGetPipCancel(); - TestGetPipCancelDownload(); - TestGetPipFailed(); - TestInstallPipFailed(); - TestInstallPipConnectFailed(); - TestSourceConnectFailure(); - TestSourceInstallFailure(); - TestSourceInstallSuccess(); - } - - // Test canceling the pip installation dialog - private static void TestGetPipCancel() - { - var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); - SetPipInstallResults(pythonInstaller, true, false, false, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); - var errorDlg = ShowDialog(messageDlg.BtnCancelClick); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, errorDlg.Message)); - OkDialog(errorDlg, errorDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test canceling the pip download - private static void TestGetPipCancelDownload() - { - var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); - SetPipInstallResults(pythonInstaller, true, false, false, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); - var pipErrorDlg = ShowDialog(() => Accept(messageDlg)); - RunUI(() => Assert.AreEqual(Resources.MultiFileAsynchronousDownloadClient_DownloadFileAsyncWithBroker_Download_canceled_, pipErrorDlg.Message)); - OkDialog(pipErrorDlg, pipErrorDlg.OkDialog); - var packageErrorDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, packageErrorDlg.Message)); - OkDialog(packageErrorDlg, packageErrorDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test failing the pip download - private static void TestGetPipFailed() - { - var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); - SetPipInstallResults(pythonInstaller, false, false, false, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); - var pipErrorDlg = ShowDialog(() => Accept(messageDlg)); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_DownloadPip_Download_failed__Check_your_network_connection_or_contact_Skyline_developers_, pipErrorDlg.Message)); - OkDialog(pipErrorDlg, pipErrorDlg.OkDialog); - var packageErrorDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, packageErrorDlg.Message)); - OkDialog(packageErrorDlg, packageErrorDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test failing to connect in the pip installation - private static void TestInstallPipConnectFailed() - { - var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); - SetPipInstallResults(pythonInstaller, false, true, false, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); - var pipErrorDlg = ShowDialog(() => Accept(messageDlg)); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPip_Unknown_error_installing_pip_, pipErrorDlg.Message)); - OkDialog(pipErrorDlg, pipErrorDlg.OkDialog); - var packageErrorDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, packageErrorDlg.Message)); - OkDialog(packageErrorDlg, packageErrorDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test failing the pip installation - private static void TestInstallPipFailed() - { - var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); - SetPipInstallResults(pythonInstaller, false, true, true, false); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); - var pipErrorDlg = ShowDialog(() => Accept(messageDlg)); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPip_Pip_installation_failed__Error_log_output_in_immediate_window__, pipErrorDlg.Message)); - OkDialog(pipErrorDlg, pipErrorDlg.OkDialog); - var packageErrorDlg = FindOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_, packageErrorDlg.Message)); - OkDialog(packageErrorDlg, packageErrorDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - private static void TestSourceConnectFailure() - { - var pythonInstaller = FormatPackageInstallerOnlySources(false, true, false, false); - SetPipInstallResults(pythonInstaller, false, true, true, true); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); - var pipMessageDlg = ShowDialog(() => Accept(messageDlg)); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipMessageDlg.Message)); - OkDialog(pipMessageDlg, pipMessageDlg.OkDialog); - var errorDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Unknown_error_installing_packages_, errorDlg.Message)); - OkDialog(errorDlg, errorDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test failing to install the packages from source - private static void TestSourceInstallFailure() - { - var pythonInstaller = FormatPackageInstallerOnlySources(false, true, true, false); - SetPipInstallResults(pythonInstaller, false, true, true, true); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); - var pipMessageDlg = ShowDialog(() => Accept(messageDlg)); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipMessageDlg.Message)); - OkDialog(pipMessageDlg, pipMessageDlg.OkDialog); - var errorDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Package_installation_failed__Error_log_output_in_immediate_window_, errorDlg.Message)); - OkDialog(errorDlg, errorDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test successfully installing packages from source only - private static void TestSourceInstallSuccess() - { - var pythonInstaller = FormatPackageInstallerOnlySources(false, true, true, true); - SetPipInstallResults(pythonInstaller, false, true, true, true); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); - var pipMessageDlg = ShowDialog(() => Accept(messageDlg)); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipMessageDlg.Message)); - OkDialog(pipMessageDlg, pipMessageDlg.OkDialog); - var installSuccessDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPackages_Package_installation_completed_, installSuccessDlg.Message)); - OkDialog(installSuccessDlg, installSuccessDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - private static void SetPipInstallResults(PythonInstaller pythonInstaller, bool cancelDownload, - bool downloadSuccess, bool connectSuccess, bool installSuccess) - { - RunUI(() => - { - pythonInstaller.TestPipDownloadClient = new TestAsynchronousDownloadClient - { - CancelDownload = cancelDownload, - DownloadSuccess = downloadSuccess - }; - pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner - { - ConnectSuccess = connectSuccess, - ExitCode = installSuccess ? 0 : 1 - }; - pythonInstaller.TestingPip = true; - }); - } - - private static void TestPackagesExecutablesOnly() - { - TestExecutableInstallFailure(); - TestExecutableInstallSuccess(); - } - - // Test failing to install executable packages - private static void TestExecutableInstallFailure() - { - var pythonInstaller = FormatPackageInstallerOnlyExes(false, true, false); - RunUI(() => pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = 1}); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Package_Installation_was_not_completed__Canceling_tool_installation_, messageDlg.Message)); - OkDialog(messageDlg, messageDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test successfully installing packages from executables only - private static void TestExecutableInstallSuccess() - { - var pythonInstaller = FormatPackageInstallerOnlyExes(false, true, true); - RunUI(() => pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = 0}); - var installSuccessDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPackages_Package_installation_completed_, installSuccessDlg.Message)); - OkDialog(installSuccessDlg, installSuccessDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - // Test successfully installing packages from both source and executables - private static void TestPackagesBothFileTypes() - { - var pythonInstaller = FormatPackageInstallerBothTypes(false, true, true, true); - SetPipInstallResults(pythonInstaller, false, true, true, true); - RunUI(() => pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = 0}); - var messageDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, messageDlg.Message)); - var pipMessageDlg = ShowDialog(() => Accept(messageDlg)); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipMessageDlg.Message)); - OkDialog(pipMessageDlg, pipMessageDlg.OkDialog); - var installSuccessDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPackages_Package_installation_completed_, installSuccessDlg.Message)); - OkDialog(installSuccessDlg, installSuccessDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - private static PythonInstaller FormatPackageInstallerOnlyExes(bool cancelDownload, bool downloadSuccess, bool installSuccess) - { - return FormatPackageInstaller(cancelDownload, downloadSuccess, true, installSuccess, - new Collection {EXE_PACKAGE, LOCAL_EXE_PACKAGE}); - } - - private static PythonInstaller FormatPackageInstallerOnlySources(bool cancelDownload, bool downloadSuccess, - bool connectSuccess, bool installSuccess) - { - return FormatPackageInstaller(cancelDownload, downloadSuccess, connectSuccess, installSuccess, - new Collection {TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE}); - } - - private static PythonInstaller FormatPackageInstallerBothTypes(bool cancelDownload, bool downloadSuccess, - bool connectSuccess, bool installSuccess) - { - return FormatPackageInstaller(cancelDownload, downloadSuccess, connectSuccess, installSuccess, - new Collection {ZIP_PACKAGE, TARGZ_PACKAGE, EXE_PACKAGE, LOCAL_ZIP_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_EXE_PACKAGE}); - } - - private static PythonInstaller FormatPackageInstaller(bool cancelDownload, bool downloadSuccess, - bool connectSuccess, bool installSuccess, IEnumerable packageUris) - { - var ppc = new ProgramPathContainer(PYTHON, VERSION_27); - var pythonInstaller = ShowDialog(() => InstallProgram(ppc, packageUris, true)); - WaitForConditionUI(() => pythonInstaller.IsLoaded); - RunUI(() => - { - pythonInstaller.TestDownloadClient = new TestAsynchronousDownloadClient - { - CancelDownload = cancelDownload, - DownloadSuccess = downloadSuccess - }; - pythonInstaller.TestSkylineProcessRunner = new TestSkylineProcessRunner - { - ConnectSuccess = connectSuccess, - ExitCode = installSuccess ? 0 : 1 - }; - }); - return pythonInstaller; - } - - // Tests a start to finish installation of Python, and both executable and source packages - private static void TestStartToFinish() - { - var pythonInstaller = - ShowDialog( - () => - InstallProgram(new ProgramPathContainer(PYTHON, VERSION_27), - new Collection {EXE_PACKAGE, TARGZ_PACKAGE, ZIP_PACKAGE, LOCAL_EXE_PACKAGE, LOCAL_TARGZ_PACKAGE, LOCAL_ZIP_PACKAGE}, false)); - - RunUI(() => - { - pythonInstaller.TestDownloadClient = pythonInstaller.TestPipDownloadClient = new TestAsynchronousDownloadClient - { - CancelDownload = false, - DownloadSuccess = true - }; - pythonInstaller.TestRunProcess = new TestRunProcess {ExitCode = 0}; - pythonInstaller.TestPipDownloadClient = new TestAsynchronousDownloadClient - { - CancelDownload = false, - DownloadSuccess = true - }; - pythonInstaller.TestPipeSkylineProcessRunner = pythonInstaller.TestSkylineProcessRunner = new TestSkylineProcessRunner - { - ConnectSuccess = true, - ExitCode = 0 - }; - pythonInstaller.TestingPip = true; - }); - - var pythonInstallSuccessDlg = ShowDialog(pythonInstaller.OkDialog); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPython_Python_installation_completed_, pythonInstallSuccessDlg.Message)); - OkDialog(pythonInstallSuccessDlg, pythonInstallSuccessDlg.OkDialog); - var pipPromptDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, pipPromptDlg.Message)); - OkDialog(pipPromptDlg, () => Accept(pipPromptDlg)); - var pipInstallSuccessDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_InstallPackages_Pip_installation_complete_, pipInstallSuccessDlg.Message)); - OkDialog(pipInstallSuccessDlg, pipInstallSuccessDlg.OkDialog); - var packageInstallSuccessDlg = WaitForOpenForm(); - RunUI(() => Assert.AreEqual(Resources.PythonInstaller_GetPackages_Package_installation_completed_, packageInstallSuccessDlg.Message)); - OkDialog(packageInstallSuccessDlg, packageInstallSuccessDlg.OkDialog); - WaitForClosedForm(pythonInstaller); - } - - private static void InstallProgram(ProgramPathContainer ppc, IEnumerable packageUris, bool installed) - { - using (var dlg = new PythonInstaller(ppc, packageUris, installed, null)) - { - // Keep OK button from doing anything ever - dlg.TestRunProcess = new TestRunProcess { ExitCode = 0 }; - dlg.ShowDialog(SkylineWindow); - } - } - - private static void Accept(Form form) - { - WaitForConditionUI(5000, () => form.AcceptButton != null); - form.AcceptButton.PerformClick(); - } - - private static void Cancel(Form form) - { - WaitForConditionUI(5000, () => form.CancelButton != null); - form.CancelButton.PerformClick(); - } - } -} +/* + * + * Copyright 2025 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using pwiz.Skyline.Model.Tools; +using pwiz.SkylineTestUtil; +using System; +using System.Collections.Generic; +using System.Text; +using pwiz.Skyline.ToolsUI; +using pwiz.Skyline.Controls; +using pwiz.Skyline.Util; +using pwiz.Skyline.Alerts; +using pwiz.Skyline.Properties; +using System.IO; +using Ionic.Zip; + +namespace pwiz.SkylineTestFunctional +{ + /// + /// Functional test for PythonInstaller + /// + [TestClass] + public class PythonInstallerTest : AbstractFunctionalTest + { + private const string PYTHON = @"Python"; + //private const string VERSION_312 = @"3.1.2"; + private const string VERSION = @"3.9.2"; + //private IPythonInstallerTaskValidator TaskValidator { get; } + + //[TestMethod] + public void TestPythonInstaller() + { + //TestFilesZip = @"TestFunctional\PythonInstallerTest.zip"; + RunFunctionalTest(); + } + + protected override void DoTest() + { + TestDownloadPythonEmbeddablePackage_Cancel(); + TestDownloadPythonEmbeddablePackage_Fail(); + TestDownloadPythonEmbeddablePackage_Success(); + TestUnzipPythonEmbeddablePackage_Fail(); + TestUnzipPythonEmbeddablePackage_Success(); + TestEnableSearchPathInPythonEmbeddablePackage_NoSearchPathFile_Fail(); + TestEnableSearchPathInPythonEmbeddablePackage_MoreThanOneSearchPathFile_Fail(); + TestEnableSearchPathInPythonEmbeddablePackage_Success(); + TestDownloadGetPipScript_Cancel(); + TestDownloadGetPipScript_Fail(); + TestDownloadGetPipScript_Success(); + TestRunGetPipScript_Fail(); + TestRunGetPipScript_Success(); + TestPipInstallVirtualenv_Fail(); + TestPipInstallVirtualenv_Success(); + TestCreateVirtualEnvironment_Fail(); + TestCreateVirtualEnvironment_Success(); + TestPipInstallPackages_Fail(); + TestPipInstallPackages_Success(); + TestStartToFinishWithVirtualEnvironment_Success(); + } + + private static void TestDownloadPythonEmbeddablePackage_Cancel() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = true + }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_download_Python_embeddable_package, dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.MultiFileAsynchronousDownloadClient_DownloadFileAsyncWithBroker_Download_canceled_, dlg2.Message)); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestDownloadPythonEmbeddablePackage_Fail() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = false, + DownloadSuccess = false + }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_download_Python_embeddable_package, dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_Download_failed__Check_your_network_connection_or_contact_Skyline_team_for_help_, dlg2.Message)); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestDownloadPythonEmbeddablePackage_Success() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = false, + DownloadSuccess = true + }; + pythonInstaller.TestPythonVirtualEnvironmentTaskNames = new List + { PythonTaskName.download_python_embeddable_package }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, dlg.Message)); + + OkDialog(dlg, dlg.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestUnzipPythonEmbeddablePackage_Fail() + { + // Set up + var taskValidator = new TestPythonInstallerTaskValidator(); + taskValidator.SetSuccessUntil(PythonTaskName.download_python_embeddable_package); + var pythonInstaller = GetPythonInstaller(taskValidator); + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_unzip_Python_embeddable_package, dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.IsTrue(dlg2.Message.Contains($@"Could not find file"))); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestUnzipPythonEmbeddablePackage_Success() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + try + { + var fileName = $@"test.txt"; + var filePath = CreateFile(PythonInstallerUtil.PythonRootDir, fileName); + CreateZipFile(pythonInstaller.PythonEmbeddablePackageDownloadPath, (fileName, filePath)); + } + catch (Exception) + { + CleanupDir(PythonInstallerUtil.PythonRootDir); + throw; + } + + RunUI(() => + { + pythonInstaller.TestPythonVirtualEnvironmentTaskNames = new List + { PythonTaskName.unzip_python_embeddable_package }; + }); + + // Test + var dlg = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, dlg.Message)); + + OkDialog(dlg, dlg.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + private static void TestEnableSearchPathInPythonEmbeddablePackage_NoSearchPathFile_Fail() + { + // Set up + var taskValidator = new TestPythonInstallerTaskValidator(); + taskValidator.SetSuccessUntil(PythonTaskName.unzip_python_embeddable_package); + var pythonInstaller = GetPythonInstaller(taskValidator); + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + try + { + CreateDirectory(pythonInstaller.PythonEmbeddablePackageExtractDir); + } + catch (Exception) + { + CleanupDir(PythonInstallerUtil.PythonRootDir); + throw; + } + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_enable_search_path_in_Python_embeddable_package, dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_EnableSearchPathInPythonEmbeddablePackage_Found_0_or_more_than_one_files_with__pth_extension__this_is_unexpected, dlg2.Message)); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestEnableSearchPathInPythonEmbeddablePackage_MoreThanOneSearchPathFile_Fail() + { + // Set up + var taskValidator = new TestPythonInstallerTaskValidator(); + taskValidator.SetSuccessUntil(PythonTaskName.unzip_python_embeddable_package); + var pythonInstaller = GetPythonInstaller(taskValidator); + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + try + { + CreateDirectory(pythonInstaller.PythonEmbeddablePackageExtractDir); + CreateFile(pythonInstaller.PythonEmbeddablePackageExtractDir, @"file1._pth"); + CreateFile(pythonInstaller.PythonEmbeddablePackageExtractDir, @"file2._pth"); + } + catch (Exception) + { + CleanupDir(PythonInstallerUtil.PythonRootDir); + throw; + } + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_enable_search_path_in_Python_embeddable_package, dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_EnableSearchPathInPythonEmbeddablePackage_Found_0_or_more_than_one_files_with__pth_extension__this_is_unexpected, dlg2.Message)); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestEnableSearchPathInPythonEmbeddablePackage_Success() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestPythonVirtualEnvironmentTaskNames = new List + { PythonTaskName.enable_search_path_in_python_embeddable_package }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + try + { + CreateDirectory(pythonInstaller.PythonEmbeddablePackageExtractDir); + CreateFile(pythonInstaller.PythonEmbeddablePackageExtractDir, @"python312._pth"); + } + catch (Exception) + { + CleanupDir(PythonInstallerUtil.PythonRootDir); + throw; + } + + // Test + var dlg = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, dlg.Message)); + RunUI(() => Assert.IsTrue(File.Exists(Path.Combine(pythonInstaller.PythonEmbeddablePackageExtractDir, @"python312.pth")))); + + OkDialog(dlg, dlg.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestDownloadGetPipScript_Cancel() + { + // Set up + var taskValidator = new TestPythonInstallerTaskValidator(); + taskValidator.SetSuccessUntil(PythonTaskName.enable_search_path_in_python_embeddable_package); + var pythonInstaller = GetPythonInstaller(taskValidator); + pythonInstaller.TestPipDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = true + }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_download_the_get_pip_py_script, dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(Resources.MultiFileAsynchronousDownloadClient_DownloadFileAsyncWithBroker_Download_canceled_, dlg2.Message)); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestDownloadGetPipScript_Fail() + { + // Set up + var taskValidator = new TestPythonInstallerTaskValidator(); + taskValidator.SetSuccessUntil(PythonTaskName.enable_search_path_in_python_embeddable_package); + var pythonInstaller = GetPythonInstaller(taskValidator); + pythonInstaller.TestPipDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = false, + DownloadSuccess = false + }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_download_the_get_pip_py_script, dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_Download_failed__Check_your_network_connection_or_contact_Skyline_team_for_help_, dlg2.Message)); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestDownloadGetPipScript_Success() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = false, + DownloadSuccess = true + }; + pythonInstaller.TestPythonVirtualEnvironmentTaskNames = new List + { PythonTaskName.download_getpip_script }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, dlg.Message)); + + OkDialog(dlg, dlg.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestRunGetPipScript_Fail() + { + // Set up + var taskValidator = new TestPythonInstallerTaskValidator(); + taskValidator.SetSuccessUntil(PythonTaskName.download_getpip_script); + var pythonInstaller = GetPythonInstaller(taskValidator); + pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 1 + }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_run_the_get_pip_py_script, dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.IsTrue(dlg2.Message.Contains($@"Failed to execute command"))); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestRunGetPipScript_Success() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 0 + }; + pythonInstaller.TestPythonVirtualEnvironmentTaskNames = new List + { PythonTaskName.run_getpip_script }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, dlg.Message)); + + OkDialog(dlg, dlg.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestPipInstallVirtualenv_Fail() + { + // Set up + var taskValidator = new TestPythonInstallerTaskValidator(); + taskValidator.SetSuccessUntil(PythonTaskName.run_getpip_script); + var pythonInstaller = GetPythonInstaller(taskValidator); + pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 1 + }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(string.Format(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_run_pip_install__0_, @"virtualenv"), dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.IsTrue(dlg2.Message.Contains($@"Failed to execute command"))); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestPipInstallVirtualenv_Success() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 0 + }; + pythonInstaller.TestPythonVirtualEnvironmentTaskNames = new List + { PythonTaskName.pip_install_virtualenv }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, dlg.Message)); + + OkDialog(dlg, dlg.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestCreateVirtualEnvironment_Fail() + { + // Set up + var taskValidator = new TestPythonInstallerTaskValidator(); + taskValidator.SetSuccessUntil(PythonTaskName.pip_install_virtualenv); + var pythonInstaller = GetPythonInstaller(taskValidator); + pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 1 + }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual( + string.Format(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_create_virtual_environment__0_, @"test"), dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.IsTrue(dlg2.Message.Contains($@"Failed to execute command"))); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestCreateVirtualEnvironment_Success() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 0 + }; + pythonInstaller.TestPythonVirtualEnvironmentTaskNames = new List + { PythonTaskName.create_virtual_environment }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, dlg.Message)); + + OkDialog(dlg, dlg.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestPipInstallPackages_Fail() + { + // Set up + var taskValidator = new TestPythonInstallerTaskValidator(); + taskValidator.SetSuccessUntil(PythonTaskName.create_virtual_environment); + var pythonInstaller = GetPythonInstaller(taskValidator); + pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 1 + }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg1 = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual( + string.Format(ToolsResources.PythonInstaller_GetPythonTask_Failed_to_install_Python_packages_in_virtual_environment__0_, @"test"), dlg1.Message)); + + OkDialog(dlg1, dlg1.OkDialog); + var dlg2 = WaitForOpenForm(); + RunUI(() => Assert.IsTrue(dlg2.Message.Contains($@"Failed to execute command"))); + + OkDialog(dlg2, dlg2.OkDialog); + var dlg3 = WaitForOpenForm(); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, dlg3.Message)); + + OkDialog(dlg3, dlg3.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestPipInstallPackages_Success() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 0 + }; + pythonInstaller.TestPythonVirtualEnvironmentTaskNames = new List + { PythonTaskName.pip_install_packages }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + + // Test + var dlg = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, dlg.Message)); + + OkDialog(dlg, dlg.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static void TestStartToFinishWithVirtualEnvironment_Success() + { + // Set up + var pythonInstaller = GetPythonInstaller(); + pythonInstaller.TestDownloadClient = new TestAsynchronousDownloadClient + { + CancelDownload = false, + DownloadSuccess = true + }; + pythonInstaller.TestPipeSkylineProcessRunner = new TestSkylineProcessRunner + { + ConnectSuccess = true, + ExitCode = 0 + }; + var pythonInstallerDlg = ShowDialog( + () => + { + using var dlg = new PythonInstallerDlg(pythonInstaller); + dlg.ShowDialog(SkylineWindow); + }); + try + { + var fileName = $@"python312._pth"; + var filePath = CreateFile(PythonInstallerUtil.PythonRootDir, fileName); + CreateZipFile(pythonInstaller.PythonEmbeddablePackageDownloadPath, (fileName, filePath)); + } + catch (Exception) + { + CleanupDir(PythonInstallerUtil.PythonRootDir); + throw; + } + + // Test + var dlg = ShowDialog(pythonInstallerDlg.OkDialog); + RunUI(() => Assert.AreEqual(ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, dlg.Message)); + RunUI(() => Assert.AreEqual(8, pythonInstaller.NumTotalTasks)); + RunUI(() => Assert.AreEqual(8, pythonInstaller.NumCompletedTasks)); + + OkDialog(dlg, dlg.OkDialog); + WaitForClosedForm(pythonInstallerDlg); + + // Tear down + CleanupDir(PythonInstallerUtil.PythonRootDir); + } + + private static PythonInstaller GetPythonInstaller() + { + return GetPythonInstaller(new TestPythonInstallerTaskValidator()); + } + + private static PythonInstaller GetPythonInstaller(IPythonInstallerTaskValidator taskValidator) + { + var packages = new List() + { + new PythonPackage {Name = @"peptdeep", Version = null }, + new PythonPackage {Name = @"numpy", Version = @"1.26.4" } + }; + var programContainer = new ProgramPathContainer(PYTHON, VERSION); + return new PythonInstaller(programContainer, packages, new TextBoxStreamWriterHelper(), taskValidator, @"test"); + } + + private static string CreateFile(string dir, string fileName, string content = "") + { + var path = Path.Combine(dir, fileName); + var buffer = new UTF8Encoding(true).GetBytes(content); + using var fileStream = File.Create(path); + fileStream.Write(buffer, 0, buffer.Length); + return path; + } + + private static void CreateZipFile(string outputFilePath, params (string entryName, string filePath)[] inputFileTuples) + { + using var zipFile = new ZipFile(); + foreach (var inputFileTuple in inputFileTuples) + { + var entry = zipFile.AddFile(inputFileTuple.filePath); + entry.FileName = inputFileTuple.entryName; + } + zipFile.Save(outputFilePath); + } + + private static void CreateDirectory(string dir) + { + if (Directory.Exists(dir)) + { + return; + } + Directory.CreateDirectory(dir); + } + + private static void CleanupDir(string dir) + { + if (Directory.Exists(dir)) + { + Directory.Delete(dir, true); + } + } + } +} diff --git a/pwiz_tools/Skyline/TestFunctional/TestFunctional.csproj b/pwiz_tools/Skyline/TestFunctional/TestFunctional.csproj index 4568d1a468..f3752185f0 100644 --- a/pwiz_tools/Skyline/TestFunctional/TestFunctional.csproj +++ b/pwiz_tools/Skyline/TestFunctional/TestFunctional.csproj @@ -171,6 +171,7 @@ + @@ -217,6 +218,7 @@ + @@ -403,7 +405,7 @@ - + @@ -650,6 +652,7 @@ + diff --git a/pwiz_tools/Skyline/TestPerf/AlphapeptdeepBuildLibraryTest.cs b/pwiz_tools/Skyline/TestPerf/AlphapeptdeepBuildLibraryTest.cs new file mode 100644 index 0000000000..633ae16615 --- /dev/null +++ b/pwiz_tools/Skyline/TestPerf/AlphapeptdeepBuildLibraryTest.cs @@ -0,0 +1,183 @@ +/* + * Author: David Shteynberg , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2025 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using pwiz.Skyline.Controls; +using pwiz.Skyline.Model.Irt; +using pwiz.Skyline.Model.Tools; +using pwiz.Skyline.SettingsUI; +using pwiz.Skyline.Util; +using pwiz.SkylineTestUtil; + +namespace TestPerf +{ + [TestClass] + public class AlphapeptdeepBuildLibraryTest : AbstractFunctionalTestEx + { + [TestMethod] + public void TestAlphaPeptDeepBuildLibrary() + { + _pythonTestUtil = new PythonTestUtil(BuildLibraryDlg.ALPHAPEPTDEEP_PYTHON_VERSION, @"AlphaPeptDeep", true); + TestFilesZip = "TestPerf/AlphapeptdeepBuildLibraryTest.zip"; + RunFunctionalTest(); + } + + private string LibraryPathWithoutIrt => + TestContext.GetTestPath("TestAlphapeptdeepBuildLibrary\\LibraryWithoutIrt.blib"); + private string LibraryPathWithIrt => + TestContext.GetTestPath("TestAlphapeptdeepBuildLibrary\\LibraryWithIrt.blib"); + + private string _storedHashWithoutIrt = @"2b31dec24e3a22bf2807769864ec6d7045236be32a9f78e0548e62975afe7318"; + + private string _storedHashWithIrt = @"2b31dec24e3a22bf2807769864ec6d7045236be32a9f78e0548e62975afe7318"; + + private PythonTestUtil _pythonTestUtil; + private PeptideSettingsUI _peptideSettings; + private BuildLibraryDlg _buildLibraryDlg; + + protected override void DoTest() + { + OpenDocument(TestFilesDir.GetTestPath(@"Rat_plasma.sky")); + + + const string libraryWithoutIrt = "AlphaPeptDeepLibraryWithoutIrt"; + const string libraryWithIrt = "AlphaPeptDeepLibraryWithIrt"; + + _peptideSettings = ShowPeptideSettings(PeptideSettingsUI.TABS.Library); + _buildLibraryDlg = ShowDialog(_peptideSettings.ShowBuildLibraryDlg); + RunUI(() => + { + _buildLibraryDlg.LibraryName = libraryWithoutIrt; + _buildLibraryDlg.LibraryPath = LibraryPathWithoutIrt; + _buildLibraryDlg.AlphaPeptDeep = true; + }); + + if (!_pythonTestUtil.HavePythonPrerequisite(_buildLibraryDlg)) + { + _pythonTestUtil.CancelPython(_buildLibraryDlg); + + + _pythonTestUtil.InstallPythonTestNvidia(_buildLibraryDlg); + WaitForClosedForm(); + + //_pythonTestUtil.InstallPython(_buildLibraryDlg); + //WaitForClosedForm(); + + //AbstractFunctionalTest.RunLongDlg(nvidiaResult.OkDialog, buildDlg => { }, dlg => { }); + } + + AlphapeptdeepBuildLibrary(libraryWithoutIrt, LibraryPathWithoutIrt, _storedHashWithoutIrt, IrtStandard.PIERCE); + OkDialog(_peptideSettings, _peptideSettings.OkDialog); + + // test with iRT + _peptideSettings = ShowPeptideSettings(PeptideSettingsUI.TABS.Library); + _buildLibraryDlg = ShowDialog(_peptideSettings.ShowBuildLibraryDlg); + + AlphapeptdeepBuildLibrary(libraryWithIrt, LibraryPathWithIrt, _storedHashWithIrt, IrtStandard.PIERCE); + OkDialog(_peptideSettings, _peptideSettings.OkDialog); + + var spectralLibraryViewer = ShowDialog(SkylineWindow.ViewSpectralLibraries); + RunUI(() => + { + spectralLibraryViewer.ChangeSelectedLibrary(libraryWithoutIrt); + spectralLibraryViewer.ChangeSelectedLibrary(libraryWithIrt); + }); + + OkDialog(spectralLibraryViewer, spectralLibraryViewer.Close); + } + + /// + /// Test goes through building of a Library by AlphaPeptDeep with or without iRT + /// + /// Name of the library to build + /// Path of the library to build + /// checksum of the library to build + /// iRT standard type + private void AlphapeptdeepBuildLibrary( string libraryName, string libraryPath, string storedHash, IrtStandard iRTtype = null) + { + + RunUI(() => + { + _buildLibraryDlg.LibraryName = libraryName; + _buildLibraryDlg.LibraryPath = libraryPath; + _buildLibraryDlg.AlphaPeptDeep = true; + if (iRTtype != null) _buildLibraryDlg.IrtStandard = iRTtype; + }); + + // Test the control path where Python needs installation and is + + if (!_pythonTestUtil.HavePythonPrerequisite(_buildLibraryDlg)) + { + //PauseTest(); + + // Test the control path where Python is installable + if (!_pythonTestUtil.InstallPython(_buildLibraryDlg)) + { + OkDialog(_buildLibraryDlg, _buildLibraryDlg.OkWizardPage); + AbstractFunctionalTest.WaitForClosedForm(); + } + + //PauseTest(); + //TestResultingLibByHash(storedHash); + TestResultingLibByValues(); + + } + else + { + AbstractFunctionalTest.RunLongDlg(_buildLibraryDlg.OkWizardPage, WaitForClosedForm, dlg => { + }); + + //TestResultingLibByHash(storedHash); + TestResultingLibByValues(); + } + + } + protected override void Cleanup() + { + DirectoryEx.SafeDelete("TestAlphapeptdeepBuildLibrary"); + } + + private void TestResultingLibByValues() + { + string product = _buildLibraryDlg.BuilderLibFilepath; + string answer = TestFilesDir.GetTestPath(@"predict.speclib.tsv"); + using (var answerReader = new StreamReader(answer)) + { + using (var productReader = new StreamReader(product)) + { + AssertEx.FieldsEqual(productReader, answerReader, 13, null, true, 0, 1e-3); + } + } + } + private void TestResultingLibByHash(string storedHash) + { + Assert.IsTrue(_pythonTestUtil.HavePythonPrerequisite(_buildLibraryDlg)); + + //PauseTest(); + + //OkDialog(_peptideSettings, _peptideSettings.OkDialog); + + // Test the hash of the created library + string libHash = PythonInstallerUtil.GetFileHash(_buildLibraryDlg.BuilderLibFilepath); + + Assert.AreEqual(storedHash, libHash); + } + } +} diff --git a/pwiz_tools/Skyline/TestPerf/AlphapeptdeepBuildLibraryTest.zip b/pwiz_tools/Skyline/TestPerf/AlphapeptdeepBuildLibraryTest.zip new file mode 100644 index 0000000000..d09c91b559 Binary files /dev/null and b/pwiz_tools/Skyline/TestPerf/AlphapeptdeepBuildLibraryTest.zip differ diff --git a/pwiz_tools/Skyline/TestPerf/TestPerf.csproj b/pwiz_tools/Skyline/TestPerf/TestPerf.csproj index a13f8c3880..7d733b7a4f 100644 --- a/pwiz_tools/Skyline/TestPerf/TestPerf.csproj +++ b/pwiz_tools/Skyline/TestPerf/TestPerf.csproj @@ -134,6 +134,7 @@ + diff --git a/pwiz_tools/Skyline/TestRunnerLib/TestRunnerFormLookup.csv b/pwiz_tools/Skyline/TestRunnerLib/TestRunnerFormLookup.csv index 36c55e1f0a..87a40f0966 100644 --- a/pwiz_tools/Skyline/TestRunnerLib/TestRunnerFormLookup.csv +++ b/pwiz_tools/Skyline/TestRunnerLib/TestRunnerFormLookup.csv @@ -180,7 +180,8 @@ PermuteIsotopeModificationsDlg,TestModificationPermuter PivotEditor,TestPivotEditor PopupPickList,TestPrecursorIon PublishDocumentDlg,TestAccessServer -PythonInstaller,TestPythonInstaller +PythonInstallerDlg, TestPythonInstaller +PythonInstallerLegacyDlg,TestPythonInstallerLegacyDlg QuickFilterForm,TestLiveReportsFilter RefineDlg.ConsistencyTab,TestMethodRefinementTutorial RefineDlg.DocumentTab,TestMethodEditTutorial diff --git a/pwiz_tools/Skyline/TestUtil/PythonTestUtil.cs b/pwiz_tools/Skyline/TestUtil/PythonTestUtil.cs new file mode 100644 index 0000000000..511dbda824 --- /dev/null +++ b/pwiz_tools/Skyline/TestUtil/PythonTestUtil.cs @@ -0,0 +1,343 @@ +/* + * Author: David Shteynberg , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2025 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using pwiz.Skyline.Alerts; +using pwiz.Skyline.Model.Tools; +using pwiz.Skyline.SettingsUI; +using pwiz.Skyline.ToolsUI; +using System; +using pwiz.Skyline.Model; + +namespace pwiz.SkylineTestUtil +{ public class PythonTestUtil + { + private string _pythonVersion + { + get; set; + } + private string _toolName + { + get; set; + } + + private bool _undoRegistry; + + public const int WAIT_TIME = 15 * 60 * 1000; + + public PythonTestUtil(string pythonVersion, string toolName, bool cleanSlate) + { + _pythonVersion = pythonVersion; + _toolName = toolName; + if (cleanSlate) + AssertEx.IsTrue(PythonInstaller.DeleteToolsPythonDirectory()); + } + /// + /// Pretends Python needs installation then Cancels Python install + /// + /// Build Library dialog + public void CancelPython(BuildLibraryDlg buildLibraryDlg) + { + // Test the control path where Python is not installed, and the user is prompted to deal with admin access + PythonInstaller.SimulatedInstallationState = PythonInstaller.eSimulatedInstallationState.NAIVE; // Simulates not having the needed registry settings + MultiButtonMsgDlg installPythonDlg = AbstractFunctionalTest.ShowDialog(() => buildLibraryDlg.OkWizardPage()); // Expect the offer to install Python + //PauseTest("install offer"); + AssertEx.AreComparableStrings(ToolsUIResources.PythonInstaller_BuildPrecursorTable_Python_0_installation_is_required, installPythonDlg.Message); + AbstractFunctionalTest.CancelDialog(installPythonDlg, installPythonDlg.CancelDialog); // Cancel it immediately + //PauseTest("back to wizard"); + installPythonDlg = AbstractFunctionalTest.ShowDialog(() => buildLibraryDlg.OkWizardPage()); // Expect the offer to install Python + // PauseTest("install offer again"); + AssertEx.AreComparableStrings(ToolsUIResources.PythonInstaller_BuildPrecursorTable_Python_0_installation_is_required, installPythonDlg.Message); + + AbstractFunctionalTest.OkDialog(installPythonDlg, installPythonDlg.OkDialog); + var needAdminDlg = AbstractFunctionalTest.WaitForOpenForm(); // Cannot open AlertDlg when another AlertDlg is open, must close first then wait to open another + + AssertEx.AreComparableStrings(ToolsUIResources.PythonInstaller_Requesting_Administrator_elevation, needAdminDlg.Message); + AbstractFunctionalTest.CancelDialog(needAdminDlg , needAdminDlg.CancelDialog); + + + // PauseTest("need admin msg"); + // PauseTest("back to wizard"); + } + + public void InstallPythonTestNvidia(BuildLibraryDlg buildLibraryDlg) + { + // Test the control path where Nvidia Card is Available and Nvidia Libraries are not installed, and the user is prompted to deal with Nvidia + PythonInstaller.SimulatedInstallationState = PythonInstaller.eSimulatedInstallationState.NONVIDIASOFT; // Simulates not having Nvidia library but having the GPU + + //Test for LongPaths not set and admin + if (PythonInstaller.IsRunningElevated() && !PythonInstallerTaskValidator.ValidateEnableLongpaths()) + { + AlertDlg adminDlg = AbstractFunctionalTest.ShowDialog(() => buildLibraryDlg.OkWizardPage(), WAIT_TIME); // Expect request for elevated privileges + AssertEx.AreComparableStrings(ToolsUIResources.PythonInstaller_Requesting_Administrator_elevation, adminDlg.Message); + AbstractFunctionalTest.OkDialog(adminDlg, adminDlg.OkDialog); + } + else if (!PythonInstallerTaskValidator.ValidateEnableLongpaths()) + { + Assert.Fail($@"Error: Cannot finish {_toolName}BuildLibraryTest because {PythonInstaller.REG_FILESYSTEM_KEY}\{PythonInstaller.REG_LONGPATHS_ENABLED} is not set and have insufficient permissions to set it"); + } + + MultiButtonMsgDlg installNvidiaDlg = AbstractFunctionalTest.ShowDialog(() => buildLibraryDlg.OkWizardPage(), WAIT_TIME); // Expect the offer to installNvidia + AssertEx.AreComparableStrings(ToolsUIResources.PythonInstaller_Install_Cuda_Library, + installNvidiaDlg.Message); + AbstractFunctionalTest.CancelDialog(installNvidiaDlg, installNvidiaDlg.CancelDialog); + installNvidiaDlg = AbstractFunctionalTest.ShowDialog(() => buildLibraryDlg.OkWizardPage(), WAIT_TIME); + AssertEx.AreComparableStrings(ToolsUIResources.PythonInstaller_Install_Cuda_Library, + installNvidiaDlg.Message); + AbstractFunctionalTest.OkDialog(installNvidiaDlg, installNvidiaDlg.ClickYes); + var needAdminDlg = AbstractFunctionalTest.WaitForOpenForm(); + AssertEx.AreComparableStrings(ModelResources.NvidiaInstaller_Requesting_Administrator_elevation, + needAdminDlg.Message); + AbstractFunctionalTest.CancelDialog(needAdminDlg, () => needAdminDlg.CancelDialog()); // Expect the offer to installNvidia + installNvidiaDlg = AbstractFunctionalTest.ShowDialog(() => buildLibraryDlg.OkWizardPage(), WAIT_TIME); + AssertEx.AreComparableStrings(ToolsUIResources.PythonInstaller_Install_Cuda_Library, + installNvidiaDlg.Message); + AbstractFunctionalTest.CancelDialog(installNvidiaDlg, installNvidiaDlg.ClickNo); + } + + /// + /// Pretends no NVIDIA hardware then Installs Python, returns true if Python installer ran (successful or not), false otherwise + /// + /// Build Library + /// + public bool InstallPython(BuildLibraryDlg buildLibraryDlg) + { + PythonInstaller.SimulatedInstallationState = PythonInstaller.eSimulatedInstallationState.NONVIDIAHARD; // Normal tests systems will have registry set suitably + + bool havePythonPrerequisite = false; + + AbstractFunctionalTest.RunUI(() => { havePythonPrerequisite = buildLibraryDlg.PythonRequirementMet(); }); + + if (!havePythonPrerequisite) + { + AlertDlg confirmDlg = null; + AbstractFunctionalTest.RunLongDlg(buildLibraryDlg.OkWizardPage, pythonDlg => + { + Assert.AreEqual(string.Format(ToolsUIResources.PythonInstaller_BuildPrecursorTable_Python_0_installation_is_required, + _pythonVersion, _toolName), pythonDlg.Message); + + if (!PythonInstallerTaskValidator.ValidateEnableLongpaths()) + { + AlertDlg longPathDlg = AbstractFunctionalTest.ShowDialog(pythonDlg.OkDialog); + + Assert.AreEqual(string.Format(ToolsUIResources.PythonInstaller_Requesting_Administrator_elevation), + longPathDlg.Message); + + if (PythonInstaller.IsRunningElevated()) + { + confirmDlg = AbstractFunctionalTest.ShowDialog(pythonDlg.OkDialog, WAIT_TIME); + ConfirmPythonSuccess(confirmDlg); + + } + else + { + Assert.Fail($@"Error: Cannot finish {_toolName}BuildLibraryTest because {PythonInstaller.REG_FILESYSTEM_KEY}\{PythonInstaller.REG_LONGPATHS_ENABLED} is not set and have insufficient permissions to set it"); + } + } + else + { + Console.WriteLine(@"Info: LongPathsEnabled registry key is already set to 1"); + AbstractFunctionalTest.OkDialog(pythonDlg, pythonDlg.OkDialog); + confirmDlg = AbstractFunctionalTest.WaitForOpenForm(WAIT_TIME); + ConfirmPythonSuccess(confirmDlg); + + } + + + }, dlg => { + dlg.Close(); + }); + if (_undoRegistry) + { + PythonInstallerTaskValidator.DisableWindowsLongPaths(); + } + + return true; + } + + return false; + } + /// + /// Checks for Python prerequisite + /// + /// Build Library dialog + /// + public bool HavePythonPrerequisite(BuildLibraryDlg buildLibraryDlg) + { + bool havePythonPrerequisite = false; + AbstractFunctionalTest.RunUI(() => { havePythonPrerequisite = buildLibraryDlg.PythonRequirementMet(); }); + return havePythonPrerequisite; + } + + public bool HaveNvidiaSoftware() + { + return PythonInstaller.NvidiaLibrariesInstalled(); + } + + public bool HaveNvidiaHardware() + { + return PythonInstaller.TestForNvidiaGPU() == true; + } + /// + /// Runs Nvidia Dialog + /// + /// Nvidia Detected Dialog + /// Python Installer Dialog + /// true clicks No, false clicks Yes, null clicks Cancel to Nvidia Detected Dialog + private void RunNvidiaDialog(MultiButtonMsgDlg nvidiaDlg, MultiButtonMsgDlg pythonDlg, bool? clickNo = true) + { + if (clickNo == true) + { + + // Say 'No' + AbstractFunctionalTest.RunLongDlg(nvidiaDlg.ClickNo, confirmDlg => + { + + }, confirmDlg => { + ConfirmPythonSuccess(confirmDlg); + }); + } + else if (clickNo == false) + { + // Say 'Yes' + AbstractFunctionalTest.RunLongDlg(nvidiaDlg.ClickYes, confirmDlg => + { + + }, confirmDlg => { + ConfirmPythonSuccess(confirmDlg); + }); + } + else // clickNo == null + { + // Say 'Cancel' + AbstractFunctionalTest.RunLongDlg(nvidiaDlg.ClickCancel, confirmDlg => + { + + }, confirmDlg => { + ConfirmPythonSuccess(confirmDlg); + }); + + } + + if (!nvidiaDlg.IsDisposed) + nvidiaDlg.Dispose(); + } + + /// + /// Helps with Nvidia GPU Detections + /// + /// Python set up is required dialog + /// What to tell Nvidia Dialog: Yes=install, No=don't install, null=cancel operation + private void NvidiaTestHelper( MultiButtonMsgDlg pythonDlg, bool? nvidiaClickNo) + { + PythonInstaller.SimulatedInstallationState = PythonInstaller.eSimulatedInstallationState.NONVIDIASOFT; + if (PythonInstaller.TestForNvidiaGPU() == true && !PythonInstaller.NvidiaLibrariesInstalled()) + { + Console.WriteLine(@"Info: NVIDIA GPU DETECTED on test node"); + + MultiButtonMsgDlg nvidiaDlg = AbstractFunctionalTest.ShowMultiButtonMsgDlg(pythonDlg.OkDialog, ToolsUIResources.PythonInstaller_Install_Cuda_Library, WAIT_TIME); + + RunNvidiaDialog(nvidiaDlg, pythonDlg, nvidiaClickNo); + + } + else + { + if (PythonInstaller.TestForNvidiaGPU() != true) + Console.WriteLine(@"Info: NVIDIA GPU *NOT* DETECTED on test node"); + else + Console.WriteLine(@"Info: Nvidia libraries already installed"); + AbstractFunctionalTest.OkDialog(pythonDlg, pythonDlg.OkDialog); + //Not cancelled + var confirmDlg = AbstractFunctionalTest.ShowDialog(pythonDlg.OkDialog, WAIT_TIME); + ConfirmPythonSuccess(confirmDlg); + + } + + + + + } + + /// + /// Tries to set EnableLongPaths + /// + /// EnableLongPaths registry dialog + /// + private MessageDlg RunLongPathsDialog(MessageDlg longPathDlg) + { + Console.WriteLine(@"Info: Trying to set LongPathsEnabled registry key to 1"); + AbstractFunctionalTest.OkDialog(longPathDlg, longPathDlg.OkDialog); + + MessageDlg okDlg = AbstractFunctionalTest.ShowDialog(longPathDlg.OkDialog); + + Console.WriteLine(@"Info: Successfully set LongPathsEnabled registry key to 1"); + _undoRegistry = true; + + if (!longPathDlg.IsDisposed) + longPathDlg.Dispose(); + + return okDlg; + } + + /// + /// Confirms Python installation success + /// + /// Message dialog success + private void ConfirmPythonSuccess(AlertDlg confirmDlg) + { + ConfirmPython(confirmDlg); + } + /// + /// Confirms Python installation failure + /// + /// Message dialog failed + private void ConfirmPythonFailed(AlertDlg confirmDlg) + { + ConfirmPython(confirmDlg, false); + } + + /// + /// Confirms Python installation + /// + /// Alert dialog + /// true for success, false for failure + private void ConfirmPython(AlertDlg confirmDlg, bool confirmSuccess = true) + { + var expectMsg = string.Format(ToolsUIResources + .PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment); + if (confirmSuccess) + expectMsg = string.Format(ToolsUIResources + .PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment); + + Assert.AreEqual(expectMsg, confirmDlg.Message); + AbstractFunctionalTest.OkDialog(confirmDlg, confirmDlg.OkDialog); + } + + /// + /// Second dialog after Nvidia is detected to direct user to admin instructions for setting up Nvidia + /// + /// Message dialog to the user with admin instructions + private void ConfirmInstallNvidiaBatMessage(MessageDlg confirmDlg) + { + AssertEx.AreComparableStrings(string.Format(ModelResources.NvidiaInstaller_Requesting_Administrator_elevation, PythonInstaller.GetInstallNvidiaLibrariesBat()), + confirmDlg.Message); + AbstractFunctionalTest.OkDialog(confirmDlg, confirmDlg.OkDialog); + } + } +} diff --git a/pwiz_tools/Skyline/TestUtil/TestFunctional.cs b/pwiz_tools/Skyline/TestUtil/TestFunctional.cs index f96cfbc90b..6f3b61f06b 100644 --- a/pwiz_tools/Skyline/TestUtil/TestFunctional.cs +++ b/pwiz_tools/Skyline/TestUtil/TestFunctional.cs @@ -282,7 +282,7 @@ protected void ShowSkyline(Action act) SkylineWindow.DocumentChangedEvent += OnDocumentChangedLogging; } - protected static TDlg ShowDialog(Action act, int millis = -1) where TDlg : Form + protected internal static TDlg ShowDialog(Action act, int millis = -1) where TDlg : Form { var existingDialog = FindOpenForm(); if (existingDialog != null) @@ -305,6 +305,29 @@ protected static TDlg ShowDialog(Action act, int millis = -1) where TDlg : return dlg; } + /// + /// Like ShowDialog - "expect to see a dialog as a consequence of this action" - but specifically for MultiButtonMsgDlg + /// showing specific messages, since it's possible to have more than one MultiButtonMsgDlg open at the same time. + /// + /// + /// Can be a formatting string e.g. "Please {0} the {1}" etc + /// timeout + /// MultiButtonMsgDlg found to have the expected string + protected internal static MultiButtonMsgDlg ShowMultiButtonMsgDlg(Action act, string messageFormat, int millis = WAIT_TIME) + { + var existingDialog = WaitForMultiButtonMsgDlg(messageFormat, 1); + if (existingDialog != null) + { + Assert.Fail("MultiButtonMsgDlg is already open with the message: " + messageFormat); + } + + SkylineBeginInvoke(act); + var dlg = WaitForMultiButtonMsgDlg(messageFormat, millis); + Assert.IsNotNull(dlg); + + return dlg; + } + private static void EnsureScreenshotIcon(Form dlg) { if (IsRecordingScreenShots && dlg.ShowIcon && !ReferenceEquals(dlg, SkylineWindow)) @@ -405,7 +428,7 @@ private static void SkylineBeginInvoke(Action act) /// /// Action which causes the dialog to be shown /// Action which can do some things and then must close the dialog. - protected static void RunDlg([InstantHandle] Action showDlgAction, + protected internal static void RunDlg([InstantHandle] Action showDlgAction, [InstantHandle] [NotNull] Action exerciseDlgAction) where TDlg : Form { @@ -428,7 +451,7 @@ protected static void RunDlg([InstantHandle] Action showDlgAction, /// Action which runs on the UI thread and causes the dialog to be shown /// Action which runs on the test thread and interacts with the dialog /// Action which runs on the UI thread and closes the dialog - protected static void RunLongDlg([InstantHandle] Action showDlgAction, [InstantHandle] Action exerciseDlgAction, Action closeDlgAction) where TDlg : Form + protected internal static void RunLongDlg([InstantHandle] Action showDlgAction, [InstantHandle] Action exerciseDlgAction, Action closeDlgAction) where TDlg : Form { bool showDlgActionCompleted = false; TDlg dlg = ShowDialog(() => diff --git a/pwiz_tools/Skyline/TestUtil/TestUtil.csproj b/pwiz_tools/Skyline/TestUtil/TestUtil.csproj index 40494b8ace..0dcfa872ce 100644 --- a/pwiz_tools/Skyline/TestUtil/TestUtil.csproj +++ b/pwiz_tools/Skyline/TestUtil/TestUtil.csproj @@ -142,6 +142,7 @@ True + False @@ -183,6 +184,7 @@ True TestUtilSettings.settings + Form diff --git a/pwiz_tools/Skyline/ToolsUI/PythonInstallerDlg.Designer.cs b/pwiz_tools/Skyline/ToolsUI/PythonInstallerDlg.Designer.cs new file mode 100644 index 0000000000..ed8f5a9816 --- /dev/null +++ b/pwiz_tools/Skyline/ToolsUI/PythonInstallerDlg.Designer.cs @@ -0,0 +1,86 @@ +namespace pwiz.Skyline.ToolsUI +{ + partial class PythonInstallerDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PythonInstallerDlg)); + this.labelDescription = new System.Windows.Forms.Label(); + this.textBoxPackages = new System.Windows.Forms.TextBox(); + this.btnInstall = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // labelDescription + // + resources.ApplyResources(this.labelDescription, "labelDescription"); + this.labelDescription.Name = "labelDescription"; + // + // textBoxPackages + // + this.textBoxPackages.BackColor = System.Drawing.SystemColors.Control; + this.textBoxPackages.BorderStyle = System.Windows.Forms.BorderStyle.None; + resources.ApplyResources(this.textBoxPackages, "textBoxPackages"); + this.textBoxPackages.Name = "textBoxPackages"; + // + // btnInstall + // + resources.ApplyResources(this.btnInstall, "btnInstall"); + this.btnInstall.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnInstall.Name = "btnInstall"; + this.btnInstall.UseVisualStyleBackColor = true; + this.btnInstall.Click += new System.EventHandler(this.btnInstall_Click); + // + // btnCancel + // + resources.ApplyResources(this.btnCancel, "btnCancel"); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Name = "btnCancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // PythonInstallerDlg + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnInstall); + this.Controls.Add(this.textBoxPackages); + this.Controls.Add(this.labelDescription); + this.Name = "PythonInstallerDlg"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label labelDescription; + private System.Windows.Forms.TextBox textBoxPackages; + private System.Windows.Forms.Button btnInstall; + private System.Windows.Forms.Button btnCancel; + } +} \ No newline at end of file diff --git a/pwiz_tools/Skyline/ToolsUI/PythonInstallerDlg.cs b/pwiz_tools/Skyline/ToolsUI/PythonInstallerDlg.cs new file mode 100644 index 0000000000..055e444702 --- /dev/null +++ b/pwiz_tools/Skyline/ToolsUI/PythonInstallerDlg.cs @@ -0,0 +1,98 @@ +using System; +using System.Diagnostics; +using System.Text; +using System.Windows.Forms; +using pwiz.Common.Collections; +using pwiz.Skyline.Alerts; +using pwiz.Skyline.Controls; +using pwiz.Skyline.Model.Tools; +using pwiz.Skyline.Util; +using pwiz.Skyline.Util.Extensions; + +namespace pwiz.Skyline.ToolsUI +{ + public partial class PythonInstallerDlg : FormEx + { + private const string HYPHEN = TextUtil.HYPHEN; + private const string SPACE = TextUtil.SPACE; + + private PythonInstaller Installer { get; } + + public PythonInstallerDlg(PythonInstaller installer) + { + InitializeComponent(); + Installer = installer; + PopulateDlgText(); + } + + public void OkDialog() + { + var tasks = Installer.PendingTasks.IsNullOrEmpty() ? + Installer.ValidatePythonVirtualEnvironment() : Installer.PendingTasks; + Installer.NumTotalTasks = tasks.Count; + Installer.NumCompletedTasks = 0; + foreach (var task in tasks) + { + try + { + if (task.IsActionWithNoArg) + { + using var waitDlg = new LongWaitDlg(); + waitDlg.Message = task.InProgressMessage; + waitDlg.PerformWork(this, 50, task.AsActionWithNoArg); + } + else if (task.IsActionWithProgressMonitor) + { + using var waitDlg = new LongWaitDlg(); + waitDlg.ProgressValue = 0; + waitDlg.PerformWork(this, 50, task.AsActionWithProgressMonitor); + } + else + { + throw new PythonInstallerUnsupportedTaskException(task); + } + Installer.NumCompletedTasks++; + } + catch (Exception ex) + { + MessageDlg.Show(this, task.FailureMessage); + MessageDlg.ShowWithException(this, (ex.InnerException ?? ex).Message, ex); + break; + } + } + Debug.WriteLine($@"total: {Installer.NumTotalTasks}, completed: {Installer.NumCompletedTasks}"); + if (Installer.NumCompletedTasks == Installer.NumTotalTasks) + { + Installer.PendingTasks.Clear(); + MessageDlg.Show(this, ToolsUIResources.PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment); + DialogResult = DialogResult.OK; + } + else + { + MessageDlg.Show(this, ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment); + DialogResult = DialogResult.Cancel; + } + } + + private void PopulateDlgText() + { + this.labelDescription.Text = $@"This tool requires Python {Installer.PythonVersion} and the following packages. A dedicated python virtual environment {Installer.VirtualEnvironmentName} will be created for the tool. Click the ""Install"" button to start installation."; + var packagesStringBuilder = new StringBuilder(); + foreach (var package in Installer.PythonPackages) + { + packagesStringBuilder.Append(HYPHEN + SPACE + package.Name); + if (package.Version != null) + { + packagesStringBuilder.Append(SPACE + package.Version); + } + packagesStringBuilder.Append(Environment.NewLine); + } + this.textBoxPackages.Text = packagesStringBuilder.ToString(); + } + + private void btnInstall_Click(object sender, EventArgs e) + { + OkDialog(); + } + } +} diff --git a/pwiz_tools/Skyline/ToolsUI/PythonInstallerDlg.resx b/pwiz_tools/Skyline/ToolsUI/PythonInstallerDlg.resx new file mode 100644 index 0000000000..b0487f12a5 --- /dev/null +++ b/pwiz_tools/Skyline/ToolsUI/PythonInstallerDlg.resx @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + 12, 9 + + + 454, 93 + + + + 0 + + + This tool requires Python {PythonVersion} and the following packages. A dedicated python virtual environment {VirtualEnvironmentName} will be created for the tool. Click the "Install" button to start installation. + + + labelDescription + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 16, 105 + + + True + + + 450, 364 + + + 1 + + + - package1 +- package2 1.2.3 +- package3 + + + textBoxPackages + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + + Bottom, Right + + + NoControl + + + 233, 495 + + + 4, 5, 4, 5 + + + 112, 35 + + + 3 + + + Install + + + btnInstall + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Bottom, Right + + + NoControl + + + 353, 495 + + + 4, 5, 4, 5 + + + 112, 35 + + + 4 + + + Cancel + + + btnCancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 478, 544 + + + PythonInstallerDlg + + + modeUIHandler + + + pwiz.Skyline.Util.Helpers+ModeUIExtender, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null + + + PythonInstallerDlg + + + pwiz.Skyline.Util.FormEx, Skyline-daily, Version=24.1.1.233, Culture=neutral, PublicKeyToken=null + + \ No newline at end of file diff --git a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.Designer.cs b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.Designer.cs similarity index 94% rename from pwiz_tools/Skyline/ToolsUI/PythonInstaller.Designer.cs rename to pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.Designer.cs index 7b7772502a..5d3eb693f1 100644 --- a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.Designer.cs +++ b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.Designer.cs @@ -1,92 +1,92 @@ -namespace pwiz.Skyline.ToolsUI -{ - partial class PythonInstaller - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PythonInstaller)); - this.btnInstall = new System.Windows.Forms.Button(); - this.btnCancel = new System.Windows.Forms.Button(); - this.labelMessage = new System.Windows.Forms.Label(); - this.clboxPackages = new System.Windows.Forms.CheckedListBox(); - this.SuspendLayout(); - // - // btnInstall - // - resources.ApplyResources(this.btnInstall, "btnInstall"); - this.btnInstall.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.btnInstall.Name = "btnInstall"; - this.btnInstall.UseVisualStyleBackColor = true; - this.btnInstall.Click += new System.EventHandler(this.btnInstall_Click); - // - // btnCancel - // - resources.ApplyResources(this.btnCancel, "btnCancel"); - this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.btnCancel.Name = "btnCancel"; - this.btnCancel.UseVisualStyleBackColor = true; - // - // labelMessage - // - resources.ApplyResources(this.labelMessage, "labelMessage"); - this.labelMessage.Name = "labelMessage"; - // - // clboxPackages - // - resources.ApplyResources(this.clboxPackages, "clboxPackages"); - this.clboxPackages.CheckOnClick = true; - this.clboxPackages.FormattingEnabled = true; - this.clboxPackages.Name = "clboxPackages"; - // - // PythonInstaller - // - this.AcceptButton = this.btnInstall; - resources.ApplyResources(this, "$this"); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.CancelButton = this.btnCancel; - this.Controls.Add(this.clboxPackages); - this.Controls.Add(this.labelMessage); - this.Controls.Add(this.btnCancel); - this.Controls.Add(this.btnInstall); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "PythonInstaller"; - this.ShowIcon = false; - this.ShowInTaskbar = false; - this.Load += new System.EventHandler(this.PythonInstaller_Load); - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.Button btnInstall; - private System.Windows.Forms.Button btnCancel; - private System.Windows.Forms.Label labelMessage; - private System.Windows.Forms.CheckedListBox clboxPackages; - } +namespace pwiz.Skyline.ToolsUI +{ + partial class PythonInstallerLegacyDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PythonInstallerLegacyDlg)); + this.btnInstall = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.labelMessage = new System.Windows.Forms.Label(); + this.clboxPackages = new System.Windows.Forms.CheckedListBox(); + this.SuspendLayout(); + // + // btnInstall + // + resources.ApplyResources(this.btnInstall, "btnInstall"); + this.btnInstall.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnInstall.Name = "btnInstall"; + this.btnInstall.UseVisualStyleBackColor = true; + this.btnInstall.Click += new System.EventHandler(this.btnInstall_Click); + // + // btnCancel + // + resources.ApplyResources(this.btnCancel, "btnCancel"); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Name = "btnCancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // labelMessage + // + resources.ApplyResources(this.labelMessage, "labelMessage"); + this.labelMessage.Name = "labelMessage"; + // + // clboxPackages + // + resources.ApplyResources(this.clboxPackages, "clboxPackages"); + this.clboxPackages.CheckOnClick = true; + this.clboxPackages.FormattingEnabled = true; + this.clboxPackages.Name = "clboxPackages"; + // + // PythonInstaller + // + this.AcceptButton = this.btnInstall; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.Controls.Add(this.clboxPackages); + this.Controls.Add(this.labelMessage); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnInstall); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PythonInstallerLegacyDlg"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.Load += new System.EventHandler(this.PythonInstaller_Load); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button btnInstall; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Label labelMessage; + private System.Windows.Forms.CheckedListBox clboxPackages; + } } \ No newline at end of file diff --git a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.cs b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.cs similarity index 94% rename from pwiz_tools/Skyline/ToolsUI/PythonInstaller.cs rename to pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.cs index affaab3a29..c9890ea84e 100644 --- a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.cs +++ b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.cs @@ -1,589 +1,589 @@ -/* - * Original author: Trevor Killeen , - * MacCoss Lab, Department of Genome Sciences, UW - * - * Copyright 2013 University of Washington - Seattle, WA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Windows.Forms; -using Microsoft.Win32; -using pwiz.Common.SystemUtil; -using pwiz.Skyline.Alerts; -using pwiz.Skyline.Controls; -using pwiz.Skyline.Model.Tools; -using pwiz.Skyline.Properties; -using pwiz.Skyline.Util; -using pwiz.Skyline.Util.Extensions; - -namespace pwiz.Skyline.ToolsUI -{ - public partial class PythonInstaller : FormEx - { - private readonly string _version; - private readonly bool _installed; - private readonly TextWriter _writer; - - private IList PackageUris { get; set; } - private IList LocalPackages { get; set; } - - public PythonInstaller(ProgramPathContainer pythonPathContainer, IEnumerable packages, TextWriter writer) - : this(pythonPathContainer, packages, PythonUtil.CheckInstalled(pythonPathContainer.ProgramVersion), writer) - { - } - - public PythonInstaller(ProgramPathContainer pythonPathContainer, IEnumerable packages, bool installed, TextWriter writer) - { - _version = pythonPathContainer.ProgramVersion; - _installed = installed; - _writer = writer; - AssignPackages(packages); - InitializeComponent(); - } - - private void AssignPackages(IEnumerable packages) - { - PackageUris = new List(); - LocalPackages = new List(); - foreach (var package in packages) - { - if (package.StartsWith(@"http")) - PackageUris.Add(package); - else - LocalPackages.Add(package); - } - } - - public bool IsLoaded { get; private set; } - - private void PythonInstaller_Load(object sender, EventArgs e) - { - if (!_installed && (PackageUris.Count + LocalPackages.Count) != 0) - { - labelMessage.Text = string.Format(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_Python__0__and_the_following_packages__Select_packages_to_install_and_then_click_Install_to_begin_the_installation_process_, _version); - PopulatePackageCheckListBox(); - } else if (!_installed) - { - labelMessage.Text = string.Format(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_Python__0___Click_install_to_begin_the_installation_process_, _version); - int shift = btnCancel.Top - clboxPackages.Top; - clboxPackages.Visible = clboxPackages.Enabled = false; - Height -= shift; - } - else if ((PackageUris.Count + LocalPackages.Count) != 0) - { - labelMessage.Text = Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_the_following_Python_packages__Select_packages_to_install_and_then_click_Install_to_begin_the_installation_process_; - PopulatePackageCheckListBox(); - } - IsLoaded = true; - } - - private void PopulatePackageCheckListBox() - { - clboxPackages.DataSource = IsolatePackageNames(PackageUris).Concat(IsolatePackageNames(LocalPackages)).ToList(); - - // initially set them as checked - for (int i = 0; i < clboxPackages.Items.Count; i++) - { - clboxPackages.SetItemChecked(i, true); - } - } - - private static IEnumerable IsolatePackageNames(IEnumerable packages) - { - ICollection packageNames = new Collection(); - const string pattern = @"([^/\\]*)\.(zip|tar\.gz|exe)$"; - foreach (var package in packages) - { - Match name = Regex.Match(package, pattern); - packageNames.Add(name.Groups[1].ToString()); - } - return packageNames; - } - - private void btnInstall_Click(object sender, EventArgs e) - { - OkDialog(); - } - - public void OkDialog() - { - Enabled = false; - - if ((_installed || GetPython()) && (clboxPackages.CheckedIndices.Count == 0 || GetPackages())) - { - DialogResult = DialogResult.OK; - } - else - { - DialogResult = DialogResult.Cancel; - } - } - - private bool GetPython() - { - try - { - using (var waitDlg = new LongWaitDlg()) - { - waitDlg.ProgressValue = 0; - // Short wait, because this can't possible happen fast enough to avoid - // showing progress, except in testing - waitDlg.PerformWork(this, 50, DownloadPython); - } - using (var waitDlg = new LongWaitDlg(null, false)) - { - waitDlg.Message = ToolsUIResources.PythonInstaller_GetPython_Installing_Python; - waitDlg.PerformWork(this, 50, InstallPython); - } - MessageDlg.Show(this, Resources.PythonInstaller_GetPython_Python_installation_completed_); - return true; - } - catch (Exception ex) - { - MessageDlg.ShowWithException(this, (ex.InnerException ?? ex).Message, ex); - } - return false; - } - - private string DownloadPath { get; set; } - - private void DownloadPython(IProgressMonitor waitBroker) - { - // the base Url for python releases - const string baseUri = "http://python.org/ftp/python/"; - - // the installer file name, e.g. python-2.7.msi - string fileName = @"python-" + _version + @".msi"; - - // the fully formed Uri, e.g. http://python.org/ftp/python/2.7/python-2.7.msi - var downloadUri = new Uri(baseUri + _version + @"/" + fileName); - - using (var webClient = TestDownloadClient ?? new MultiFileAsynchronousDownloadClient(waitBroker, 1)) - { - if (!webClient.DownloadFileAsync(downloadUri, DownloadPath = Path.GetTempPath() + fileName, out var downloadException)) - throw new ToolExecutionException(TextUtil.LineSeparate( - Resources.PythonInstaller_DownloadPython_Download_failed_, - Resources.PythonInstaller_DownloadPython_Check_your_network_connection_or_contact_the_tool_provider_for_installation_support_), downloadException); - } - } - - private void InstallPython() - { - var processRunner = TestRunProcess ?? new SynchronousRunProcess(); - var startInfo = new ProcessStartInfo - { - FileName = @"msiexec", - // ReSharper disable LocalizableElement - Arguments = "/i \"" + DownloadPath + "\"", - // ReSharper restore LocalizableElement - }; - if (processRunner.RunProcess(new Process {StartInfo = startInfo}) != 0) - throw new ToolExecutionException(Resources.PythonInstaller_InstallPython_Python_installation_failed__Canceling_tool_installation_); - } - - private bool GetPackages() - { - ICollection downloadablePackages = new Collection(); - ICollection localPackages = new Collection(); - AssignPackagesToInstall(ref downloadablePackages, ref localPackages); - - IEnumerable packagePaths = null; - try - { - // download packages - using (var waitDlg = new LongWaitDlg()) - { - waitDlg.ProgressValue = 0; - waitDlg.PerformWork(this, 500, longWaitBroker => packagePaths = DownloadPackages(longWaitBroker, downloadablePackages)); - } - - // separate packages - ICollection exePaths = new Collection(); - ICollection sourcePaths = new Collection(); - foreach (var package in (packagePaths == null) ? localPackages : packagePaths.Concat(localPackages)) - { - if (package.EndsWith(@".exe")) - exePaths.Add(package); - else - sourcePaths.Add(package); - } - - // first install executable packages, if any - if (exePaths.Count != 0) - { - using (var waitDlg = new LongWaitDlg(null, false)) - { - waitDlg.Message = ToolsUIResources.PythonInstaller_GetPackages_Installing_Packages; - waitDlg.PerformWork(this, 500, () => InstallExecutablePackages(exePaths)); - } - } - - // then install source paths, if any - if (sourcePaths.Count != 0) - { - // try and find the path to the pip package manager .exe - string pipPath = PythonUtil.GetPipPath(_version); - - // if it can't be found, install it - if (pipPath == null || TestingPip) - { - DialogResult result = MultiButtonMsgDlg.Show( - this, - Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, - ToolsUIResources.PythonInstaller_InstallPackages_Install); - if (result == DialogResult.OK && GetPip()) - { - pipPath = PythonUtil.GetPipPath(_version); - MessageDlg.Show(this, Resources.PythonInstaller_InstallPackages_Pip_installation_complete_); - } - else - { - MessageDlg.Show(this, Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_); - return false; - } - } - - using (var waitDlg = new LongWaitDlg(null, false)) - { - waitDlg.Message = ToolsUIResources.PythonInstaller_GetPackages_Installing_Packages; - waitDlg.PerformWork(this, 500, () => InstallSourcePackages(sourcePaths, pipPath)); - } - } - MessageDlg.Show(this, Resources.PythonInstaller_GetPackages_Package_installation_completed_); - return true; - } - catch (TargetInvocationException ex) - { - if (ex.InnerException is ToolExecutionException) - { - MessageDlg.ShowException(this, ex); - return false; - } - throw; - } - } - - private void AssignPackagesToInstall(ref ICollection downloadableFiles, ref ICollection localFiles) - { - foreach (int index in clboxPackages.CheckedIndices) - { - if (index < PackageUris.Count) - { - downloadableFiles.Add(PackageUris[index]); - } - else - { - localFiles.Add(LocalPackages[index - PackageUris.Count]); - } - } - } - - private IEnumerable DownloadPackages(IProgressMonitor waitBroker, IEnumerable packagesToDownload) - { - ICollection downloadPaths = new Collection(); - ICollection failedDownloads = new Collection(); - List downloadExceptions = new List(); - - using (var webClient = TestDownloadClient ?? new MultiFileAsynchronousDownloadClient(waitBroker, PackageUris.Count)) - { - foreach (var package in packagesToDownload) - { - Match file = Regex.Match(package, @"[^/]*$"); - string downloadPath = Path.GetTempPath() + file; - if (webClient.DownloadFileAsync(new Uri(package), downloadPath, out var downloadException)) - { - downloadPaths.Add(downloadPath); - } - else - { - failedDownloads.Add(package); - if (downloadException != null) - { - downloadExceptions.Add(downloadException); - } - } - } - } - - if (failedDownloads.Count != 0) - { - Exception cause; - switch (downloadExceptions.Count) - { - case 0: - cause = null; - break; - case 1: - cause = downloadExceptions[0]; - break; - default: - cause = new AggregateException(downloadExceptions); - break; - } - throw new ToolExecutionException( - TextUtil.LineSeparate( - Resources.PythonInstaller_DownloadPackages_Failed_to_download_the_following_packages_, - string.Empty, - TextUtil.LineSeparate(failedDownloads), - string.Empty, - Resources.PythonInstaller_DownloadPython_Check_your_network_connection_or_contact_the_tool_provider_for_installation_support_), cause); - } - return downloadPaths; - } - - public void InstallExecutablePackages(IEnumerable packages) - { - foreach (var package in packages) - { - var processRunner = TestRunProcess ?? new SynchronousRunProcess(); - if (processRunner.RunProcess(new Process { StartInfo = new ProcessStartInfo(package) }) != 0) - { - throw new ToolExecutionException(Resources.PythonInstaller_InstallPackages_Package_Installation_was_not_completed__Canceling_tool_installation_); - } - } - } - - private void InstallSourcePackages(ICollection packages, string pipPath) - { - // then install packages from source - if (packages.Count != 0) - { - if (!File.Exists(pipPath) && !TestingPip) - throw new ToolExecutionException(Resources.PythonInstaller_InstallPackages_Unknown_error_installing_packages_); - - var argumentBuilder = new StringBuilder(@"echo installing packages"); - foreach (var package in packages) - { - // ReSharper disable LocalizableElement - argumentBuilder.Append(" & ") - .Append(pipPath) - .Append(" install ") - .Append("\"") - .Append(package) - .Append("\""); - // ReSharper restore LocalizableElement - } - - var pipedProcessRunner = TestSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); - try - { - if (pipedProcessRunner.RunProcess(argumentBuilder.ToString(), false, _writer) != 0) - throw new ToolExecutionException(Resources.PythonInstaller_InstallPackages_Package_installation_failed__Error_log_output_in_immediate_window_); - } - catch (IOException) - { - throw new ToolExecutionException(Resources.PythonInstaller_InstallPackages_Unknown_error_installing_packages_); - } - } - } - - private bool GetPip() - { - try - { - using (var dlg = new LongWaitDlg()) - { - dlg.ProgressValue = 0; - // Short wait, because this can't possible happen fast enough to avoid - // showing progress, except in testing - dlg.PerformWork(this, 50, DownloadPip); - } - using (var dlg = new LongWaitDlg(null, false)) - { - dlg.Message = ToolsUIResources.PythonInstaller_GetPip_Installing_Pip; - dlg.PerformWork(this, 50, InstallPip); - } - } - catch (TargetInvocationException ex) - { - if (ex.InnerException is ToolExecutionException) - { - MessageDlg.ShowException(this, ex); - return false; - } - throw; - } - return true; - } - - private string SetupToolsPath { get; set; } - private string PipPath { get; set; } - - // Consider: the location of the following python links is assumed to be relatively stable, but could change. We - // might want to package these scripts with Skyline itself to assure that they are available - - private void DownloadPip(IProgressMonitor longWaitBroker) - { - // location of the setuptools install script - const string setupToolsScript = "https://bitbucket.org/pypa/setuptools/downloads/ez_setup.py"; - SetupToolsPath = Path.GetTempPath() + @"ez_setup.py"; - - // location of the pip install script - const string pipScript = "https://raw.github.com/pypa/pip/master/contrib/get-pip.py"; - PipPath = Path.GetTempPath() + @"get-pip.py"; - - using (var webClient = TestPipDownloadClient ?? new MultiFileAsynchronousDownloadClient(longWaitBroker, 2)) - { - Exception error; - if (!webClient.DownloadFileAsync(new Uri(setupToolsScript), SetupToolsPath, out error) || - !webClient.DownloadFileAsync(new Uri(pipScript), PipPath, out error)) - { - throw new ToolExecutionException(Resources.PythonInstaller_DownloadPip_Download_failed__Check_your_network_connection_or_contact_Skyline_developers_, error); - } - } - } - - private void InstallPip() - { - var argumentBuilder = new StringBuilder(); - string pythonPath = PythonUtil.GetProgramPath(_version); - argumentBuilder.Append(pythonPath) - .Append(TextUtil.SEPARATOR_SPACE) - .Append(SetupToolsPath) - .Append(@" & ") - .Append(pythonPath) - .Append(TextUtil.SEPARATOR_SPACE) - .Append(PipPath); - - var pipedProcessRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); - try - { - if (pipedProcessRunner.RunProcess(argumentBuilder.ToString(), false, _writer) != 0) - throw new ToolExecutionException(Resources.PythonInstaller_InstallPip_Pip_installation_failed__Error_log_output_in_immediate_window__); - } - catch (IOException) - { - throw new ToolExecutionException(Resources.PythonInstaller_InstallPip_Unknown_error_installing_pip_); - } - } - - #region Functional testing support - - public IAsynchronousDownloadClient TestDownloadClient { get; set; } - public IRunProcess TestRunProcess { get; set; } - public ISkylineProcessRunnerWrapper TestSkylineProcessRunner { get; set; } - public IAsynchronousDownloadClient TestPipDownloadClient { get; set; } - public ISkylineProcessRunnerWrapper TestPipeSkylineProcessRunner { get; set; } - public IRunProcess TestPipRunProcess { get; set; } - public bool TestingPip { get; set; } - - public string Message - { - get { return labelMessage.Text; } - } - - public int PackagesListCount - { - get { return clboxPackages.Items.Count; } - } - - public int CheckedPackagesCount - { - get { return clboxPackages.CheckedItems.Count; } - } - - public void UncheckAllPackages() - { - foreach (int packageIndex in clboxPackages.CheckedIndices) - { - clboxPackages.SetItemCheckState(packageIndex, CheckState.Unchecked); - } - } - - #endregion - } - - public static class PythonUtil - { - /// - /// A utility function to check and see if the specified version of Python is installed - /// on the user's machine - /// - /// The version to check - /// True if the specified version is installed, otherwise false - public static bool CheckInstalled(string version) - { - return GetPythonKey(version) != null; - } - - /// - /// A utility function to find the program path of the .exe for specified version of Python - /// - /// The version to find - /// The install path. If the version is not installed, or the install path cannot be found, - /// returns null. - public static string GetProgramPath(string version) - { - RegistryKey pythonKey = GetPythonKey(version); - return (pythonKey != null) ? pythonKey.GetValue(null) + (@"python.exe") : null; - } - - /// - /// A utility function to find the pip (Python package installer) executable - /// - /// The version of Python pip is associated with - /// The program path of pip.exe if it exists, otherwise null - public static string GetPipPath(string version) - { - RegistryKey pythonKey = GetPythonKey(version); - if (pythonKey != null) - { - // ReSharper disable LocalizableElement - string path = pythonKey.GetValue(null) + "scripts\\pip.exe"; - // ReSharper restore LocalizableElement - return File.Exists(path) ? path : null; - } - return null; - } - - private const string PYTHON_X64_LOCATION = @"SOFTWARE\Wow6432Node\Python\PythonCore\"; - private const string PYTHON_X86_LOCATION = @"SOFTWARE\Python\PythonCore\"; - private const string INSTALL_DIR = "\\InstallPath"; - - // Returns the registry key where the specified version of Python is installed. It does so by opening the - // registry folder \Python\PythonCore\\InstallPath - // - // When Python is uninstalled, it leaves a registry folder. So if we uninstalled Python 2.7, we would - // still find the folder \Python\PythonCore\2.7\ . Thus we have to see if the install directory folder - // remains in order to check installation; documented here: http://bugs.python.org/issue3778 - private static RegistryKey GetPythonKey(string version) - { - RegistryKey pythonKey = - (Registry.LocalMachine.OpenSubKey(PYTHON_X64_LOCATION + FormatVersion(version) + INSTALL_DIR)) - ?? - (Registry.LocalMachine.OpenSubKey(PYTHON_X86_LOCATION + FormatVersion(version) + INSTALL_DIR)) - ?? - (Registry.CurrentUser.OpenSubKey(PYTHON_X86_LOCATION + FormatVersion(version) + INSTALL_DIR)); - - return pythonKey; - } - - // Python stores version info in the registry using only the first two numbers of the version. For example, Python 2.7.5 - // is stored as Python\PythonCore\2.7\ So for checking installation, and the program path, we need to format the version string - // to look for the base version - private static string FormatVersion(string version) - { - Match versionBase = Regex.Match(version, @"(^[0-9]+\.[0-9]+).*"); - return versionBase.Groups[1].ToString(); - } - } -} +/* + * Original author: Trevor Killeen , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2013 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using Microsoft.Win32; +using pwiz.Common.SystemUtil; +using pwiz.Skyline.Alerts; +using pwiz.Skyline.Controls; +using pwiz.Skyline.Model.Tools; +using pwiz.Skyline.Properties; +using pwiz.Skyline.Util; +using pwiz.Skyline.Util.Extensions; + +namespace pwiz.Skyline.ToolsUI +{ + public partial class PythonInstallerLegacyDlg : FormEx + { + private readonly string _version; + private readonly bool _installed; + private readonly TextWriter _writer; + + private IList PackageUris { get; set; } + private IList LocalPackages { get; set; } + + public PythonInstallerLegacyDlg(ProgramPathContainer pythonPathContainer, IEnumerable packages, TextWriter writer) + : this(pythonPathContainer, packages, PythonUtil.CheckInstalled(pythonPathContainer.ProgramVersion), writer) + { + } + + public PythonInstallerLegacyDlg(ProgramPathContainer pythonPathContainer, IEnumerable packages, bool installed, TextWriter writer) + { + _version = pythonPathContainer.ProgramVersion; + _installed = installed; + _writer = writer; + AssignPackages(packages); + InitializeComponent(); + } + + private void AssignPackages(IEnumerable packages) + { + PackageUris = new List(); + LocalPackages = new List(); + foreach (var package in packages) + { + if (package.StartsWith(@"http")) + PackageUris.Add(package); + else + LocalPackages.Add(package); + } + } + + public bool IsLoaded { get; private set; } + + private void PythonInstaller_Load(object sender, EventArgs e) + { + if (!_installed && (PackageUris.Count + LocalPackages.Count) != 0) + { + labelMessage.Text = string.Format(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_Python__0__and_the_following_packages__Select_packages_to_install_and_then_click_Install_to_begin_the_installation_process_, _version); + PopulatePackageCheckListBox(); + } else if (!_installed) + { + labelMessage.Text = string.Format(Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_Python__0___Click_install_to_begin_the_installation_process_, _version); + int shift = btnCancel.Top - clboxPackages.Top; + clboxPackages.Visible = clboxPackages.Enabled = false; + Height -= shift; + } + else if ((PackageUris.Count + LocalPackages.Count) != 0) + { + labelMessage.Text = Resources.PythonInstaller_PythonInstaller_Load_This_tool_requires_the_following_Python_packages__Select_packages_to_install_and_then_click_Install_to_begin_the_installation_process_; + PopulatePackageCheckListBox(); + } + IsLoaded = true; + } + + private void PopulatePackageCheckListBox() + { + clboxPackages.DataSource = IsolatePackageNames(PackageUris).Concat(IsolatePackageNames(LocalPackages)).ToList(); + + // initially set them as checked + for (int i = 0; i < clboxPackages.Items.Count; i++) + { + clboxPackages.SetItemChecked(i, true); + } + } + + private static IEnumerable IsolatePackageNames(IEnumerable packages) + { + ICollection packageNames = new Collection(); + const string pattern = @"([^/\\]*)\.(zip|tar\.gz|exe)$"; + foreach (var package in packages) + { + Match name = Regex.Match(package, pattern); + packageNames.Add(name.Groups[1].ToString()); + } + return packageNames; + } + + private void btnInstall_Click(object sender, EventArgs e) + { + OkDialog(); + } + + public void OkDialog() + { + Enabled = false; + + if ((_installed || GetPython()) && (clboxPackages.CheckedIndices.Count == 0 || GetPackages())) + { + DialogResult = DialogResult.OK; + } + else + { + DialogResult = DialogResult.Cancel; + } + } + + private bool GetPython() + { + try + { + using (var waitDlg = new LongWaitDlg()) + { + waitDlg.ProgressValue = 0; + // Short wait, because this can't possible happen fast enough to avoid + // showing progress, except in testing + waitDlg.PerformWork(this, 50, DownloadPython); + } + using (var waitDlg = new LongWaitDlg(null, false)) + { + waitDlg.Message = ToolsResources.PythonInstaller_GetPython_Installing_Python; + waitDlg.PerformWork(this, 50, InstallPython); + } + MessageDlg.Show(this, Resources.PythonInstaller_GetPython_Python_installation_completed_); + return true; + } + catch (Exception ex) + { + MessageDlg.ShowWithException(this, (ex.InnerException ?? ex).Message, ex); + } + return false; + } + + private string DownloadPath { get; set; } + + private void DownloadPython(IProgressMonitor waitBroker) + { + // the base Url for python releases + const string baseUri = "http://python.org/ftp/python/"; + + // the installer file name, e.g. python-2.7.msi + string fileName = @"python-" + _version + @".msi"; + + // the fully formed Uri, e.g. http://python.org/ftp/python/2.7/python-2.7.msi + var downloadUri = new Uri(baseUri + _version + @"/" + fileName); + + using (var webClient = TestDownloadClient ?? new MultiFileAsynchronousDownloadClient(waitBroker, 1)) + { + if (!webClient.DownloadFileAsync(downloadUri, DownloadPath = Path.GetTempPath() + fileName, out var downloadException)) + throw new ToolExecutionException(TextUtil.LineSeparate( + Resources.PythonInstaller_DownloadPython_Download_failed_, + Resources.PythonInstaller_DownloadPython_Check_your_network_connection_or_contact_the_tool_provider_for_installation_support_), downloadException); + } + } + + private void InstallPython() + { + var processRunner = TestRunProcess ?? new SynchronousRunProcess(); + var startInfo = new ProcessStartInfo + { + FileName = @"msiexec", + // ReSharper disable LocalizableElement + Arguments = "/i \"" + DownloadPath + "\"", + // ReSharper restore LocalizableElement + }; + if (processRunner.RunProcess(new Process {StartInfo = startInfo}) != 0) + throw new ToolExecutionException(Resources.PythonInstaller_InstallPython_Python_installation_failed__Canceling_tool_installation_); + } + + private bool GetPackages() + { + ICollection downloadablePackages = new Collection(); + ICollection localPackages = new Collection(); + AssignPackagesToInstall(ref downloadablePackages, ref localPackages); + + IEnumerable packagePaths = null; + try + { + // download packages + using (var waitDlg = new LongWaitDlg()) + { + waitDlg.ProgressValue = 0; + waitDlg.PerformWork(this, 500, longWaitBroker => packagePaths = DownloadPackages(longWaitBroker, downloadablePackages)); + } + + // separate packages + ICollection exePaths = new Collection(); + ICollection sourcePaths = new Collection(); + foreach (var package in (packagePaths == null) ? localPackages : packagePaths.Concat(localPackages)) + { + if (package.EndsWith(@".exe")) + exePaths.Add(package); + else + sourcePaths.Add(package); + } + + // first install executable packages, if any + if (exePaths.Count != 0) + { + using (var waitDlg = new LongWaitDlg(null, false)) + { + waitDlg.Message = ToolsResources.PythonInstaller_GetPackages_Installing_Packages; + waitDlg.PerformWork(this, 500, () => InstallExecutablePackages(exePaths)); + } + } + + // then install source paths, if any + if (sourcePaths.Count != 0) + { + // try and find the path to the pip package manager .exe + string pipPath = PythonUtil.GetPipPath(_version); + + // if it can't be found, install it + if (pipPath == null || TestingPip) + { + DialogResult result = MultiButtonMsgDlg.Show( + this, + Resources.PythonInstaller_InstallPackages_Skyline_uses_the_Python_tool_setuptools_and_the_Python_package_manager_Pip_to_install_packages_from_source__Click_install_to_begin_the_installation_process_, + ToolsResources.PythonInstaller_InstallPackages_Install); + if (result == DialogResult.OK && GetPip()) + { + pipPath = PythonUtil.GetPipPath(_version); + MessageDlg.Show(this, Resources.PythonInstaller_InstallPackages_Pip_installation_complete_); + } + else + { + MessageDlg.Show(this, Resources.PythonInstaller_InstallPackages_Python_package_installation_cannot_continue__Canceling_tool_installation_); + return false; + } + } + + using (var waitDlg = new LongWaitDlg(null, false)) + { + waitDlg.Message = ToolsResources.PythonInstaller_GetPackages_Installing_Packages; + waitDlg.PerformWork(this, 500, () => InstallSourcePackages(sourcePaths, pipPath)); + } + } + MessageDlg.Show(this, Resources.PythonInstaller_GetPackages_Package_installation_completed_); + return true; + } + catch (TargetInvocationException ex) + { + if (ex.InnerException is ToolExecutionException) + { + MessageDlg.ShowException(this, ex); + return false; + } + throw; + } + } + + private void AssignPackagesToInstall(ref ICollection downloadableFiles, ref ICollection localFiles) + { + foreach (int index in clboxPackages.CheckedIndices) + { + if (index < PackageUris.Count) + { + downloadableFiles.Add(PackageUris[index]); + } + else + { + localFiles.Add(LocalPackages[index - PackageUris.Count]); + } + } + } + + private IEnumerable DownloadPackages(IProgressMonitor waitBroker, IEnumerable packagesToDownload) + { + ICollection downloadPaths = new Collection(); + ICollection failedDownloads = new Collection(); + List downloadExceptions = new List(); + + using (var webClient = TestDownloadClient ?? new MultiFileAsynchronousDownloadClient(waitBroker, PackageUris.Count)) + { + foreach (var package in packagesToDownload) + { + Match file = Regex.Match(package, @"[^/]*$"); + string downloadPath = Path.GetTempPath() + file; + if (webClient.DownloadFileAsync(new Uri(package), downloadPath, out var downloadException)) + { + downloadPaths.Add(downloadPath); + } + else + { + failedDownloads.Add(package); + if (downloadException != null) + { + downloadExceptions.Add(downloadException); + } + } + } + } + + if (failedDownloads.Count != 0) + { + Exception cause; + switch (downloadExceptions.Count) + { + case 0: + cause = null; + break; + case 1: + cause = downloadExceptions[0]; + break; + default: + cause = new AggregateException(downloadExceptions); + break; + } + throw new ToolExecutionException( + TextUtil.LineSeparate( + Resources.PythonInstaller_DownloadPackages_Failed_to_download_the_following_packages_, + string.Empty, + TextUtil.LineSeparate(failedDownloads), + string.Empty, + Resources.PythonInstaller_DownloadPython_Check_your_network_connection_or_contact_the_tool_provider_for_installation_support_), cause); + } + return downloadPaths; + } + + public void InstallExecutablePackages(IEnumerable packages) + { + foreach (var package in packages) + { + var processRunner = TestRunProcess ?? new SynchronousRunProcess(); + if (processRunner.RunProcess(new Process { StartInfo = new ProcessStartInfo(package) }) != 0) + { + throw new ToolExecutionException(Resources.PythonInstaller_InstallPackages_Package_Installation_was_not_completed__Canceling_tool_installation_); + } + } + } + + private void InstallSourcePackages(ICollection packages, string pipPath) + { + // then install packages from source + if (packages.Count != 0) + { + if (!File.Exists(pipPath) && !TestingPip) + throw new ToolExecutionException(Resources.PythonInstaller_InstallPackages_Unknown_error_installing_packages_); + + var argumentBuilder = new StringBuilder(@"echo installing packages"); + foreach (var package in packages) + { + // ReSharper disable LocalizableElement + argumentBuilder.Append(" & ") + .Append(pipPath) + .Append(" install ") + .Append("\"") + .Append(package) + .Append("\""); + // ReSharper restore LocalizableElement + } + + var pipedProcessRunner = TestSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + try + { + if (pipedProcessRunner.RunProcess(argumentBuilder.ToString(), false, _writer) != 0) + throw new ToolExecutionException(Resources.PythonInstaller_InstallPackages_Package_installation_failed__Error_log_output_in_immediate_window_); + } + catch (IOException) + { + throw new ToolExecutionException(Resources.PythonInstaller_InstallPackages_Unknown_error_installing_packages_); + } + } + } + + private bool GetPip() + { + try + { + using (var dlg = new LongWaitDlg()) + { + dlg.ProgressValue = 0; + // Short wait, because this can't possible happen fast enough to avoid + // showing progress, except in testing + dlg.PerformWork(this, 50, DownloadPip); + } + using (var dlg = new LongWaitDlg(null, false)) + { + dlg.Message = ToolsResources.PythonInstaller_GetPip_Installing_Pip; + dlg.PerformWork(this, 50, InstallPip); + } + } + catch (TargetInvocationException ex) + { + if (ex.InnerException is ToolExecutionException) + { + MessageDlg.ShowException(this, ex); + return false; + } + throw; + } + return true; + } + + private string SetupToolsPath { get; set; } + private string PipPath { get; set; } + + // Consider: the location of the following python links is assumed to be relatively stable, but could change. We + // might want to package these scripts with Skyline itself to assure that they are available + + private void DownloadPip(IProgressMonitor longWaitBroker) + { + // location of the setuptools install script + const string setupToolsScript = "https://bitbucket.org/pypa/setuptools/downloads/ez_setup.py"; + SetupToolsPath = Path.GetTempPath() + @"ez_setup.py"; + + // location of the pip install script + const string pipScript = "https://raw.github.com/pypa/pip/master/contrib/get-pip.py"; + PipPath = Path.GetTempPath() + @"get-pip.py"; + + using (var webClient = TestPipDownloadClient ?? new MultiFileAsynchronousDownloadClient(longWaitBroker, 2)) + { + Exception error; + if (!webClient.DownloadFileAsync(new Uri(setupToolsScript), SetupToolsPath, out error) || + !webClient.DownloadFileAsync(new Uri(pipScript), PipPath, out error)) + { + throw new ToolExecutionException(Resources.PythonInstaller_DownloadPip_Download_failed__Check_your_network_connection_or_contact_Skyline_developers_, error); + } + } + } + + private void InstallPip() + { + var argumentBuilder = new StringBuilder(); + string pythonPath = PythonUtil.GetProgramPath(_version); + argumentBuilder.Append(pythonPath) + .Append(TextUtil.SEPARATOR_SPACE) + .Append(SetupToolsPath) + .Append(@" & ") + .Append(pythonPath) + .Append(TextUtil.SEPARATOR_SPACE) + .Append(PipPath); + + var pipedProcessRunner = TestPipeSkylineProcessRunner ?? new SkylineProcessRunnerWrapper(); + try + { + if (pipedProcessRunner.RunProcess(argumentBuilder.ToString(), false, _writer) != 0) + throw new ToolExecutionException(Resources.PythonInstaller_InstallPip_Pip_installation_failed__Error_log_output_in_immediate_window__); + } + catch (IOException) + { + throw new ToolExecutionException(Resources.PythonInstaller_InstallPip_Unknown_error_installing_pip_); + } + } + + #region Functional testing support + + public IAsynchronousDownloadClient TestDownloadClient { get; set; } + public IRunProcess TestRunProcess { get; set; } + public ISkylineProcessRunnerWrapper TestSkylineProcessRunner { get; set; } + public IAsynchronousDownloadClient TestPipDownloadClient { get; set; } + public ISkylineProcessRunnerWrapper TestPipeSkylineProcessRunner { get; set; } + public IRunProcess TestPipRunProcess { get; set; } + public bool TestingPip { get; set; } + + public string Message + { + get { return labelMessage.Text; } + } + + public int PackagesListCount + { + get { return clboxPackages.Items.Count; } + } + + public int CheckedPackagesCount + { + get { return clboxPackages.CheckedItems.Count; } + } + + public void UncheckAllPackages() + { + foreach (int packageIndex in clboxPackages.CheckedIndices) + { + clboxPackages.SetItemCheckState(packageIndex, CheckState.Unchecked); + } + } + + #endregion + } + + public static class PythonUtil + { + /// + /// A utility function to check and see if the specified version of Python is installed + /// on the user's machine + /// + /// The version to check + /// True if the specified version is installed, otherwise false + public static bool CheckInstalled(string version) + { + return GetPythonKey(version) != null; + } + + /// + /// A utility function to find the program path of the .exe for specified version of Python + /// + /// The version to find + /// The install path. If the version is not installed, or the install path cannot be found, + /// returns null. + public static string GetProgramPath(string version) + { + RegistryKey pythonKey = GetPythonKey(version); + return (pythonKey != null) ? pythonKey.GetValue(null) + (@"python.exe") : null; + } + + /// + /// A utility function to find the pip (Python package installer) executable + /// + /// The version of Python pip is associated with + /// The program path of pip.exe if it exists, otherwise null + public static string GetPipPath(string version) + { + RegistryKey pythonKey = GetPythonKey(version); + if (pythonKey != null) + { + // ReSharper disable LocalizableElement + string path = pythonKey.GetValue(null) + "scripts\\pip.exe"; + // ReSharper restore LocalizableElement + return File.Exists(path) ? path : null; + } + return null; + } + + private const string PYTHON_X64_LOCATION = @"SOFTWARE\Wow6432Node\Python\PythonCore\"; + private const string PYTHON_X86_LOCATION = @"SOFTWARE\Python\PythonCore\"; + private const string INSTALL_DIR = "\\InstallPath"; + + // Returns the registry key where the specified version of Python is installed. It does so by opening the + // registry folder \Python\PythonCore\\InstallPath + // + // When Python is uninstalled, it leaves a registry folder. So if we uninstalled Python 2.7, we would + // still find the folder \Python\PythonCore\2.7\ . Thus we have to see if the install directory folder + // remains in order to check installation; documented here: http://bugs.python.org/issue3778 + private static RegistryKey GetPythonKey(string version) + { + RegistryKey pythonKey = + (Registry.LocalMachine.OpenSubKey(PYTHON_X64_LOCATION + FormatVersion(version) + INSTALL_DIR)) + ?? + (Registry.LocalMachine.OpenSubKey(PYTHON_X86_LOCATION + FormatVersion(version) + INSTALL_DIR)) + ?? + (Registry.CurrentUser.OpenSubKey(PYTHON_X86_LOCATION + FormatVersion(version) + INSTALL_DIR)); + + return pythonKey; + } + + // Python stores version info in the registry using only the first two numbers of the version. For example, Python 2.7.5 + // is stored as Python\PythonCore\2.7\ So for checking installation, and the program path, we need to format the version string + // to look for the base version + private static string FormatVersion(string version) + { + Match versionBase = Regex.Match(version, @"(^[0-9]+\.[0-9]+).*"); + return versionBase.Groups[1].ToString(); + } + } +} diff --git a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.ja.resx b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.ja.resx similarity index 97% rename from pwiz_tools/Skyline/ToolsUI/PythonInstaller.ja.resx rename to pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.ja.resx index 79a8faa312..36b0bb305d 100644 --- a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.ja.resx +++ b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.ja.resx @@ -1,249 +1,249 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - Bottom, Right - - - - 146, 287 - - - 75, 23 - - - - 0 - - - インストール - - - btnInstall - - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 3 - - - Bottom, Right - - - 227, 287 - - - 75, 23 - - - 1 - - - キャンセル - - - btnCancel - - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 2 - - - Top, Left, Right - - - 12, 9 - - - 290, 52 - - - 2 - - - このツールではPython<バージョン番号>と以下のパッケージを使用する必要があります。インストールするパッケージを選択し、「インストール」をクリックするとインストールが開始します。 - - - labelMessage - - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 1 - - - Top, Bottom, Left, Right - - - 12, 62 - - - 290, 184 - - - 3 - - - clboxPackages - - - System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 0 - - - True - - - 6, 13 - - - 314, 322 - - - CenterParent - - - Pythonインストーラ - - - PythonInstaller - - - pwiz.Skyline.Util.FormEx, Skyline, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Bottom, Right + + + + 146, 287 + + + 75, 23 + + + + 0 + + + インストール + + + btnInstall + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Bottom, Right + + + 227, 287 + + + 75, 23 + + + 1 + + + キャンセル + + + btnCancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Top, Left, Right + + + 12, 9 + + + 290, 52 + + + 2 + + + このツールではPython<バージョン番号>と以下のパッケージを使用する必要があります。インストールするパッケージを選択し、「インストール」をクリックするとインストールが開始します。 + + + labelMessage + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Top, Bottom, Left, Right + + + 12, 62 + + + 290, 184 + + + 3 + + + clboxPackages + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 314, 322 + + + CenterParent + + + Pythonインストーラ + + + PythonInstallerLegacyDlg + + + pwiz.Skyline.Util.FormEx, Skyline, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + \ No newline at end of file diff --git a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.resx b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.resx similarity index 97% rename from pwiz_tools/Skyline/ToolsUI/PythonInstaller.resx rename to pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.resx index ac9cb471d4..ba75e0ac3a 100644 --- a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.resx +++ b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.resx @@ -1,249 +1,249 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - Bottom, Right - - - - 146, 287 - - - 75, 23 - - - - 0 - - - Install - - - btnInstall - - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 3 - - - Bottom, Right - - - 227, 287 - - - 75, 23 - - - 1 - - - Cancel - - - btnCancel - - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 2 - - - Top, Left, Right - - - 12, 9 - - - 290, 52 - - - 2 - - - This tool requires the use of Python <Version Number> and the following packages. Select packages to install and then click Install to begin the installation process. - - - labelMessage - - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 1 - - - Top, Bottom, Left, Right - - - 12, 62 - - - 290, 184 - - - 3 - - - clboxPackages - - - System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 0 - - - True - - - 6, 13 - - - 314, 322 - - - CenterParent - - - Python Installer - - - PythonInstaller - - - pwiz.Skyline.Util.FormEx, Skyline, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Bottom, Right + + + + 146, 287 + + + 75, 23 + + + + 0 + + + Install + + + btnInstall + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Bottom, Right + + + 227, 287 + + + 75, 23 + + + 1 + + + Cancel + + + btnCancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Top, Left, Right + + + 12, 9 + + + 290, 52 + + + 2 + + + This tool requires the use of Python <Version Number> and the following packages. Select packages to install and then click Install to begin the installation process. + + + labelMessage + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Top, Bottom, Left, Right + + + 12, 62 + + + 290, 184 + + + 3 + + + clboxPackages + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 314, 322 + + + CenterParent + + + Python Installer + + + PythonInstallerLegacyDlg + + + pwiz.Skyline.Util.FormEx, Skyline, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + \ No newline at end of file diff --git a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.zh-CHS.resx b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.zh-CHS.resx similarity index 97% rename from pwiz_tools/Skyline/ToolsUI/PythonInstaller.zh-CHS.resx rename to pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.zh-CHS.resx index 0da49af3b5..8eb50921ab 100644 --- a/pwiz_tools/Skyline/ToolsUI/PythonInstaller.zh-CHS.resx +++ b/pwiz_tools/Skyline/ToolsUI/PythonInstallerLegacyDlg.zh-CHS.resx @@ -1,249 +1,249 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - Bottom, Right - - - - 146, 287 - - - 75, 23 - - - - 0 - - - 安装 - - - btnInstall - - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 3 - - - Bottom, Right - - - 227, 287 - - - 75, 23 - - - 1 - - - 取消 - - - btnCancel - - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 2 - - - Top, Left, Right - - - 12, 9 - - - 290, 52 - - - 2 - - - 此工具需要使用 Python <版本号> 和下列软件包。选择将要安装的软件包,然后单击“安装”以开始安装过程。 - - - labelMessage - - - System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 1 - - - Top, Bottom, Left, Right - - - 12, 62 - - - 290, 184 - - - 3 - - - clboxPackages - - - System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 0 - - - True - - - 6, 13 - - - 314, 322 - - - CenterParent - - - Python 安装程序 - - - PythonInstaller - - - pwiz.Skyline.Util.FormEx, Skyline, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Bottom, Right + + + + 146, 287 + + + 75, 23 + + + + 0 + + + 安装 + + + btnInstall + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + Bottom, Right + + + 227, 287 + + + 75, 23 + + + 1 + + + 取消 + + + btnCancel + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + Top, Left, Right + + + 12, 9 + + + 290, 52 + + + 2 + + + 此工具需要使用 Python <版本号> 和下列软件包。选择将要安装的软件包,然后单击“安装”以开始安装过程。 + + + labelMessage + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + Top, Bottom, Left, Right + + + 12, 62 + + + 290, 184 + + + 3 + + + clboxPackages + + + System.Windows.Forms.CheckedListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 314, 322 + + + CenterParent + + + Python 安装程序 + + + PythonInstallerLegacyDlg + + + pwiz.Skyline.Util.FormEx, Skyline, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + \ No newline at end of file diff --git a/pwiz_tools/Skyline/ToolsUI/PythonInstallerUI.cs b/pwiz_tools/Skyline/ToolsUI/PythonInstallerUI.cs new file mode 100644 index 0000000000..8e75eedab4 --- /dev/null +++ b/pwiz_tools/Skyline/ToolsUI/PythonInstallerUI.cs @@ -0,0 +1,264 @@ +/* + * Author: David Shteynberg , + * MacCoss Lab, Department of Genome Sciences, UW + * + * Copyright 2025 University of Washington - Seattle, WA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using pwiz.Skyline.Alerts; +using pwiz.Skyline.Controls; +using pwiz.Skyline.Model.Tools; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Windows.Forms; +using pwiz.Common.Collections; +using pwiz.Skyline.Model; + +namespace pwiz.Skyline.ToolsUI +{ + + public static class PythonInstallerUI + { + private static string _userAnswerToCuda; + public static MultiButtonMsgDlg EnableNvidiaGpuDlg { get; set; } + + private static IList _tasks; + public static DialogResult InstallPythonVirtualEnvironment(Control parent, PythonInstaller pythonInstaller) + { + var result = DialogResult.OK; + + _tasks = new List(pythonInstaller.PendingTasks.IsNullOrEmpty() + ? pythonInstaller.ValidatePythonVirtualEnvironment() + : pythonInstaller.PendingTasks); + + _userAnswerToCuda = null; + pythonInstaller.NumTotalTasks = _tasks.Count; + pythonInstaller.NumCompletedTasks = 0; + List abortedTasks = new List(); + bool abortTask = false; + foreach (var task in _tasks) + { + try + { + if (task.Name == PythonTaskName.setup_nvidia_libraries || task.Name == PythonTaskName.download_cuda_library || task.Name == PythonTaskName.install_cuda_library || + task.Name == PythonTaskName.download_cudnn_library || task.Name == PythonTaskName.install_cudnn_library) + { + if (_userAnswerToCuda != @"No") + { + var choice = DialogResult.None; + EnableNvidiaGpuDlg = new MultiButtonMsgDlg(string.Format(ToolsUIResources.PythonInstaller_Install_Cuda_Library), DialogResult.Yes.ToString(), DialogResult.No.ToString(), true); + + choice = EnableNvidiaGpuDlg.ShowDialog(); + if (choice == DialogResult.No) + { + _userAnswerToCuda = @"No"; + if (pythonInstaller.NumTotalTasks > 0) pythonInstaller.NumTotalTasks--; + abortTask = true; + } + else if (choice == DialogResult.Cancel) + { + if (!EnableNvidiaGpuDlg.IsDisposed) EnableNvidiaGpuDlg.Dispose(); + if (pythonInstaller.NumTotalTasks > 0) pythonInstaller.NumTotalTasks--; + return choice; + } + else if (choice == DialogResult.Yes) + { + _userAnswerToCuda = @"Yes"; + pythonInstaller.WriteInstallNvidiaBatScript(); + AlertDlg adminMessageDlg = + new AlertDlg( + string.Format(ModelResources.NvidiaInstaller_Requesting_Administrator_elevation, + PythonInstaller.InstallNvidiaLibrariesBat), MessageBoxButtons.OKCancel); + + //if (!PythonInstaller.IsRunningElevated()) + // adminMessageDlg.FindButton(DialogResult.OK).Enabled = false; + + var nvidiaChoice = adminMessageDlg.ShowDialog(); + //Download + if (nvidiaChoice == DialogResult.Cancel) + { + _userAnswerToCuda = @"Cancel"; + if (!adminMessageDlg.IsDisposed) adminMessageDlg.Dispose(); + if (!EnableNvidiaGpuDlg.IsDisposed) EnableNvidiaGpuDlg.Dispose(); + if (pythonInstaller.NumTotalTasks > 0) pythonInstaller.NumTotalTasks--; + return nvidiaChoice; + } + else if (nvidiaChoice == DialogResult.OK) + { + // Attempt to run + abortTask = !PerformTaskAction(parent, task); + } + if (!adminMessageDlg.IsDisposed) adminMessageDlg.Dispose(); + } + if (!EnableNvidiaGpuDlg.IsDisposed) EnableNvidiaGpuDlg.Dispose(); + + } + else if (_userAnswerToCuda == @"No") + { + if (pythonInstaller.NumTotalTasks > 0) pythonInstaller.NumTotalTasks--; + abortTask = true; + } + else + { + abortTask = !PerformTaskAction(parent, task); + } + } + else if (task.Name == PythonTaskName.enable_longpaths) + { + AlertDlg adminMessageDlg = + new AlertDlg(string.Format(ToolsUIResources.PythonInstaller_Requesting_Administrator_elevation), MessageBoxButtons.OKCancel); + var choice = adminMessageDlg.ShowDialog(); + if (choice == DialogResult.Cancel) + { + if (!adminMessageDlg.IsDisposed) adminMessageDlg.Dispose(); + if (pythonInstaller.NumTotalTasks > 0) pythonInstaller.NumTotalTasks--; + return choice; + } + else if (choice == DialogResult.OK) + { + // Attempt to enable Windows Long Paths + pythonInstaller.EnableWindowsLongPaths(); + } + if (!adminMessageDlg.IsDisposed) adminMessageDlg.Dispose(); + } + else if (task.IsAction) + { + abortTask = !PerformTaskAction(parent,task); + } + else + { + throw new PythonInstallerUnsupportedTaskException(task); + } + if (!abortTask) + { + pythonInstaller.NumCompletedTasks++; + } + else + { + abortedTasks.Add(task); + return DialogResult.Cancel; + } + } + catch (Exception ex) + { + MessageDlg.ShowWithException(parent, (ex.InnerException ?? ex).Message, ex); + return DialogResult.Cancel; + } + } + Debug.WriteLine($@"total: {pythonInstaller.NumTotalTasks}, completed: {pythonInstaller.NumCompletedTasks}"); + if (_resultAlertDlg != null && ! _resultAlertDlg.IsDisposed) _resultAlertDlg.Dispose(); + + pythonInstaller.CheckPendingTasks(); + + if (pythonInstaller.HavePythonTasks) + { + if (pythonInstaller.IsPythonVirtualEnvironmentReady(abortedTasks)) + { + if (pythonInstaller.NumTotalTasks == pythonInstaller.NumCompletedTasks && + pythonInstaller.NumCompletedTasks > 0) + { + _resultAlertDlg = + new AlertDlg( + ToolsUIResources + .PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment, + MessageBoxButtons.OK); + _resultAlertDlg.ShowDialog(); + } + + pythonInstaller.PendingTasks.Clear(); + result = DialogResult.OK; + } + else if (!pythonInstaller.IsPythonVirtualEnvironmentReady()) + { + _resultAlertDlg = + new AlertDlg( + ToolsUIResources.PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment, + MessageBoxButtons.OK); + _resultAlertDlg.ShowDialog(); + result = DialogResult.Cancel; + } + else + { + result = DialogResult.OK; + } + } + + if (pythonInstaller.HaveNvidiaTasks) + { + if (PythonInstaller.TestForNvidiaGPU() == true) + { + if (_userAnswerToCuda == @"Yes") + { + if (pythonInstaller.IsNvidiaEnvironmentReady(abortedTasks)) + { + _resultAlertDlg = + new AlertDlg( + ToolsUIResources.NvidiaInstaller_OkDialog_Successfully_set_up_Nvidia, + MessageBoxButtons.OK); + _resultAlertDlg.ShowDialog(); + pythonInstaller.PendingTasks.Clear(); + result = DialogResult.OK; + } + else + { + _resultAlertDlg = + new AlertDlg( + ToolsUIResources.NvidiaInstaller_OkDialog_Failed_to_set_up_Nvidia, + MessageBoxButtons.OK); + _resultAlertDlg.ShowDialog(); + } + } + } + } + + return result; + } + public static void Dispose() + { + if (_resultAlertDlg != null && !_resultAlertDlg.IsDisposed) + { + _resultAlertDlg.Dispose(); + } + } + private static AlertDlg _resultAlertDlg; + private static bool PerformTaskAction(Control parent, PythonTask task, int startProgress = 0) + { + //IProgressStatus proStatus = null; + using var waitDlg = new LongWaitDlg(); + if (task.IsActionWithNoArg) + { + waitDlg.Message = task.InProgressMessage; + waitDlg.PerformWork(parent, 50, task.AsActionWithNoArg); + } + else if (task.IsActionWithProgressMonitor) + { + waitDlg.Message = task.InProgressMessage; + waitDlg.ProgressValue = startProgress; + waitDlg.PerformWork(parent, 50, task.AsActionWithProgressMonitor); + } + else + { + waitDlg.Message = task.InProgressMessage; + waitDlg.PerformWork(parent, 50, task.AsActionWithLongWaitBroker); + } + + //if (proStatus != null && proStatus.IsCanceled) + // return false; + + return !waitDlg.IsCanceled; + } + } +} diff --git a/pwiz_tools/Skyline/ToolsUI/ToolsUIResources.designer.cs b/pwiz_tools/Skyline/ToolsUI/ToolsUIResources.designer.cs index 435ca0912d..395b27601b 100644 --- a/pwiz_tools/Skyline/ToolsUI/ToolsUIResources.designer.cs +++ b/pwiz_tools/Skyline/ToolsUI/ToolsUIResources.designer.cs @@ -537,38 +537,102 @@ public static string LibrarySpec_CreateFromPath_Unrecognized_library_type_at__0_ } /// - /// Looks up a localized string similar to Installing Packages. + /// Looks up a localized string similar to Failed to set up Nvidia libraries. /// - public static string PythonInstaller_GetPackages_Installing_Packages { + public static string NvidiaInstaller_OkDialog_Failed_to_set_up_Nvidia { get { - return ResourceManager.GetString("PythonInstaller_GetPackages_Installing_Packages", resourceCulture); + return ResourceManager.GetString("NvidiaInstaller_OkDialog_Failed_to_set_up_Nvidia", resourceCulture); } } /// - /// Looks up a localized string similar to Installing Pip. + /// Looks up a localized string similar to Successfully set up Nvidia libraries. /// - public static string PythonInstaller_GetPip_Installing_Pip { + public static string NvidiaInstaller_OkDialog_Successfully_set_up_Nvidia { get { - return ResourceManager.GetString("PythonInstaller_GetPip_Installing_Pip", resourceCulture); + return ResourceManager.GetString("NvidiaInstaller_OkDialog_Successfully_set_up_Nvidia", resourceCulture); } } /// - /// Looks up a localized string similar to Installing Python. + /// Looks up a localized string similar to Setting up Nvidia GPU Computation. /// - public static string PythonInstaller_GetPython_Installing_Python { + public static string NvidiaInstaller_Setup_Nvidia_Libraries { get { - return ResourceManager.GetString("PythonInstaller_GetPython_Installing_Python", resourceCulture); + return ResourceManager.GetString("NvidiaInstaller_Setup_Nvidia_Libraries", resourceCulture); } } /// - /// Looks up a localized string similar to Install. + /// Looks up a localized string similar to Python {0} set up is required for {1}, click 'OK' to proceed.. + /// + public static string PythonInstaller_BuildPrecursorTable_Python_0_installation_is_required { + get { + return ResourceManager.GetString("PythonInstaller_BuildPrecursorTable_Python_0_installation_is_required", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Python {0} set up is required for {1}, please wait for python to install and configure .... + /// + public static string PythonInstaller_BuildPrecursorTable_Python_0_installation_is_required_please_wait { + get { + return ResourceManager.GetString("PythonInstaller_BuildPrecursorTable_Python_0_installation_is_required_please_wait" + + "", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading Cuda software library.... /// - public static string PythonInstaller_InstallPackages_Install { + public static string PythonInstaller_Downloading_Cuda_Library { get { - return ResourceManager.GetString("PythonInstaller_InstallPackages_Install", resourceCulture); + return ResourceManager.GetString("PythonInstaller_Downloading_Cuda_Library", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Skyline detected an Nvidia GPU on this computer. Would you like to enable faster AI processing by setting up Nvidia GPU computation? Elevated privileges are required to install this feature.. + /// + public static string PythonInstaller_Install_Cuda_Library { + get { + return ResourceManager.GetString("PythonInstaller_Install_Cuda_Library", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Installing Cuda software library.... + /// + public static string PythonInstaller_Installing_Cuda_Library { + get { + return ResourceManager.GetString("PythonInstaller_Installing_Cuda_Library", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to set up Python virtual environment. + /// + public static string PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment { + get { + return ResourceManager.GetString("PythonInstaller_OkDialog_Failed_to_set_up_Python_virtual_environment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Successfully set up Python virtual environment. + /// + public static string PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment { + get { + return ResourceManager.GetString("PythonInstaller_OkDialog_Successfully_set_up_Python_virtual_environment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Administrative privileges are required to complete the installation, consult with your system administrator if necessary. Would you like to continue?. + /// + public static string PythonInstaller_Requesting_Administrator_elevation { + get { + return ResourceManager.GetString("PythonInstaller_Requesting_Administrator_elevation", resourceCulture); } } @@ -617,6 +681,15 @@ public static string RInstaller_InstallR_Downloading_R { } } + /// + /// Looks up a localized string similar to SkylineProcessRunner timed-out after {0} milliseconds. + /// + public static string SkylineProcessRunner_Timeout { + get { + return ResourceManager.GetString("SkylineProcessRunner_Timeout", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot find a file with that identifier.. /// diff --git a/pwiz_tools/Skyline/ToolsUI/ToolsUIResources.resx b/pwiz_tools/Skyline/ToolsUI/ToolsUIResources.resx index f07b654d9e..99814388a4 100644 --- a/pwiz_tools/Skyline/ToolsUI/ToolsUIResources.resx +++ b/pwiz_tools/Skyline/ToolsUI/ToolsUIResources.resx @@ -259,18 +259,6 @@ Unrecognized library type at {0} - - Installing Packages - - - Installing Pip - - - Installing Python - - - Install - Installing Packages @@ -356,6 +344,42 @@ Installing updates to {0} + + Successfully set up Nvidia libraries + + + Failed to set up Nvidia libraries + + + Setting up Nvidia GPU Computation + + + Successfully set up Python virtual environment + + + Failed to set up Python virtual environment + + + Python {0} set up is required for {1}, click 'OK' to proceed. + + + Python {0} set up is required for {1}, please wait for python to install and configure ... + + + Administrative privileges are required to complete the installation, consult with your system administrator if necessary. Would you like to continue? + + + Skyline detected an Nvidia GPU on this computer. Would you like to enable faster AI processing by setting up Nvidia GPU computation? Elevated privileges are required to install this feature. + + + Downloading Cuda software library... + + + Installing Cuda software library... + + + SkylineProcessRunner timed-out after {0} milliseconds + Connect diff --git a/pwiz_tools/Skyline/Util/Extensions/Text.cs b/pwiz_tools/Skyline/Util/Extensions/Text.cs index 5b53552392..64691fa2d9 100644 --- a/pwiz_tools/Skyline/Util/Extensions/Text.cs +++ b/pwiz_tools/Skyline/Util/Extensions/Text.cs @@ -35,6 +35,19 @@ namespace pwiz.Skyline.Util.Extensions /// public static class TextUtil { + public const string HYPHEN = "-"; + public const string SPACE = " "; + public const string CARET = @"^"; + public const string AMPERSAND = @"&"; + public const string EQUAL = @"="; + public const string FORWARD_SLASH = @"/"; + public const string SEMICOLON = @";"; + public const string UNDERSCORE = @"_"; + public const string LEFT_PARENTHESIS = @"("; + public const string RIGHT_PARENTHESIS = @")"; + public const string LEFT_SQUARE_BRACKET = @"["; + public const string RIGHT_SQUARE_BRACKET = @"]"; + public const string EXT_CSV = ".csv"; public const string EXT_TSV = ".tsv"; diff --git a/pwiz_tools/Skyline/Util/UtilIO.cs b/pwiz_tools/Skyline/Util/UtilIO.cs index 49a97327ab..fc84a52a51 100644 --- a/pwiz_tools/Skyline/Util/UtilIO.cs +++ b/pwiz_tools/Skyline/Util/UtilIO.cs @@ -22,6 +22,7 @@ using System.IO; using System.IO.Pipes; using System.Linq; +using System.Management; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -1016,7 +1017,23 @@ public static void SafeDelete(string path) catch (Exception) { } // ReSharper restore EmptyGeneralCatchClause } - + public static void SafeDeleteLongPath(string path) + { + try + { + string longPath = $@"\\?\{path}"; + Helpers.TryTwice(() => + { + if (path != null && Directory.Exists(path)) // Don't waste time trying to delete something that's already deleted + { + Directory.Delete(longPath, true); + } + }, $@"Directory.Delete({longPath})"); + } + // ReSharper disable EmptyGeneralCatchClause + catch (Exception) { } + // ReSharper restore EmptyGeneralCatchClause + } public static string GetUniqueName(string dirName) { return Directory.Exists(dirName) @@ -1583,30 +1600,64 @@ public bool WaitForConnection(NamedPipeServerStream serverStream, string outPipe public static class SkylineProcessRunner { + /// + /// Kill a process, and all of its children, grandchildren, etc. + /// + /// Process ID. + private static void KillProcessAndDescendants(int pid) + { + // Cannot close 'system idle process'. + if (pid == 0) + { + return; + } + ManagementObjectSearcher searcher = new ManagementObjectSearcher + (@"Select * From Win32_Process Where ParentProcessID=" + pid); + ManagementObjectCollection moc = searcher.Get(); + foreach (ManagementObject mo in moc) + { + KillProcessAndDescendants(Convert.ToInt32(mo[@"ProcessID"])); + } + try + { + Process proc = Process.GetProcessById(pid); + if (!proc.HasExited) + proc.Kill(); + } + catch (ArgumentException) + { + // Process already exited. + } + } + /// /// Runs the SkylineProcessRunner executable file with the given arguments. These arguments /// are passed to CMD.exe within the NamedPipeProcessRunner /// /// The arguments to run at the command line /// If true, this process will be run as administrator, which - /// allows for the CMD.exe process to be ran with elevated privileges + /// allows for the CMD.exe process to be ran with elevated privileges /// The textwriter to which the command lines output will be written to + /// Whether or not execution runs in its own window + /// Allows to Cancel /// The exitcode of the CMD process ran with the specified arguments - public static int RunProcess(string arguments, bool runAsAdministrator, TextWriter writer) + public static int RunProcess(string arguments, bool runAsAdministrator, TextWriter writer, bool createNoWindow = false, CancellationToken cancellationToken = default ) { // create GUID string guidSuffix = string.Format(@"-{0}", Guid.NewGuid()); var startInfo = new ProcessStartInfo - { - FileName = GetSkylineProcessRunnerExePath(), - Arguments = guidSuffix + @" " + arguments, - }; + { + CreateNoWindow = createNoWindow, + UseShellExecute = !createNoWindow, + FileName = GetSkylineProcessRunnerExePath(), + Arguments = guidSuffix + @" " + arguments, + }; if (runAsAdministrator) startInfo.Verb = @"runas"; var process = new Process {StartInfo = startInfo, EnableRaisingEvents = true}; - + int processID = -1; string pipeName = @"SkylineProcessRunnerPipe" + guidSuffix; using (var pipeStream = new NamedPipeServerStream(pipeName)) @@ -1616,6 +1667,7 @@ public static int RunProcess(string arguments, bool runAsAdministrator, TextWrit try { process.Start(); + processID = process.Id; } catch (System.ComponentModel.Win32Exception win32Exception) { @@ -1624,7 +1676,7 @@ public static int RunProcess(string arguments, bool runAsAdministrator, TextWrit // not as administrator if (runAsAdministrator && win32Exception.NativeErrorCode == ERROR_CANCELLED) { - return RunProcess(arguments, false, writer); + return RunProcess(arguments, false, writer, createNoWindow, cancellationToken); } throw; } @@ -1632,13 +1684,16 @@ public static int RunProcess(string arguments, bool runAsAdministrator, TextWrit var namedPipeServerConnector = new NamedPipeServerConnector(); if (namedPipeServerConnector.WaitForConnection(pipeStream, pipeName)) { - using (var reader = new StreamReader(pipeStream)) + var reader = new StreamReader(pipeStream, new UTF8Encoding(false, true), true, 1024 * 1024); + + using var registration = cancellationToken.Register(o => { - string line; - while ((line = reader.ReadLine()) != null) - { - writer.WriteLine(line); - } + KillProcessAndDescendants(process.Id); + }, null); + + while (reader.ReadLine() is { } line) + { + writer.WriteLine(line); } while (!processFinished) diff --git a/pwiz_tools/Skyline/Util/UtilInstall.cs b/pwiz_tools/Skyline/Util/UtilInstall.cs index c53afc7b73..844fc9e017 100644 --- a/pwiz_tools/Skyline/Util/UtilInstall.cs +++ b/pwiz_tools/Skyline/Util/UtilInstall.cs @@ -23,6 +23,7 @@ using System.Linq; using System.Net; using System.Text.RegularExpressions; +using System.Threading; using Ionic.Zip; using pwiz.Common.SystemUtil; using pwiz.Skyline.Model.Tools; @@ -363,14 +364,14 @@ public interface ISkylineProcessRunnerWrapper /// /// Wrapper interface for the NamedPipeProcessRunner class /// - int RunProcess(string arguments, bool runAsAdministrator, TextWriter writer); + int RunProcess(string arguments, bool runAsAdministrator, TextWriter writer, bool createNoWindow = false, CancellationToken cancellationToken = default); } public class SkylineProcessRunnerWrapper : ISkylineProcessRunnerWrapper { - public int RunProcess(string arguments, bool runAsAdministrator, TextWriter writer) + public int RunProcess(string arguments, bool runAsAdministrator, TextWriter writer, bool createNoWindow = false, CancellationToken cancellationToken = default) { - return SkylineProcessRunner.RunProcess(arguments, runAsAdministrator, writer); + return SkylineProcessRunner.RunProcess(arguments, runAsAdministrator, writer, createNoWindow, cancellationToken); } } @@ -384,7 +385,7 @@ public class TestSkylineProcessRunner : ISkylineProcessRunnerWrapper public int ExitCode { get; set; } public string stringToWriteToWriter { get; set; } - public int RunProcess(string arguments, bool runAsAdministrator, TextWriter writer) + public int RunProcess(string arguments, bool runAsAdministrator, TextWriter writer, bool createNoWindow = false, CancellationToken cancellationToken = default) { if (!UserOkRunAsAdministrator) { diff --git a/pwiz_tools/Skyline/Util/UtilUI.cs b/pwiz_tools/Skyline/Util/UtilUI.cs index fd55e70da3..0c94cc7c17 100644 --- a/pwiz_tools/Skyline/Util/UtilUI.cs +++ b/pwiz_tools/Skyline/Util/UtilUI.cs @@ -131,6 +131,11 @@ public bool IsCanceled get { return _broker.IsCanceled; } } + public CancellationToken CancellationToken + { + get { return _broker.CancellationToken; } + } + public UpdateProgressResponse UpdateProgress(IProgressStatus status) { _broker.ProgressValue = status.PercentComplete; diff --git a/scripts/misc/vcs_trigger_and_paths_config.py b/scripts/misc/vcs_trigger_and_paths_config.py index 20adccda5e..2e29bc6abb 100644 --- a/scripts/misc/vcs_trigger_and_paths_config.py +++ b/scripts/misc/vcs_trigger_and_paths_config.py @@ -111,7 +111,7 @@ ("pwiz_tools/Shared/.*", merge(targets['Skyline'], targets['BumbershootRelease'], targets['Container'])), ("pwiz_tools/.*", targets['All']), ("Jamroot.jam", targets['All']), - ("*.bat", targets['Windows']), - ("*.sh", targets['Linux']) + (".*\.bat", targets['Windows']), + (".*\.sh", targets['Linux']) ]