diff --git a/docs/user-documentation/general-users/additional-functionalities.md b/docs/user-documentation/general-users/additional-functionalities.md index 47f8b06e6583f1b285c57943c36487829264d9c4..688314de3d2eff64674c202794a1330f116c4c18 100644 --- a/docs/user-documentation/general-users/additional-functionalities.md +++ b/docs/user-documentation/general-users/additional-functionalities.md @@ -43,7 +43,7 @@ Here we give an overview of the main functionalities of the tables.  -## Filters +### Filters Two filter options are available form the **Filters** button: **Filter Per Column** and **Global Filter**. The first allows to filter on @@ -60,7 +60,7 @@ terms across the entire table using the **AND** or **OR** operator. >  -## Sorting +### Sorting It is possible to sort individual columns or also multiple columns. For multi-column sorting, you should click on the column header and press @@ -73,7 +73,7 @@ each column, as shown below. >  -## Exports +### Exports Tables can be exported in different ways, using the export button shown below. @@ -147,7 +147,7 @@ below. > >  -## Columns +### Columns Users can select which properties to display in the table clicking on the **Columns** button. It is also possible to show all properties or @@ -161,7 +161,7 @@ This information is stored in the database for each user.  -### **Spreadsheets** +#### **Spreadsheets** If a table contains *Objects* which have a spreadsheet field which is filled in, a spreadsheet icon is displayed in the table. Upon clicking @@ -175,7 +175,7 @@ on the icon, the content of the spreadsheet can be expanded.  > >  -### Text fields +#### Text fields If a table contains Objects which have long text fields, only the beginning of the text is shown and can be expanded. If the text contains @@ -188,7 +188,7 @@ the text becomes visible by clicking on the icon. > >  -## **Selection of entries in table** +### **Selection of entries in table** Single entries in a table can be selected using the checkbox in the row. By clicking the checkbox in the table header, all entries of the table @@ -225,14 +225,6 @@ In *Object* tables inside *Experiments/Collections* there is an  - - - - - - - - Updated on April 26, 2023 ## Browse Entries by Type @@ -721,3 +713,18 @@ from one *Object*/*Collection* to another, the PermID of the old and new *Objects*/*Collections* are shown in the history table. Updated on November 10, 2022 + +## Spreadsheet + +The spreadsheet component needs to be enabled by a group admin or lab manager who can edit the ELN Settings, as described here: [Enable Rich Text Editor or Spreadsheet Widgets](../general-admin-users/admins-documentation/new-entity-type-registration.md) + + + +The spreadsheet supports some basic Excel functionalities, such as mathematical formulas (e.g. =SUM(A1+A2)). +It is possible to import an openBIS Object into the spreadsheet, with the **import** button, on the spreadsheet itself: + + + +Please note that if the Object is updated in openBIS, it will NOT be automatically updated in the spreadsheet. + +Updated on March 4, 2022 diff --git a/docs/user-documentation/general-users/data-upload.md b/docs/user-documentation/general-users/data-upload.md index 8809b54d753baf9bca6a01d224c009c4d78dfdce..1532b00aed0ec3c61448b17b494ec2ec1d775513 100644 --- a/docs/user-documentation/general-users/data-upload.md +++ b/docs/user-documentation/general-users/data-upload.md @@ -116,7 +116,7 @@ on the eln-lims-dropbox folder.  -## Dropbox with markerfile +### Dropbox with markerfile  @@ -149,7 +149,7 @@ The marker file should be named:  -### **How to create the Marker file in Windows** +#### **How to create the Marker file in Windows**  @@ -166,7 +166,7 @@ You can create the Marker file in Windows using a text editor such as  -### **How to create the Marker file on Mac** +#### **How to create the Marker file on Mac**  @@ -191,7 +191,7 @@ other text editor will also work.  -## Dropbox monitor +### Dropbox monitor  @@ -223,7 +223,7 @@ the log with the error is shown.  -## Registration of metadata for datasets via dropbox +### Registration of metadata for datasets via dropbox  diff --git a/ui-eln-lims/src/core-plugins/eln-lims/1/dss/drop-boxes/eln-lims-dropbox/eln-lims-dropbox.py b/ui-eln-lims/src/core-plugins/eln-lims/1/dss/drop-boxes/eln-lims-dropbox/eln-lims-dropbox.py index 95f3762335386ab4446357123d92637bf0c111b6..129057a1740f1375b96666b234de8c991b7bf399 100644 --- a/ui-eln-lims/src/core-plugins/eln-lims/1/dss/drop-boxes/eln-lims-dropbox/eln-lims-dropbox.py +++ b/ui-eln-lims/src/core-plugins/eln-lims/1/dss/drop-boxes/eln-lims-dropbox/eln-lims-dropbox.py @@ -1,18 +1,16 @@ import re -import uuid - from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions import ExperimentFetchOptions from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id import ExperimentIdentifier from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions import SampleFetchOptions from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id import SampleIdentifier from ch.systemsx.cisd.common.mail import EMailAddress -from ch.systemsx.cisd.openbis.generic.client.web.client.exception import UserFailureException from ch.systemsx.cisd.openbis.dss.generic.shared import ServiceProvider +from ch.systemsx.cisd.openbis.generic.client.web.client.exception import UserFailureException from java.io import File from java.nio.file import Files, Paths, StandardCopyOption from java.util import List -from org.json import JSONObject from org.apache.commons.io import FileUtils +from org.json import JSONObject INVALID_FORMAT_ERROR_MESSAGE = "Invalid format for the folder name, should follow the pattern <ENTITY_KIND>+<SPACE_CODE>+<PROJECT_CODE>+[<EXPERIMENT_CODE>|<SAMPLE_CODE>]+<OPTIONAL_DATASET_TYPE>+<OPTIONAL_NAME>"; ILLEGAL_CHARACTERS_IN_FILE_NAMES_ERROR_MESSAGE = "Directory or its content contain illegal characters: \"', ~, $, %\""; @@ -25,174 +23,184 @@ EXPERIMENT_MISSING_ERROR_MESSAGE = "Experiment not found"; NAME_PROPERTY_SET_IN_TWO_PLACES_ERROR_MESSAGE = "$NAME property specified twice, it should just be in either folder name or metadata.json" EMAIL_SUBJECT = "ELN LIMS Dropbox Error"; ILLEGAL_FILES = ["desktop.ini", "IconCache.db", "thumbs.db"]; -ILLEGAL_FILES_ERROR_MESSAGE = "Directory or contains illegal files: " + str(ILLEGAL_FILES); -HIDDEN_FILES_ERROR_MESSAGE = "Directory or contains hidden files: files starting with '.'"; +ILLEGAL_FILES_ERROR_MESSAGE = "Directory contains illegal files: " + str(ILLEGAL_FILES); +HIDDEN_FILES_ERROR_MESSAGE = "Directory contains hidden files: files starting with '.'"; + +errorMessages = [] def process(transaction): incoming = transaction.getIncoming(); folderName = incoming.getName(); - - if not folderName.startswith('.'): - datasetInfo = folderName.split("+"); - entityKind = None; - sample = None; - experiment = None; - datasetType = None; - name = None; - - # Parse entity Kind - if len(datasetInfo) >= 1: - entityKind = datasetInfo[0]; - else: - raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_ERROR_MESSAGE); - - v3 = ServiceProvider.getV3ApplicationService(); - sessionToken = transaction.getOpenBisServiceSessionToken(); - projectSamplesEnabled = v3.getServerInformation(sessionToken)['project-samples-enabled'] == 'true' - - # Parse entity Kind Format - if entityKind == "O": - if len(datasetInfo) >= 4 and projectSamplesEnabled: - sampleSpace = datasetInfo[1]; - projectCode = datasetInfo[2]; - sampleCode = datasetInfo[3]; - - emailAddress = getSampleRegistratorsEmail(transaction, sampleSpace, projectCode, sampleCode) - sample = transaction.getSample("/" + sampleSpace + "/" + projectCode + "/" + sampleCode); - if sample is None: - reportIssue(transaction, - INVALID_FORMAT_ERROR_MESSAGE + ":" + SAMPLE_MISSING_ERROR_MESSAGE, - None); - if len(datasetInfo) >= 5: - datasetType = datasetInfo[4]; - if len(datasetInfo) >= 6: - name = datasetInfo[5]; - if len(datasetInfo) > 6: - reportIssue(transaction, - INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE, - emailAddress) - elif len(datasetInfo) >= 3 and not projectSamplesEnabled: - sampleSpace = datasetInfo[1]; - sampleCode = datasetInfo[2]; - - emailAddress = getSampleRegistratorsEmail(transaction, sampleSpace, None, sampleCode) - sample = transaction.getSample("/" + sampleSpace + "/" + sampleCode); - if sample is None: - reportIssue(transaction, - INVALID_FORMAT_ERROR_MESSAGE + ":" + SAMPLE_MISSING_ERROR_MESSAGE, - None); + emailAddress = None + + try: + if not folderName.startswith('.'): + datasetInfo = folderName.split("+"); + entityKind = None; + sample = None; + experiment = None; + datasetType = None; + name = None; + + # Parse entity Kind + if len(datasetInfo) >= 1: + entityKind = datasetInfo[0]; + else: + raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_ERROR_MESSAGE); + + v3 = ServiceProvider.getV3ApplicationService(); + sessionToken = transaction.getOpenBisServiceSessionToken(); + projectSamplesEnabled = v3.getServerInformation(sessionToken)['project-samples-enabled'] == 'true' + + # Parse entity Kind Format + if entityKind == "O": + if len(datasetInfo) >= 4 and projectSamplesEnabled: + sampleSpace = datasetInfo[1]; + projectCode = datasetInfo[2]; + sampleCode = datasetInfo[3]; + + emailAddress = getSampleRegistratorsEmail(transaction, sampleSpace, projectCode, sampleCode) + sample = transaction.getSample("/" + sampleSpace + "/" + projectCode + "/" + sampleCode); + if sample is None: + reportIssue(INVALID_FORMAT_ERROR_MESSAGE + ":" + SAMPLE_MISSING_ERROR_MESSAGE) + raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + SAMPLE_MISSING_ERROR_MESSAGE) + if len(datasetInfo) >= 5: + datasetType = datasetInfo[4]; + if len(datasetInfo) >= 6: + name = datasetInfo[5]; + if len(datasetInfo) > 6: + reportIssue(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE) + elif len(datasetInfo) >= 3 and not projectSamplesEnabled: + sampleSpace = datasetInfo[1]; + sampleCode = datasetInfo[2]; + + emailAddress = getSampleRegistratorsEmail(transaction, sampleSpace, None, sampleCode) + sample = transaction.getSample("/" + sampleSpace + "/" + sampleCode); + if sample is None: + reportIssue(INVALID_FORMAT_ERROR_MESSAGE + ":" + SAMPLE_MISSING_ERROR_MESSAGE) + raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + SAMPLE_MISSING_ERROR_MESSAGE) + if len(datasetInfo) >= 4: + datasetType = datasetInfo[3]; + if len(datasetInfo) >= 5: + name = datasetInfo[4]; + if len(datasetInfo) > 5: + reportIssue(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE) + else: + raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE); + + hiddenFiles = getHiddenFiles(incoming) + if hiddenFiles: + reportIssue(HIDDEN_FILES_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE + ":\n" + pathListToStr(hiddenFiles)) + + illegalFiles = getIllegalFiles(incoming) + if illegalFiles: + reportIssue(ILLEGAL_FILES_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE + ":\n" + pathListToStr(illegalFiles)) + + filesWithIllegalCharacters = getFilesWithIllegalCharacters(incoming) + if filesWithIllegalCharacters: + reportIssue(ILLEGAL_CHARACTERS_IN_FILE_NAMES_ERROR_MESSAGE + ":" + + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE + ":\n" + pathListToStr(filesWithIllegalCharacters)) + + readOnlyFiles = getReadOnlyFiles(incoming) + if readOnlyFiles: + reportIssue(FOLDER_CONTAINS_NON_DELETABLE_FILES_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE + ":\n" + pathListToStr(readOnlyFiles)); + if entityKind == "E": if len(datasetInfo) >= 4: - datasetType = datasetInfo[3]; - if len(datasetInfo) >= 5: - name = datasetInfo[4]; - if len(datasetInfo) > 5: - reportIssue(transaction, - INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE, - emailAddress) + experimentSpace = datasetInfo[1]; + projectCode = datasetInfo[2]; + experimentCode = datasetInfo[3]; + + emailAddress = getExperimentRegistratorsEmail(transaction, experimentSpace, projectCode, + experimentCode); + experiment = transaction.getExperiment("/" + experimentSpace + "/" + projectCode + "/" + experimentCode); + if experiment is None: + reportIssue(INVALID_FORMAT_ERROR_MESSAGE + ":" + EXPERIMENT_MISSING_ERROR_MESSAGE) + raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + EXPERIMENT_MISSING_ERROR_MESSAGE) + if len(datasetInfo) >= 5: + datasetType = datasetInfo[4]; + if len(datasetInfo) >= 6: + name = datasetInfo[5]; + if len(datasetInfo) > 6: + reportIssue(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE); + else: + raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE); + + hiddenFiles = getHiddenFiles(incoming) + if hiddenFiles: + reportIssue(HIDDEN_FILES_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE + ":\n" + pathListToStr(hiddenFiles)) + + illegalFiles = getIllegalFiles(incoming) + if illegalFiles: + reportIssue(ILLEGAL_FILES_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE + ":\n" + pathListToStr(illegalFiles)) + + filedWithIllegalCharacters = getFilesWithIllegalCharacters(incoming) + if filedWithIllegalCharacters: + reportIssue(ILLEGAL_CHARACTERS_IN_FILE_NAMES_ERROR_MESSAGE + ":" + + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE + ":\n" + pathListToStr(filedWithIllegalCharacters)) + + readOnlyFiles = getReadOnlyFiles(incoming) + if readOnlyFiles: + reportIssue(FOLDER_CONTAINS_NON_DELETABLE_FILES_ERROR_MESSAGE + ":" + + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE + ":\n" + pathListToStr(readOnlyFiles)) + + # Create dataset + dataSet = None; + if datasetType is not None: # Set type if found + dataSet = transaction.createNewDataSet(datasetType); else: - raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE); - - if hasFolderHiddenFiles(incoming): - reportIssue(transaction, HIDDEN_FILES_ERROR_MESSAGE + ":" - + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE, emailAddress); - if hasFolderIllegalFiles(incoming): - reportIssue(transaction, ILLEGAL_FILES_ERROR_MESSAGE + ":" - + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE, emailAddress); - if hasFolderIllegalCharacters(incoming): - reportIssue(transaction, ILLEGAL_CHARACTERS_IN_FILE_NAMES_ERROR_MESSAGE + ":" - + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE, emailAddress); - if hasFolderReadOnlyFiles(incoming): - reportIssue(transaction, FOLDER_CONTAINS_NON_DELETABLE_FILES_ERROR_MESSAGE + ":" - + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE, emailAddress); - if entityKind == "E": - if len(datasetInfo) >= 4: - experimentSpace = datasetInfo[1]; - projectCode = datasetInfo[2]; - experimentCode = datasetInfo[3]; - - emailAddress = getExperimentRegistratorsEmail(transaction, experimentSpace, projectCode, - experimentCode); - experiment = transaction.getExperiment("/" + experimentSpace + "/" + projectCode + "/" + experimentCode); - if experiment is None: - reportIssue(transaction, - INVALID_FORMAT_ERROR_MESSAGE + ":" + EXPERIMENT_MISSING_ERROR_MESSAGE, - None); - if len(datasetInfo) >= 5: - datasetType = datasetInfo[4]; - if len(datasetInfo) >= 6: - name = datasetInfo[5]; - if len(datasetInfo) > 6: - reportIssue(transaction, - INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE, - emailAddress); + dataSet = transaction.createNewDataSet(); + + if name is not None: + dataSet.setPropertyValue("$NAME", name); # Set name if found + + # Set sample or experiment + if sample is not None: + dataSet.setSample(sample); else: - raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE); - - if hasFolderHiddenFiles(incoming): - reportIssue(transaction, HIDDEN_FILES_ERROR_MESSAGE + ":" - + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE, emailAddress); - if hasFolderIllegalFiles(incoming): - reportIssue(transaction, ILLEGAL_FILES_ERROR_MESSAGE + ":" - + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE, emailAddress); - if hasFolderIllegalCharacters(incoming): - reportIssue(transaction, ILLEGAL_CHARACTERS_IN_FILE_NAMES_ERROR_MESSAGE + ":" - + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE, emailAddress); - if hasFolderReadOnlyFiles(incoming): - reportIssue(transaction, FOLDER_CONTAINS_NON_DELETABLE_FILES_ERROR_MESSAGE + ":" - + FAILED_TO_PARSE_EXPERIMENT_ERROR_MESSAGE, emailAddress); - - # Create dataset - dataSet = None; - if datasetType is not None: # Set type if found - dataSet = transaction.createNewDataSet(datasetType); - else: - dataSet = transaction.createNewDataSet(); - - if name is not None: - dataSet.setPropertyValue("$NAME", name); # Set name if found - - # Set sample or experiment - if sample is not None: - dataSet.setSample(sample); - else: - dataSet.setExperiment(experiment); - - # Move folder to dataset - filesInFolder = incoming.listFiles(); - - itemsInFolder = 0; - datasetItem = None; - for item in filesInFolder: - fileName = item.getName() - if fileName == "metadata.json": - root = JSONObject(FileUtils.readFileToString(item, "UTF-8")) - properties = root.get("properties") - for propertyKey in properties.keys(): - if propertyKey == "$NAME" and name is not None: - raise UserFailureException(NAME_PROPERTY_SET_IN_TWO_PLACES_ERROR_MESSAGE) - propertyValue = properties.get(propertyKey) - if propertyValue is not None: - propertyValueString = str(propertyValue) - dataSet.setPropertyValue(propertyKey, propertyValueString) + dataSet.setExperiment(experiment); + + # Move folder to dataset + filesInFolder = incoming.listFiles(); + + itemsInFolder = 0; + datasetItem = None; + for item in filesInFolder: + fileName = item.getName() + if fileName == "metadata.json": + root = JSONObject(FileUtils.readFileToString(item, "UTF-8")) + properties = root.get("properties") + for propertyKey in properties.keys(): + if propertyKey == "$NAME" and name is not None: + raise UserFailureException(NAME_PROPERTY_SET_IN_TWO_PLACES_ERROR_MESSAGE) + propertyValue = properties.get(propertyKey) + if propertyValue is not None: + propertyValueString = str(propertyValue) + dataSet.setPropertyValue(propertyKey, propertyValueString) + else: + itemsInFolder = itemsInFolder + 1; + datasetItem = item; + + if itemsInFolder > 1: + tmpPath = incoming.getAbsolutePath() + "/default"; + tmpDir = File(tmpPath); + tmpDir.mkdir(); + + try: + for inputFile in filesInFolder: + Files.move(inputFile.toPath(), Paths.get(tmpPath, inputFile.getName()), + StandardCopyOption.ATOMIC_MOVE); + transaction.moveFile(tmpDir.getAbsolutePath(), dataSet); + finally: + if tmpDir is not None: + tmpDir.delete(); else: - itemsInFolder = itemsInFolder + 1; - datasetItem = item; - - if itemsInFolder > 1: - tmpPath = incoming.getAbsolutePath() + "/default"; - tmpDir = File(tmpPath); - tmpDir.mkdir(); - - try: - for inputFile in filesInFolder: - Files.move(inputFile.toPath(), Paths.get(tmpPath, inputFile.getName()), - StandardCopyOption.ATOMIC_MOVE); - transaction.moveFile(tmpDir.getAbsolutePath(), dataSet); - finally: - if tmpDir is not None: - tmpDir.delete(); - else: - transaction.moveFile(datasetItem.getAbsolutePath(), dataSet); + transaction.moveFile(datasetItem.getAbsolutePath(), dataSet); + finally: + reportAllIssues(transaction, emailAddress) + + +def pathListToStr(list): + return "\n".join(list) def getContactsEmailAddresses(transaction): @@ -200,62 +208,69 @@ def getContactsEmailAddresses(transaction): return re.split("[,;]", emailString) if emailString is not None else [] -def reportIssue(transaction, errorMessage, emailAddress): - contacts = getContactsEmailAddresses(transaction); - allAddresses = [emailAddress] + contacts if emailAddress is not None else contacts; - sendMail(transaction, map(lambda address: EMailAddress(address), allAddresses), EMAIL_SUBJECT, errorMessage); - raise UserFailureException(errorMessage); +def reportIssue(errorMessage): + errorMessages.append(errorMessage) + + +def reportAllIssues(transaction, emailAddress): + if len(errorMessages) > 0: + contacts = getContactsEmailAddresses(transaction) + allAddresses = [emailAddress] + contacts if emailAddress is not None else contacts + joinedErrorMessages = "\n".join(errorMessages) + sendMail(transaction, map(lambda address: EMailAddress(address), allAddresses), EMAIL_SUBJECT, joinedErrorMessages); + raise UserFailureException(joinedErrorMessages) -def hasFolderIllegalCharacters(incoming): - if bool(re.search(r"['~$%]", incoming.getName())): - return True; +def getFilesWithIllegalCharacters(folder): + result = [] + if bool(re.search(r"['~$%]", folder.getPath())): + result.append(folder.getName()) - files = incoming.listFiles() + files = folder.listFiles() if files is not None: for f in files: - if hasFolderIllegalCharacters(f): - return True; + result.extend(getFilesWithIllegalCharacters(f)) return False; -def hasFolderHiddenFiles(incoming): - if incoming.getName().startswith("."): - return True; +def getHiddenFiles(folder): + result = [] + if folder.getName().startswith("."): + result.append(folder.getPath()) - files = incoming.listFiles() + files = folder.listFiles() if files is not None: for f in files: - if hasFolderHiddenFiles(f): - return True; + result.extend(getHiddenFiles(f)) + + return result - return False; -def hasFolderIllegalFiles(incoming): - if incoming.getName() in ILLEGAL_FILES: - return True; +def getIllegalFiles(folder): + result = [] + if folder.getName() in ILLEGAL_FILES: + result.append(folder.getPath()) - files = incoming.listFiles() + files = folder.listFiles() if files is not None: for f in files: - if hasFolderIllegalFiles(f): - return True; + result.extend(getIllegalFiles(f)) - return False; + return result -def hasFolderReadOnlyFiles(incoming): - if not incoming.renameTo(incoming): - return True; +def getReadOnlyFiles(folder): + result = [] + if not folder.renameTo(folder): + result.append(folder.getPath()) - files = incoming.listFiles() + files = folder.listFiles() if files is not None: for f in files: - if hasFolderReadOnlyFiles(f): - return True; + result.extend(getReadOnlyFiles(f)) - return False; + return result def sendMail(transaction, emailAddresses, subject, body):