diff --git a/src/aind_data_transfer_service/__init__.py b/src/aind_data_transfer_service/__init__.py index 2715595..efa0c96 100644 --- a/src/aind_data_transfer_service/__init__.py +++ b/src/aind_data_transfer_service/__init__.py @@ -1,7 +1,7 @@ """Init package""" import os -__version__ = "0.9.1" +__version__ = "0.9.5" # Global constants OPEN_DATA_BUCKET_NAME = os.getenv("OPEN_DATA_BUCKET_NAME", "open") diff --git a/src/aind_data_transfer_service/configs/job_upload_template.py b/src/aind_data_transfer_service/configs/job_upload_template.py index 867721b..f49c946 100644 --- a/src/aind_data_transfer_service/configs/job_upload_template.py +++ b/src/aind_data_transfer_service/configs/job_upload_template.py @@ -2,7 +2,8 @@ import datetime from io import BytesIO -from aind_data_schema.core.data_description import Modality, Platform +from aind_data_schema.models.modalities import Modality +from aind_data_schema.models.platforms import Platform from openpyxl import Workbook from openpyxl.styles import Font from openpyxl.utils import get_column_letter @@ -14,10 +15,13 @@ class JobUploadTemplate: """Class to configure and create xlsx job upload template""" FILE_NAME = "job_upload_template.xlsx" + NUM_TEMPLATE_ROWS = 20 + XLSX_DATETIME_FORMAT = "YYYY-MM-DDTHH:mm:ss" HEADERS = [ "platform", "acq_datetime", "subject_id", + "metadata_dir", "modality0", "modality0.source", "modality1", @@ -28,6 +32,7 @@ class JobUploadTemplate: Platform.BEHAVIOR.abbreviation, datetime.datetime(2023, 10, 4, 4, 0, 0), "123456", + "/allen/aind/stage/fake/metadata_dir", Modality.BEHAVIOR_VIDEOS.abbreviation, "/allen/aind/stage/fake/dir", Modality.BEHAVIOR.abbreviation, @@ -37,6 +42,7 @@ class JobUploadTemplate: Platform.SMARTSPIM.abbreviation, datetime.datetime(2023, 3, 4, 16, 30, 0), "654321", + "/allen/aind/stage/fake/Config", Modality.SPIM.abbreviation, "/allen/aind/stage/fake/dir", ], @@ -44,6 +50,7 @@ class JobUploadTemplate: Platform.ECEPHYS.abbreviation, datetime.datetime(2023, 1, 30, 19, 1, 0), "654321", + None, Modality.ECEPHYS.abbreviation, "/allen/aind/stage/fake/dir", Modality.BEHAVIOR_VIDEOS.abbreviation, @@ -53,13 +60,23 @@ class JobUploadTemplate: VALIDATORS = [ { "name": "platform", + "type": "list", "options": [p().abbreviation for p in Platform._ALL], - "ranges": ["A2:A20"], + "column_indexes": [HEADERS.index("platform")], }, { "name": "modality", + "type": "list", "options": [m().abbreviation for m in Modality._ALL], - "ranges": ["E2:E20", "G2:G20"], + "column_indexes": [ + HEADERS.index("modality0"), + HEADERS.index("modality1"), + ], + }, + { + "name": "datetime", + "type": "date", + "column_indexes": [HEADERS.index("acq_datetime")], }, ] @@ -69,33 +86,47 @@ def create_job_template(): # job template xl_io = BytesIO() workbook = Workbook() + workbook.iso_dates = True worksheet = workbook.active worksheet.append(JobUploadTemplate.HEADERS) for job in JobUploadTemplate.SAMPLE_JOBS: worksheet.append(job) # data validators for validator in JobUploadTemplate.VALIDATORS: - dv = DataValidation( - type="list", - formula1=f'"{(",").join(validator["options"])}"', - allow_blank=True, - showErrorMessage=True, - showInputMessage=True, - ) - dv.promptTitle = validator["name"] - dv.prompt = f'Select a {validator["name"]} from the dropdown' - dv.error = f'Invalid {validator["name"]}.' - for r in validator["ranges"]: - dv.add(r) + dv_type = validator["type"] + dv_name = validator["name"] + dv_params = { + "type": dv_type, + "promptTitle": dv_name, + "error": f"Invalid {dv_name}.", + "allow_blank": True, + "showErrorMessage": True, + "showInputMessage": True, + } + if dv_type == "list": + dv_params["formula1"] = f'"{(",").join(validator["options"])}"' + dv_params["prompt"] = f"Select a {dv_name} from the dropdown" + elif dv_type == "date": + dv_params["prompt"] = "Provide a {} using {}".format( + dv_name, JobUploadTemplate.XLSX_DATETIME_FORMAT + ) + dv = DataValidation(**dv_params) + for i in validator["column_indexes"]: + col = get_column_letter(i + 1) + col_range = ( + f"{col}2:{col}{JobUploadTemplate.NUM_TEMPLATE_ROWS}" + ) + dv.add(col_range) + if dv_type != "date": + continue + for (cell,) in worksheet[col_range]: + cell.number_format = JobUploadTemplate.XLSX_DATETIME_FORMAT worksheet.add_data_validation(dv) # formatting bold = Font(bold=True) - for header in worksheet["A1:G1"]: - for cell in header: - cell.font = bold - worksheet.column_dimensions[ - get_column_letter(cell.column) - ].auto_size = True + for cell in worksheet[1]: + cell.font = bold + worksheet.column_dimensions[cell.column_letter].auto_size = True # save file workbook.save(xl_io) workbook.close() diff --git a/src/aind_data_transfer_service/server.py b/src/aind_data_transfer_service/server.py index e0c535e..26c871d 100644 --- a/src/aind_data_transfer_service/server.py +++ b/src/aind_data_transfer_service/server.py @@ -68,12 +68,15 @@ async def validate_csv(request: Request): xlsx_sheet = xlsx_book.active csv_io = io.StringIO() csv_writer = csv.writer(csv_io) - for r in xlsx_sheet.rows: - csv_writer.writerow([cell.value for cell in r]) + for r in xlsx_sheet.iter_rows(values_only=True): + if any(r): + csv_writer.writerow(r) xlsx_book.close() data = csv_io.getvalue() csv_reader = csv.DictReader(io.StringIO(data)) for row in csv_reader: + if not any(row.values()): + continue try: job = BasicUploadJobConfigs.from_csv_row(row=row) # Construct hpc job setting most of the vars from the env diff --git a/src/aind_data_transfer_service/templates/index.html b/src/aind_data_transfer_service/templates/index.html index 4a5b3ac..fcb07ac 100644 --- a/src/aind_data_transfer_service/templates/index.html +++ b/src/aind_data_transfer_service/templates/index.html @@ -142,7 +142,7 @@