Пример #1
0
Файл: fyd.py Проект: mcvmcv/fyd2
 def initUI(self):
     self.setWindowTitle("Data Formatter")
     self.menubar = fydMenuBar()
     self.setMenuBar(self.menubar)
     self.statusBar()
     self.resultsTable = ResultsTable()
     self.infoTable = InfoTable()
     self.tabWidget = QtGui.QTabWidget()
     self.tabWidget.addTab(self.resultsTable, "Results")
     self.tabWidget.addTab(self.infoTable, "Information")
     self.setCentralWidget(self.tabWidget)
     self.showMaximized()
Пример #2
0
Файл: fyd.py Проект: mcvmcv/fyd2
 def new(self):
     """Nukes the resultsTable and starts again."""
     del self.resultsTable
     del self.infoTable
     del self.tabWidget
     self.resultsTable = ResultsTable()
     self.infoTable = InfoTable()
     self.tabWidget = QtGui.QTabWidget()
     self.tabWidget.addTab(self.resultsTable, "Results")
     self.tabWidget.addTab(self.infoTable, "Information")
     self.setCentralWidget(self.tabWidget)
     self.__updateView()
Пример #3
0
Файл: fyd.py Проект: mcvmcv/fyd2
class DataFormatter(QtGui.QMainWindow):
    def __init__(self):
        super(DataFormatter, self).__init__()
        self.initVars()
        self.initUI()
        self.initConnections()

    def initVars(self):
        self.path = "/home/mcv/Genotyping Project Records/Genotyping Projects/"
        self.ignoreList = ["", "Population"]

    def initUI(self):
        self.setWindowTitle("Data Formatter")
        self.menubar = fydMenuBar()
        self.setMenuBar(self.menubar)
        self.statusBar()
        self.resultsTable = ResultsTable()
        self.infoTable = InfoTable()
        self.tabWidget = QtGui.QTabWidget()
        self.tabWidget.addTab(self.resultsTable, "Results")
        self.tabWidget.addTab(self.infoTable, "Information")
        self.setCentralWidget(self.tabWidget)
        self.showMaximized()

    def initConnections(self):
        self.menubar.clearTableAction.triggered.connect(self.new)
        self.menubar.openAction.triggered.connect(self.openDialog)
        self.menubar.saveAction.triggered.connect(self.saveDialog)
        # 		self.menubar.readMarkerConfigAction.triggered.connect(self.readMarkerConfig)
        self.menubar.createSamplesAction.triggered.connect(self.createSamples)
        self.menubar.createPlatesAction.triggered.connect(self.createPlates)
        # 		self.menubar.createSamplesFromExcelAction.triggered.connect(self.createSamplesFromExcel)
        self.menubar.createSamplesFromExcelKeaAction.triggered.connect(self.createSamplesFromExcelKea)
        self.menubar.kiwifruitWizardAction.triggered.connect(self.kiwifruitWizard)
        self.menubar.brassicaRyegrassWizardAction.triggered.connect(self.brassicaRyegrassWizard)
        self.menubar.keaWizardAction.triggered.connect(self.keaWizard)
        self.menubar.exitAction.triggered.connect(self.close)

        # 		self.menubar.importSamplesSpreadsheetAction.triggered.connect(self.importSamplesSpreadsheet)
        self.menubar.importEBridaSamplesSpreadsheetAction.triggered.connect(self.importEBridaSamplesSpreadsheet)
        self.menubar.importPlatesSpreadsheetAction.triggered.connect(self.importPlatesSpreadsheet)
        self.menubar.importLightcyclerFileAction.triggered.connect(self.importLightcyclerFile)
        self.menubar.importTaqmanFileAction.triggered.connect(self.importTaqmanFile)
        # 		self.menubar.importSpecFileAction.triggered.connect(self.importSpecFile)

        self.menubar.cherryPickHelpAction.triggered.connect(self.cherryPickHelp)
        self.menubar.cherryPickNegativeUnknownAction.triggered.connect(self.cherryPickNegativeUnknown)
        self.menubar.cherryPickFromPopulationAction.triggered.connect(self.cherryPickFromPopulation)
        self.menubar.importCherryPickingAction.triggered.connect(self.importCherryPicking)

        self.menubar.setCropAction.triggered.connect(self.setCrop)
        self.menubar.explainCropAction.triggered.connect(self.explainCrop)
        self.menubar.editPlatesAction.triggered.connect(self.editPlates)
        self.menubar.viewPlateStatsAction.triggered.connect(self.viewPlateStats)
        # 		self.menubar.viewPopulationStatsAction.triggered.connect(self.viewPopulationStats)

        self.menubar.standardiseResultsAction.triggered.connect(self.standardiseResults)
        self.menubar.negativiseUnknownsAction.triggered.connect(self.negativiseUnknowns)
        self.menubar.addAutoResultsAction.triggered.connect(self.addAutoResults)
        self.menubar.addManualResultsAction.triggered.connect(self.addManualResults)
        self.menubar.setNonExistsToNoSampleAction.triggered.connect(self.setNonExistsToNoSample)
        self.menubar.setNonExistsToNegativeAction.triggered.connect(self.setNonExistsToNegative)
        self.menubar.includeAllAction.triggered.connect(self.includeAll)
        self.menubar.excludeFailGrindsAction.triggered.connect(self.excludeFailGrinds)
        self.menubar.excludeNegativeFailGrindsAction.triggered.connect(self.excludeNegativeFailGrinds)
        self.menubar.excludeNonExistsAction.triggered.connect(self.excludeNonExists)
        self.menubar.kiwifruitResultsAction.triggered.connect(self.kiwifruitResults)
        self.menubar.brassicaRyegrassResultsAction.triggered.connect(self.brassicaRyegrassResults)
        self.menubar.appleResultsAction.triggered.connect(self.appleResults)

        self.menubar.outputHarvestSheetsAction.triggered.connect(self.outputHarvestSheets)
        # 		self.menubar.outputSampleTableAction.triggered.connect(self.outputSampleTable)
        self.menubar.outputForKeaAction.triggered.connect(self.outputForKea)
        self.menubar.outputForEBridaAction.triggered.connect(self.outputForEBrida)
        self.menubar.colouredResultsAction.triggered.connect(self.colouredResults)
        self.menubar.niceKeaSpreadsheetAction.triggered.connect(self.niceKeaSpreadsheet)

    ########################################################################
    ##	Private Methods
    def __updateView(self):
        """Reorders the columns of self.resultsTable, and updates the 
		self.tableWidget dataframe to the current version of self.resultsTable"""
        self.resultsTable.update()
        self.infoTable.update()

    def __getPlatesRecords(self):
        title = "Locate plate spreadsheet..."
        fileName, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        plateData = pd.read_excel(fileName, sheetname="Plates", header=1)
        plateData = plateData[["Slipstream Identifier", "Plate Name", "Kea Name", "Non-harvested plants", "Bad Grinds"]]
        plateData.columns = ["Plate ID", "Plate Name", "Plate Kea", "Non-harvested plants", "Bad Grinds"]
        plateData = plateData.dropna(subset=["Plate ID"])
        return plateData

    def __getStatsTable(self, included, groups, by):
        stats = included[[by, "Group"]].groupby([by, "Group"]).size().unstack()
        plateLookup = included.drop_duplicates(by)[[by]]
        columns = [by] + groups
        statsTable = pd.DataFrame(columns=columns).append(plateLookup).set_index([by])
        statsTable = statsTable.combine_first(stats).fillna(0).reset_index()
        statsTable["Samples"] = statsTable.sum(axis=1)
        statsTable = statsTable[[by, "Samples"] + groups]
        return statsTable

    ## To make platesheets work:
    def __getStatsTable(self, included, groups, by):
        included = included[included.Group != "No sample"]
        stats = included[["Plate ID", "Group"]]
        stats = stats.groupby(["Plate ID", "Group"]).size().unstack()
        plateLookup = included.drop_duplicates("Plate ID")[["Plate ID", "Plate Name"]]
        columns = ["Plate ID", "Plate Name"] + groups
        statsTable = pd.DataFrame(columns=columns).append(plateLookup).set_index(["Plate ID"])
        statsTable = statsTable.combine_first(stats).fillna(0).reset_index()
        statsTable["Samples"] = statsTable.sum(axis=1)
        statsTable = statsTable[["Plate ID", "Plate Name", "Samples"] + groups]
        return statsTable

    def __readMarkerConfig(self, fileName):
        with open(fileName, "r") as config:
            for line in config.readlines():
                lineList = line.split("\t")
                Crop = lineList[0]
                Marker = lineList[1]
                Result = lineList[2]
                Group = lineList[3]
                Colour = lineList[4]

    ########################################################################
    ##	File Menu Actions
    def new(self):
        """Nukes the resultsTable and starts again."""
        del self.resultsTable
        del self.infoTable
        del self.tabWidget
        self.resultsTable = ResultsTable()
        self.infoTable = InfoTable()
        self.tabWidget = QtGui.QTabWidget()
        self.tabWidget.addTab(self.resultsTable, "Results")
        self.tabWidget.addTab(self.infoTable, "Information")
        self.setCentralWidget(self.tabWidget)
        self.__updateView()

    def openDialog(self):
        """Opens a saved .xls file."""
        title = "Open project .xls file..."
        fileName, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        results = pd.io.excel.read_excel(fileName, sheetname="Results")
        if set(results.columns) == set(self.resultsTable.table.columns):
            self.resultsTable.table = results
            self.infoTable.table = pd.io.excel.read_excel(fileName, sheetname="Information")
            self.__updateView()
        else:
            dialog = QtGui.QMessageBox.information(
                self,
                "Open file fail...",
                "The columns in this file's results\n"
                + "table are not what is expected. Please\n"
                + "make sure you have the right file, and\n"
                + "make sure the columns of the saved file\n"
                + "match those in the Results tab.",
                QtGui.QMessageBox.Ok,
                QtGui.QMessageBox.Ok,
            )

    def saveDialog(self):
        """Saves the project as an .xls file."""
        title = "Save project as..."
        fileName, f = QtGui.QFileDialog.getSaveFileName(self, title, self.path)
        fileName = fileName.split(".")[0]
        writer = pd.ExcelWriter(fileName + ".xls")
        self.resultsTable.table.to_excel(writer, "Results")
        self.infoTable.table.to_excel(writer, "Information")
        writer.save()

    def readMarkerConfig(self):
        """Opens a specified marker config file and creates marker data."""
        title = "Select marker config file..."
        fileName, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        self.markers = self.__readMarkerConfig(fileName)

    def createSamples(self):
        """Creates a given number of samples with no names or plant names."""
        samples, ok = CreateSamplesDialog.create(self)
        if ok:
            newSamplesDF = pd.DataFrame(
                samples, columns=["Plate ID", "Plate Name", "Plate Kea", "Well", "Grind", "Include"]
            )
            self.resultsTable.append(newSamplesDF)
            self.infoTable.append(newSamplesDF[["Plate ID", "Plate Name", "Plate Kea", "Well"]])
            self.__updateView()

    def createPlates(self):
        """Creates a set of consecutively numbered full plates."""
        samples, ok = CreatePlatesDialog.create(self)
        if ok:
            newSamplesDF = pd.DataFrame(
                samples, columns=["Plate ID", "Plate Name", "Plate Kea", "Well", "Crop", "Grind", "Include"]
            )
            self.resultsTable.append(newSamplesDF)
            self.infoTable.append(newSamplesDF[["Plate ID", "Plate Name", "Plate Kea", "Well"]])
            self.__updateView()

    def createSamplesFromExcel(self):
        """Opens an Excel spreadsheet and creates samples."""
        title = "Locate sample spreadsheet..."
        fileName, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        excelFile = pd.ExcelFile(fileName)
        sheets = excelFile.sheet_names
        sheet, ok = MyDialog.chooseExcelSheet(sheets)
        imported = excelFile.parse(sheet)
        columns = list(imported.columns)
        name, well, ok = MyDialog.getIndexMatches(columns)
        columnsToImport, ok = MyDialog.chooseColumns(columns)
        columnsToImport = self.columns2 + columnsToImport
        imported["Plate ID"] = ""
        imported["Plate Name"] = imported.get(name, "")
        imported["Well"] = imported.get(well, "")
        imported = imported.dropna(how="all", subset=["Plate Name", "Well"])
        imported = imported.set_index("Plate Name")
        title = "Locate plate spreadsheet..."
        fileName, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        plateData = pd.read_excel(fileName, sheetname="Plates", header=1)
        plateData = plateData[["Slipstream Identifier", "Kea Name", "Plate Name", "Bad Grinds"]]
        plateData.columns = ["Plate ID", "Kea Name", "Plate Name", "Bad Grinds"]
        plateData = plateData.dropna(how="all", subset=["Kea Name"])
        plateData = plateData.set_index("Kea Name")
        imported.update(plateData)
        imported = imported.reset_index()
        badGrinds = plateData.dropna(how="all", subset=["Bad Grinds"])
        badGrinds = pd.DataFrame(badGrinds["Bad Grinds"].str.split(" ").tolist(), index=badGrinds["Plate ID"]).stack()
        badGrinds = badGrinds.reset_index().drop("level_1", 1)
        badGrinds.columns = ["Plate ID", "Well"]
        badGrinds["Grind"] = False
        badGrinds = badGrinds.set_index(["Plate ID", "Well"])
        self.resultsTable = self.resultsTable.append(imported[self.columns2])
        self.resultsTable = self.resultsTable.set_index(["Plate ID", "Well"])
        self.resultsTable.update(badGrinds)
        self.resultsTable = self.resultsTable.reset_index()
        self.infoTable = self.infoTable.append(imported[columnsToImport])
        self.__updateView()

    def createSamplesFromExcelKea(self):
        """Opens a Kea Sample Batch spreadsheet and imports the samples."""
        title = "Locate Kea sample batch spreadsheet..."
        fileName, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        excelFile = pd.ExcelFile(fileName)
        imported = excelFile.parse("Data")
        imported["Plate ID"] = ""
        sheetColumns = ["Plate ID", "Plate No", "Position on Plate(s)", "PlantID", "Sample ID", "Plant Alt Names"]
        tableColumns = ["Plate ID", "Plate Kea", "Well", "PlantID", "Sample ID", "Plant Alt Names"]
        resColumns = ["Plate ID", "Plate Kea", "Well", "Population"]
        imported = imported[sheetColumns]
        imported.columns = tableColumns

        imported = imported.dropna(how="all", subset=["Sample ID"])
        self.population = ""

        def addPopulations(row):
            if pd.isnull(row["Plate Kea"]):
                self.population = row["Sample ID"].split(" ")[0]
            return self.population

        imported["Population"] = imported.apply(addPopulations, 1)

        imported = imported.dropna(how="all", subset=["Plate Kea", "Well"])
        self.resultsTable.append(imported[resColumns])
        self.infoTable.append(imported)
        plateData = self.__getPlatesRecords()
        self.resultsTable.importPlateData(plateData, "Plate Kea")
        self.infoTable.importPlateData(plateData, "Plate Kea")
        self.__updateView()

    def kiwifruitWizard(self):
        """Runs a wizard to input data for a kiwifruit project."""
        dialog = QtGui.QMessageBox.information(
            self,
            "Kiwifruit Wizard",
            "You will be prompted to select files for\n"
            + "import. The files you will need are:\n"
            + " - Kea sample batch harvest sheet\n"
            + " - Plates records spreadsheet\n"
            + " - E-brida information sheet\n"
            + " - Lightcycler .txt files\n\n"
            + "After running this wizard you will have\n"
            + "all required information imported and\n"
            + "all logic on results set up.\n\n"
            + "Afterwards you will want to save the\n"
            + "project, and export the data in the\n"
            + "appropriate format.",
            QtGui.QMessageBox.Ok,
            QtGui.QMessageBox.Ok,
        )
        self.createSamplesFromExcelKea()
        self.importEBridaSamplesSpreadsheet()
        self.resultsTable.setCrop("Kiwifruit")
        self.importLightcyclerFile()
        self.kiwifruitResults()
        self.__updateView()

    def brassicaRyegrassWizard(self):
        """Runs a wizard to input data for a brassica or ryegrass project."""
        dialog = QtGui.QMessageBox.information(
            self,
            "Brassica/Ryegrass Wizard",
            "You will be prompted to set up samples\n"
            + "and to select files for import. Sample\n"
            + "setup requires you to select the crop.\n"
            + "This determines which results will be\n"
            + "included in the output files.\n\n"
            + "For Brassica, select:\n"
            + "-Brassica for a normal project\n"
            + "-Brassica R-S to include Rape-Swede\n"
            + "-Brassica J/P-C to include Juncea/Pak Choy\n"
            + "-Brassica SPF for Spitfire/Other Rape\n\n"
            + "For Ryegrass, select:\n"
            + "-Ryegrass for a normal project\n"
            + "-Ryegrass501 for AR501/Other Endo\n\n"
            + "The files you will need are the plates\n"
            + "records spreadsheet and the appropriate\n"
            + "Lightcycler .txt files.\n\n"
            + "After running this wizard you will have\n"
            + "all required information imported and\n"
            + "all logic on results set up.\n\n"
            + "Afterwards you will want to save the\n"
            + "project, and export the data in the\n"
            + "appropriate format.",
            QtGui.QMessageBox.Ok,
            QtGui.QMessageBox.Ok,
        )
        self.createPlates()
        self.importPlatesSpreadsheet()
        self.importLightcyclerFile()
        self.resultsTable.negativiseUnknowns()
        self.brassicaRyegrassResults()
        self.__updateView()

    def keaWizard(self):
        """Runs a wizard to input data for a kea (no ebrida) project."""
        dialog = QtGui.QMessageBox.information(
            self,
            "Kea Wizard",
            "You will be prompted to select files for\n"
            + "import. The files you will need are:\n"
            + " - Kea sample batch harvest sheet\n"
            + " - Plates records spreadsheet\n"
            + " - Lightcycler .txt files\n\n"
            + "After running this wizard you will have\n"
            + "all required information imported and\n"
            + "all logic on results set up.\n\n"
            + "Afterwards you will want to save the\n"
            + "project, and export the data in the\n"
            + "appropriate format.",
            QtGui.QMessageBox.Ok,
            QtGui.QMessageBox.Ok,
        )
        self.createSamplesFromExcelKea()
        self.setCrop()
        self.importLightcyclerFile()
        self.appleResults()
        self.__updateView()

    ########################################################################
    ##	Import Menu Actions
    def importSamplesSpreadsheet(self):
        """Opens an Excel spreadsheet and adds the information to samples."""
        title = "Locate samples spreadsheet..."
        fileName, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        excelFile = pd.ExcelFile(fileName)
        sheet, ok = MyDialog.chooseExcelSheet(excelFile.sheet_names)
        imported = excelFile.parse(sheet)
        indexBy, ok = MyDialog.chooseIndexMethod()
        if indexBy == "Plate ID and Well":
            plateC, wellC, ok = MyDialog.choosePlateAndWellColumns(imported.columns)
            imported["Plate ID"] = imported[plateC]
            imported["Well"] = imported[wellC]
            imported = imported.set_index(["Plate ID", "Well"])
            self.infoTable = self.infoTable.set_index(["Plate ID", "Well"])
        elif indexBy == "Plate Name and Well":
            plateC, wellC, ok = MyDialog.choosePlateAndWellColumns(imported.columns)
            imported["Plate Name"] = imported[plateC]
            imported["Well"] = imported[wellC]
            imported = imported.set_index(["Plate ID", "Well"])
            self.infoTable = self.infoTable.set_index(["Plate ID", "Well"])
        elif indexBy == "Other column":
            tabC, impC, ok = MyDialog.chooseCol(self.infoTable.columns, imported.columns)
            imported = imported.set_index(impC)
            self.infoTable = self.infoTable.set_index(tabC)
        columnsToImport, ok = MyDialog.chooseColumns(imported.columns)
        self.infoTable = self.infoTable.join(imported[columnsToImport], rsuffix="_new")
        self.infoTable = self.infoTable.reset_index()
        self.__updateView()

    def importEBridaSamplesSpreadsheet(self):
        """Imports an EBrida Excel spreadsheet and adds the information.
		Join is done on 'Plant Alt Names' table column with 'Barcode'
		spreadsheet column."""
        title = "Locate E-Brida spreadsheet..."
        fileName, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        excelFile = pd.ExcelFile(fileName)
        imported = excelFile.parse("Sheet1")
        columns = {
            "From Ped. Item (origin)": "From Ped. Item",
            "synonym": "synonyms",
            "Synonym": "synonyms",
            "Synonyms": "synonyms",
            "breederCrCode": "synonyms",
            "BreederCrCode": "synonyms",
            "breederName": "BreederName",
        }
        imported = imported.rename(columns=columns)
        tableKey, importKey = "Plant Alt Names", "Barcode"
        self.infoTable.importSampleData(imported, tableKey, importKey)
        self.__updateView()

    def importPlatesSpreadsheet(self):
        """Opens the Plates Records spreadsheet and adds data."""
        plateData = self.__getPlatesRecords()
        self.resultsTable.importPlateData(plateData, "Plate ID")
        self.infoTable.importPlateData(plateData, "Plate ID")
        self.__updateView()

    def importLightcyclerFile(self):
        """Imports Lightcycler .txt files and adds the results, locating
		samples by plate name (taken from filename) and well."""
        title = "Import LC file(s)..."
        filter_mask = "Lightcycler text files (*.txt)"
        lightFileNames, f = QtGui.QFileDialog.getOpenFileNames(self, title, self.path, filter_mask)
        fileDataLists, ok = LCDialog.importLC(lightFileNames)
        if ok:
            self.resultsTable.addLCFiles(fileDataLists)
            self.__updateView()

    def importTaqmanFile(self):
        """Imports Taqman .txt files and adds the results, locating samples
		by plate name (taken from filename) and well."""
        title = "Import Taqman file(s)..."
        filter_mask = "Taqman text files (*.txt)"
        lightFileNames, f = QtGui.QFileDialog.getOpenFileNames(self, title, self.path, filter_mask)
        fileDataLists, ok = LCDialog.importLC(lightFileNames)
        if ok:
            self.resultsTable.addTaqFiles(fileDataLists)
            self.__updateView()

    def importSpecFile(self):
        """Imports csv files from the plate spec machine, creates a
		standard curve for each file, converts to concentration, and adds
		the data as a column"""
        pass

    ########################################################################
    ##	Cherry Menu Actions
    def cherryPickHelp(self):
        """Displays information about how to run a round of cherrypicking."""
        dialog = QtGui.QMessageBox.information(
            self,
            "About cherrypicking",
            "Only cherrypicking for Negatives/Unknowns is fully\n"
            + "implemented currently. Multiple marker cherrypicking\n"
            + "will be available in Poo.py.\n\n"
            + "Choosing Cherry pick N/U will open a dialog where\n"
            + "can choose the location for the cherrypicking files\n"
            + "to be saved. There will be one Excel file, which gives\n"
            + "an overview, and a bunch of .csv's to run on the robot.\n\n"
            + "The Excel file is required when the results are to be\n"
            + "imported, in order for the LC results to be mapped to\n"
            + "the correct sample.\n",
            QtGui.QMessageBox.Ok,
            QtGui.QMessageBox.Ok,
        )

    def cherryPickNegativeUnknown(self):
        """Outputs files for cherrypicking Negatives/Unknowns."""
        cherryTable = self.resultsTable.getCherriesNU()
        self.outputCherries(cherryTable)

    def cherryPickFromPopulation(self):
        """Outputs files for cherrypicking by Population and Result."""
        title = "Choose populations, results, and numbers to cherrypick..."
        populations = self.resultsTable.getPopulations()
        groups = self.resultsTable.getGroups()
        cherryData, ok = CherryDialog.run(populations, groups)
        cherryTable = self.resultsTable.getCherriesByPop(cherryData)
        self.outputCherries(cherryTable)

    def outputCherries(self, cherryTable):
        """Outputs cherrypicking files for the passed cherryTable."""
        title = "Choose folder for output..."
        directory = QtGui.QFileDialog.getExistingDirectory(self, title, self.path)
        writer = pd.ExcelWriter(os.path.join(directory, "cherrypicking.xls"))
        cherryTable.to_excel(writer, "All", index=False)
        summarySource = cherryTable[["Run", "Source Position", "Source plate"]].drop_duplicates().reset_index(drop=True)
        summaryDest = (
            cherryTable[["Run", "Destination Position", "Dest plate"]].drop_duplicates().reset_index(drop=True)
        )
        summarySource = summarySource.pivot(index="Run", columns="Source Position", values="Source plate")
        summaryDest = summaryDest.pivot(index="Run", columns="Destination Position", values="Dest plate")
        summary = summarySource.join(summaryDest)
        summary.to_excel(writer, "Summary")
        runs = pd.unique(cherryTable.Run.ravel())
        for run in runs:
            runTable = cherryTable[cherryTable.Run == run]
            runTable.to_excel(writer, run, index=False)
            runTable.to_csv(os.path.join(directory, run + ".csv"), index=False)
        writer.save()

    def importCherryPicking(self):
        """Imports files for a round of Cherrypicking."""
        title = "Choose cherrypicking file..."
        cherryFile, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        title = "Choose lightcycler files..."
        filter_mask = "Lightcycler text files (*.txt)"
        lightFileNames, f = QtGui.QFileDialog.getOpenFileNames(self, title, self.path, filter_mask)
        fileDataLists, ok = LCDialog.importLC(lightFileNames)
        lc = pd.read_excel(cherryFile, sheetname="All")
        lc = lc[["Source plate", "Dest plate", "Source Well", "Destination Well"]]
        lc.columns = ["Plate ID", "CP Plate", "Well", "CP Well"]
        lc["LC Well"] = ""
        lc["Result"] = ""
        lc["Experiment"] = ""
        lc = lc.set_index(["CP Plate", "CP Well"])
        for fDL in fileDataLists:
            name, plate, exp, robot, pos = fDL
            lcFrame = pd.read_table(name, sep="\t", header=1)
            lcFrame = lcFrame.drop(["Include", "Color", "Name", "Status"], 1)
            lcFrame["Experiment"] = exp
            lcFrame["CP Plate"] = plate
            lcFrame["CP Well"] = lcFrame["Pos"].map(lambda x: convert[robot][pos][x])
            lcFrame["Group"] = lcFrame["Group"].astype(str)
            lcFrame.columns = ["LC Well", "Result", "Experiment", "CP Plate", "CP Well"]
            lcFrame = lcFrame.set_index(["CP Plate", "CP Well"])
            lc.update(lcFrame)
        lc = lc.set_index(["Plate ID", "Well"])
        self.resultsTable.table = self.resultsTable.table.set_index(["Plate ID", "Well"])
        self.resultsTable.table.update(lc)
        self.resultsTable.table = self.resultsTable.table.reset_index()
        self.__updateView()

    def importCherryPickingTaq(self):
        """Taq"""
        title = "Choose cherrypicking file..."
        cherryFile, f = QtGui.QFileDialog.getOpenFileName(self, title, self.path)
        title = "Choose lightcycler files..."
        filter_mask = "Lightcycler text files (*.txt)"
        lightFileNames, f = QtGui.QFileDialog.getOpenFileNames(self, title, self.path, filter_mask)
        fileDataLists, ok = LCDialog.importLC(lightFileNames)
        lc = pd.read_excel(cherryFile, sheetname="All")
        lc = lc[["Source plate", "Dest plate", "Source Well", "Destination Well"]]
        lc.columns = ["Plate ID", "CP Plate", "Well", "CP Well"]
        lc["LC Well"] = ""
        lc["Result"] = ""
        lc["Experiment"] = ""
        lc = lc.set_index(["CP Plate", "CP Well"])
        for fDL in fileDataLists:
            name, plate, exp, robot, pos = fDL
            lcFrame = pd.read_table(name, sep="\t", header=1)
            lcFrame = lcFrame.drop(["Include", "Color", "Name", "Status", "465-510", "533-580", "Score"], 1)
            lcFrame["Experiment"] = exp
            lcFrame["CP Plate"] = plate
            lcFrame["CP Well"] = lcFrame["Pos"].map(lambda x: convert[robot][pos].get(x, ""))
            lcFrame["Call"] = lcFrame["Call"].astype(str)
            lcFrame.columns = ["LC Well", "Result", "Experiment", "CP Plate", "CP Well"]
            lcFrame = lcFrame[lcFrame["CP Well"] != ""]
            lcFrame = lcFrame.set_index(["CP Plate", "CP Well"])
            lc.update(lcFrame)
        lc = lc.set_index(["Plate ID", "Well"])
        lc = lc[lc["Result"] != ""]
        self.resultsTable.table = self.resultsTable.table.set_index(["Plate ID", "Well"])
        self.resultsTable.table.update(lc)
        self.resultsTable.table = self.resultsTable.table.reset_index()
        self.__updateView()

    ########################################################################
    ##	Data Menu Actions
    def editPlates(self):
        """Opens a popup table where the data in the sample table can be
		edited by plate, rather than by sample."""
        plates = self.resultsTable.table[
            ["Plate ID", "Plate Name", "Plate Kea", "Crop", "Experiment", "Grind", "Include"]
        ]
        plates = plates.drop_duplicates(subset=["Plate ID"])
        platesData, ok = EditPlatesDialog.run(plates)
        self.resultsTable.editPlates(platesData)
        self.infoTable.editPlates(platesData)
        self.__updateView()

    def viewPlateStats(self):
        """Opens a popup with result stats organised by plate."""
        included = self.resultsTable.table[self.resultsTable.table["Include"] == True]
        included = included[included.Group != "No sample"]
        groups = list(np.unique(included["Group"]))
        statsTable = self.__getStatsTable(included, groups, "Plate ID")
        ok = StatsDialog.run(statsTable)

    def viewPopulationStats(self):
        """Opens a popup with result stats organised by population."""
        included = self.resultsTable.table[self.resultsTable.table["Include"] == True]
        groups = list(np.unique(included["Group"]))
        statsTable = self.__getStatsTable(included, groups, "Population")
        ok = StatsDialog.run(statsTable)

    def setCrop(self):
        """Opens a popup to set crop for all samples."""
        crop, ok = MyDialog.chooseCrop()
        if ok:
            self.resultsTable.setCrop(crop)
            self.__updateView()

    def explainCrop(self):
        """Opens a popup to explain which marker results are valid."""
        dialog = QtGui.QMessageBox.information(
            self,
            "Crop explanation",
            "Calls expected:\n"
            + "Brassica:			Kale, Rape, Swede, Turnip\n"
            + "Brassica R-S:		Kale, Rape, Swede, Turnip, Rape-Swede\n"
            + "Brassica J-P/C:		Kale, Rape, Swede, Turnip, Juncea, Pak Choy\n"
            + "Brassica SPF:		Spitfire, Other Rape\n"
            + "Ryegrass:		AR1, AR37, Ct/wt, AR542/584, AR542, AR581\n"
            + "Ryegrass501:		AR501, Other Endo\n"
            + "Kiwifruit:			Male, Female, Super Male,\n"
            + "			(Super) (Fem/M)ale Type (1/2/I/II)\n"
            + "Apple:			1, 2, 3, 4\n\n"
            + "Statistics provided:\n"
            + "Brassica:			Kale, Rape, Swede, Turnip\n"
            + "Brassica R-S:		Kale, Rape, Swede, Turnip, Rape-Swede\n"
            + "Brassica J-P/C:		Kale, Rape, Swede, Turnip, Juncea, Pak Choy\n"
            + "Brassica SPF:		Spitfire, Other Rape\n"
            + "Ryegrass:		Group 1, Group 2, Group 3, Group 4, Group 5\n"
            + "Ryegrass501:		AR501, Other Endo\n"
            + "Kiwifruit:			Male, Female\n"
            + "Apple:			1, 2, 3, 4\n",
            QtGui.QMessageBox.Ok,
            QtGui.QMessageBox.Ok,
        )

    ########################################################################
    ##	Results Menu Actions
    def standardiseResults(self):
        """Corrects HRM results so you can change both 'male' and 'Male' to 'M'."""
        results = np.unique(self.resultsTable.table["Result"])
        standardisations, ok = MyDialog.standardiseResults(results)
        if ok:
            self.resultsTable.table["Result"] = self.resultsTable.table["Result"].map(lambda res: standardisations[res])
            self.__updateView()

    def negativiseUnknowns(self):
        """Sets all Unknown results in Results column to Negative."""
        self.resultsTable.negativiseUnknowns()
        self.__updateView()

    def addAutoResults(self):
        self.resultsTable.table["Group"] = self.resultsTable.table.apply(
            lambda row: standards[row["Crop"]].get(row["Result"], np.nan), 1
        )
        self.__updateView()

    def addManualResults(self):
        """Adds data to the Group column based on the Result column"""
        results = np.unique(self.resultsTable.table["Result"])
        standardisations, ok = MyDialog.standardiseResults(results)
        if ok:
            self.resultsTable.table["Group"] = self.resultsTable.table["Result"].map(lambda res: standardisations[res])
            self.__updateView()

    def setNonExistsToNoSample(self):
        self.resultsTable.setNonExistsToNoSample()

    def setNonExistsToNegative(self):
        self.resultsTable.setNonExistsToNegative()

    def includeAll(self):
        self.resultsTable.includeAll()
        self.__updateView()

    def excludeFailGrinds(self):
        self.resultsTable.excludeFailGrinds()
        self.__updateView()

    def excludeNegativeFailGrinds(self):
        self.resultsTable.excludeNegativeFailGrinds()
        self.__updateView()

    def excludeNonExists(self):
        self.resultsTable.excludeNonExists()
        self.__updateView()

    def kiwifruitResults(self):
        self.addAutoResults()
        self.setNonExistsToNoSample()

    def brassicaRyegrassResults(self):
        self.addAutoResults()
        self.excludeNegativeFailGrinds()
        self.excludeNonExists()

    def appleResults(self):
        self.addAutoResults()
        self.setNonExistsToNoSample()

    ########################################################################
    ##	Export Menu Actions
    def __checkResults(self):
        """Makes sure there are good results in the Results and Groups
		columns in preparation for export."""
        if self.resultsTable.missingResults()[0]:
            dialog = QtGui.QMessageBox.information(
                self,
                "Invalid Results",
                "One or more entries in the Result column is invalid.\n"
                + "Please check all LC files imported properly.\n"
                + "There should be no nan in the Result column.",
                QtGui.QMessageBox.Ok,
                QtGui.QMessageBox.Ok,
            )
            return False
        elif self.resultsTable.missingResults()[1]:
            dialog = QtGui.QMessageBox.information(
                self,
                "Invalid Groups",
                "One or more entries in the Group column is invalid.\n"
                + "Please check for typos in the Results column,\n"
                + "and make sure the correct marker is selected.\n"
                + "Typos can be mixed with Results > Standardise\n"
                + "results. The marker can be set with Data > set\n"
                + "crop. After completing either of these steps,\n"
                + "select Results > Add groups by crop...",
                QtGui.QMessageBox.Ok,
                QtGui.QMessageBox.Ok,
            )
            return False
        else:
            return True

    def outputHarvestSheets(self):
        """Outputs harvest spreadsheets."""
        title = "Excel filename..."
        exportFileName, f = QtGui.QFileDialog.getSaveFileName(self, title, self.path)
        exportFileName = exportFileName.split(".")[0]
        workBook = ExportWorkbook()
        sampleTable = self.resultsTable.table.merge(self.infoTable.table, on=["Plate ID", "Well"], suffixes=("", "_y"))
        workBook.addHarvestSheets(sampleTable)
        workBook.save(exportFileName + ".xls")

    def outputForKea(self):
        """Outputs an Excel file suitable for upload to Kea via the sex
		testing process run."""
        if self.__checkResults():
            title = "Excel filename..."
            exportFileName, f = QtGui.QFileDialog.getSaveFileName(self, title, self.path)
            exportFileName = exportFileName.split(".")[0]
            experiments = self.resultsTable.getExperiments()
            experimentData, ok = ExperimentDialog.run(experiments)
            if ok:
                columns = ["Sample ID", "Plant Alt Names", "Plate Name", "Experiment", "Group"]
                resultTable = self.resultsTable.getKeaSexTestingData()
                infoTable = self.infoTable.getKeaSexTestingData()
                table = resultTable.join(infoTable, rsuffix="i").reset_index()
                table["Screening Date"] = table["Slipstream Expt No"].apply(lambda x: experimentData[x][0])
                table["Marker Name"] = table["Slipstream Expt No"].apply(lambda x: experimentData[x][1])
                table["Notes"] = ""
                table["Operator"] = ""
                columns = [
                    "Sample ID",
                    "Plant AltName",
                    "Plate",
                    "Screening Date",
                    "Slipstream Expt No",
                    "Marker Name",
                    "Sex Marker Results",
                    "Notes",
                    "Operator",
                ]
                table = table[columns]
                writer = pd.ExcelWriter(exportFileName + ".xls")
                table.to_excel(writer, "Data", index=False)
                writer.save()

    def outputForEBrida(self):
        """Outputs results as a spreadsheet based on the recieved EBrida spreadsheet..."""
        if self.__checkResults():
            title = "Excel filename..."
            exportFileName, f = QtGui.QFileDialog.getSaveFileName(self, title, self.path)
            exportFileName = exportFileName.split(".")[0]
            keys = ["Plate ID", "Well"]
            resColumns = ["Group", "Plate ID", "Plate Kea", "Well"]

            infoColumns = [
                "Pedigree Item",
                "Plot",
                "Group",
                "Start Date",
                "From Ped. Item",
                "Mother",
                "Father",
                "Barcode",
                "synonyms",
                "fundingNameCr",
                "BreederName",
                "Sample ID",
            ]
            outputColumns = [
                "Pedigree Item",
                "Plot",
                "Group",
                "Start Date",
                "From Ped. Item",
                "Mother",
                "Father",
                "Barcode",
                "synonyms",
                "fundingNameCr",
                "BreederName",
                "keaID",
                "Result",
                "Plate ID",
                "Plate Name",
                "Well",
            ]
            info = self.infoTable.table[infoColumns + keys]
            results = self.resultsTable.table[resColumns]
            results.columns = ["Result", "Plate ID", "Plate Kea", "Well"]
            outputTable = pd.merge(info, results, on=keys, sort=False)
            outputTable.rename(columns={"Sample ID": "keaID", "Plate Kea": "Plate Name"}, inplace=True)
            outputTable = outputTable[outputColumns]
            writer = pd.ExcelWriter(exportFileName + ".xls")
            outputTable.to_excel(writer, "Sheet1", index=False)
            writer.save()

    def colouredResults(self):
        """Outputs an Excel file with a summary table, a sample table,
		and a plate sheet for each plate."""
        if self.__checkResults():
            title = "Excel filename..."
            exportFileName, f = QtGui.QFileDialog.getSaveFileName(self, title, self.path)
            exportFileName = exportFileName.split(".")[0]
            crops = list(np.unique(self.resultsTable.table["Crop"]))
            groupsLists = [groups[crop] for crop in crops]
            incGroups = [item for sublist in groupsLists for item in sublist]
            incGroups = incGroups + ["Unknown", "Negative"]
            included = self.resultsTable.table[self.resultsTable.table["Include"] == True]
            exportTable = included[["Plate ID", "Plate Name", "Well", "Result", "Group"]].reset_index(drop=True)
            statsTable = self.__getStatsTable(exportTable, incGroups, "Plate ID")
            statsTable["Ind"] = statsTable.index.values
            plates = statsTable[["Plate ID", "Plate Name"]]
            ok = True
            while (plates["Plate Name"].str.len() > 23).any() and ok:
                plates, ok = ShortenPlateNamesDialog.run(plates)
            statsTable = statsTable.set_index("Plate ID")
            exportTable = exportTable.set_index("Plate ID")
            plates = plates.set_index("Plate ID")
            statsTable.update(plates)
            exportTable.update(plates)
            statsTable = statsTable.reset_index()
            exportTable = exportTable.reset_index()
            if ok:
                workBook = ExportWorkbook()
                workBook.addStatsPage(incGroups, statsTable)
                workBook.addSamplePage(exportTable)
                workBook.addPlateSheets(statsTable, exportTable, incGroups)
                workBook.save(exportFileName + ".xls")

    def niceKeaSpreadsheet(self):
        """Outputs Results and Kea data to a spreadsheet."""
        if self.__checkResults():
            title = "Excel filename..."
            exportFileName, f = QtGui.QFileDialog.getSaveFileName(self, title, self.path)
            exportFileName = exportFileName.split(".")[0]
            columns = ["Plate ID", "Plate Kea", "Well", "Sample ID", "PlantID", "Group"]
            table = (
                self.resultsTable.table.set_index(["Plate ID", "Well"])
                .join(self.infoTable.table.set_index(["Plate ID", "Well"]), rsuffix="i")
                .reset_index()
            )
            table = table[columns]
            table["Ind"] = table.index.values
            workBook = ExportWorkbook()
            workBook.addKeaTable(table)
            workBook.save(exportFileName + ".xls")