diff --git a/pyproject.toml b/pyproject.toml index 0156c2b38..435851f2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ preview = true [tool.ruff.lint] ignore = ["E202", "E203", "E221", "E241", "E251", "E272"] -select = ["E", "EXE", "F", "I", "N", "RUF", "UP", "W"] +select = ["DTZ", "E", "EXE", "F", "I", "N", "RUF", "UP", "W"] [tool.ruff.lint.pycodestyle] max-doc-length = 100 diff --git a/python/lib/database_lib/mri_scanner.py b/python/lib/database_lib/mri_scanner.py index c3dd1bbff..ee7543d98 100644 --- a/python/lib/database_lib/mri_scanner.py +++ b/python/lib/database_lib/mri_scanner.py @@ -1,6 +1,6 @@ """This class performs database queries for the mri_scanner table""" -import datetime +from datetime import UTC, datetime from typing_extensions import deprecated @@ -114,8 +114,8 @@ def register_new_scanner(self, manufacturer, software_version, serial_number, sc 'UserID', 'Entity_type', 'RegistrationProjectID', 'Date_registered', ) values = ( - new_cand_id, 'scanner', center_id, datetime.datetime.now(), - 'imaging.py', 'Scanner', project_id, datetime.datetime.now() + new_cand_id, 'scanner', center_id, datetime.now(UTC), + 'imaging.py', 'Scanner', project_id, datetime.now(UTC), ) candidate_id = self.db.insert( diff --git a/python/lib/database_lib/notification.py b/python/lib/database_lib/notification.py index 6af7568e1..cac6c0f31 100644 --- a/python/lib/database_lib/notification.py +++ b/python/lib/database_lib/notification.py @@ -1,6 +1,6 @@ """This class performs database queries for the notification_spool table""" -import datetime +from datetime import UTC, datetime from typing_extensions import deprecated @@ -76,7 +76,7 @@ def write_to_notification_spool(self, message, is_error, is_verbose, center_id=N 'ProcessID', 'Error', 'Verbose' ) values = ( - type_id, datetime.datetime.now(), message, self.notification_origin, + type_id, datetime.now(UTC), message, self.notification_origin, self.process_id, is_error, is_verbose ) diff --git a/python/lib/db/decorators/int_datetime.py b/python/lib/db/decorators/int_datetime.py index 7ef2a7eb1..fdecc88d5 100644 --- a/python/lib/db/decorators/int_datetime.py +++ b/python/lib/db/decorators/int_datetime.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from sqlalchemy import Integer from sqlalchemy.engine import Dialect @@ -24,4 +24,4 @@ def process_result_value(self, value: int | None | None, dialect: Dialect) -> da if value is None: return None - return datetime.fromtimestamp(value) + return datetime.fromtimestamp(value, tz=UTC) diff --git a/python/lib/dcm2bids_imaging_pipeline_lib/nifti_insertion_pipeline.py b/python/lib/dcm2bids_imaging_pipeline_lib/nifti_insertion_pipeline.py index c9a4d75a4..a19c76216 100644 --- a/python/lib/dcm2bids_imaging_pipeline_lib/nifti_insertion_pipeline.py +++ b/python/lib/dcm2bids_imaging_pipeline_lib/nifti_insertion_pipeline.py @@ -1,11 +1,12 @@ -import datetime import json import os import re import subprocess import sys +from datetime import UTC, datetime from pathlib import Path +import dateutil from loris_bids_importer.file_type import get_check_bids_imaging_file_type_from_extension from loris_bids_importer.mri.sidecar import add_bids_mri_sidecar_file_parameters, get_bids_mri_sidecar_session_info from loris_bids_utils.mri.sidecar import BidsMriSidecarJsonFile @@ -635,7 +636,7 @@ def _register_violations_log(self, violations_list, file_rel_path): scan_param = self.json_file_dict phase_enc_dir = scan_param['PhaseEncodingDirection'] if 'PhaseEncodingDirection' in scan_param.keys() else None base_info_dict = { - 'TimeRun': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'TimeRun': datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S'), 'SeriesUID': scan_param['SeriesInstanceUID'] if 'SeriesInstanceUID' in scan_param.keys() else None, 'TarchiveID': self.dicom_archive.id, 'MincFile': file_rel_path, @@ -666,9 +667,7 @@ def _register_into_files_and_parameter_file(self, nifti_rel_path): scan_param = self.json_file_dict acquisition_date = None if "AcquisitionDateTime" in scan_param.keys(): - acquisition_date = datetime.datetime.strptime( - scan_param['AcquisitionDateTime'], '%Y-%m-%dT%H:%M:%S.%f' - ).date() + acquisition_date = dateutil.parser.parse(scan_param['AcquisitionDateTime']).date() file_type = get_check_bids_imaging_file_type_from_extension(self.env, Path(nifti_rel_path)) file = register_mri_file( diff --git a/python/lib/imaging.py b/python/lib/imaging.py index 4f84db8aa..44dc9ac25 100644 --- a/python/lib/imaging.py +++ b/python/lib/imaging.py @@ -1,10 +1,10 @@ """This class performs database queries and common imaging checks (MRI...)""" -import datetime import json import os import re import tarfile +from datetime import UTC, datetime import nibabel as nib from loris_utils.crypto import compute_file_blake2b_hash @@ -184,7 +184,7 @@ def insert_parameter_file(self, file_id, parameter_name, value): 'ParameterTypeID': param_type_id, 'FileID': file_id, 'Value': value, - 'InsertTime': datetime.datetime.now().timestamp() + 'InsertTime': datetime.now(UTC).timestamp() } pf_entry = self.param_file_db_obj.get_parameter_file_for_file_id_param_type_id(file_id, param_type_id) @@ -222,7 +222,7 @@ def insert_mri_candidate_errors(self, patient_name, tarchive_id, scan_param, fil return info_to_insert_dict = { - "TimeRun": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "TimeRun": datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S'), "SeriesUID": series_uid, "TarchiveID": tarchive_id, "MincFile": file_rel_path, @@ -277,7 +277,7 @@ def insert_protocol_violated_scan(self, patient_name, cand_id, psc_id, tarchive_ "CandidateID": candidate_id, "PSCID": psc_id, "TarchiveID": tarchive_id, - "time_run": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + "time_run": datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S'), "series_description": scan_param["SeriesDescription"] if "SeriesDescription" in scan_param.keys() else None, "minc_location": file_rel_path, "PatientName": patient_name, diff --git a/python/lib/imaging_io.py b/python/lib/imaging_io.py index 141615008..09b3d21fe 100644 --- a/python/lib/imaging_io.py +++ b/python/lib/imaging_io.py @@ -1,9 +1,9 @@ -import datetime import os import shutil import sys import tarfile import tempfile +from datetime import UTC, datetime from typing_extensions import deprecated @@ -27,7 +27,7 @@ def extract_archive(self, location, prefix, tmp_dir): :rtype: str """ - now = datetime.datetime.now() + now = datetime.now(UTC) upload_prefix = f'{prefix}_DIR_{now.strftime("%Y-%m-%d_%Hh%Mm%Ss")}_' extract_location = tempfile.mkdtemp(prefix=upload_prefix, dir=tmp_dir) tar_file = tarfile.open(location) diff --git a/python/lib/imaging_lib/file.py b/python/lib/imaging_lib/file.py index ed754dd39..6f2e60f66 100644 --- a/python/lib/imaging_lib/file.py +++ b/python/lib/imaging_lib/file.py @@ -1,5 +1,5 @@ import getpass -from datetime import date, datetime +from datetime import UTC, date, datetime from pathlib import Path from lib.db.models.dicom_archive import DbDicomArchive @@ -31,7 +31,7 @@ def register_mri_file( """ user = getpass.getuser() - time = datetime.now() + time = datetime.now(UTC) file = DbFile( path = file_path, diff --git a/python/lib/imaging_lib/file_parameter.py b/python/lib/imaging_lib/file_parameter.py index 6fefe8fc7..7d5738ed5 100644 --- a/python/lib/imaging_lib/file_parameter.py +++ b/python/lib/imaging_lib/file_parameter.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from typing import Any from lib.db.models.file import DbFile @@ -31,7 +31,7 @@ def register_mri_file_parameter(env: Env, file: DbFile, parameter_name: str, par parameter = try_get_file_parameter_with_file_id_type_id(env.db, file.id, parameter_type.id) if parameter is None: - time = datetime.now() + time = datetime.now(UTC) parameter = DbFileParameter( type_id = parameter_type.id, diff --git a/python/lib/imaging_lib/mri_scanner.py b/python/lib/imaging_lib/mri_scanner.py index 513f15fca..60c29eecc 100644 --- a/python/lib/imaging_lib/mri_scanner.py +++ b/python/lib/imaging_lib/mri_scanner.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from datetime import datetime +from datetime import UTC, datetime from lib.candidate import generate_new_cand_id from lib.db.models.candidate import DbCandidate @@ -43,7 +43,7 @@ def get_or_create_scanner( return mri_scanner cand_id = generate_new_cand_id(env) - now = datetime.now() + now = datetime.now(UTC) candidate = DbCandidate( cand_id = cand_id, diff --git a/python/lib/logging.py b/python/lib/logging.py index 1995666b2..d48408d01 100644 --- a/python/lib/logging.py +++ b/python/lib/logging.py @@ -1,5 +1,5 @@ import sys -from datetime import datetime +from datetime import UTC, datetime from typing import Never from lib.db.models.notification_spool import DbNotificationSpool @@ -80,7 +80,7 @@ def register_notification(env: Env, message: str, is_error: bool, is_verbose: bo notification = DbNotificationSpool( type_id = env.notifier.type_id, - time_spooled = datetime.now(), + time_spooled = datetime.now(UTC), message = message, origin = env.notifier.origin, process_id = env.notifier.process_id, diff --git a/python/lib/make_env.py b/python/lib/make_env.py index 854f79b7c..5d574ac6f 100644 --- a/python/lib/make_env.py +++ b/python/lib/make_env.py @@ -1,6 +1,6 @@ import sys import tempfile -from datetime import datetime +from datetime import UTC, datetime from pathlib import Path from typing import Any, cast @@ -104,7 +104,7 @@ def create_script_tmp_dir(script_name: str) -> Path: env_tmp_dir = tempfile.gettempdir() # Create a recognizable temporary directory name for this pipeline. - date_string = datetime.now().strftime('%Y-%m-%d_%Hh%Mm%Ss_') + date_string = datetime.now(UTC).strftime('%Y-%m-%d_%Hh%Mm%Ss_') tmp_dir_prefix = f'{script_name}_{date_string}' # Create and return the pipeline temporary directory. diff --git a/python/lib/physio/channels.py b/python/lib/physio/channels.py index c40ad0026..bf833f8b6 100644 --- a/python/lib/physio/channels.py +++ b/python/lib/physio/channels.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from decimal import Decimal from pathlib import Path @@ -36,7 +36,7 @@ def insert_physio_channel( file_path = file_path, channel_type_id = channel_type.id, status_type_id = status_type.id if status_type is not None else None, - insert_time = datetime.now(), + insert_time = datetime.now(UTC), name = name, description = description, sampling_frequency = sampling_frequency, diff --git a/python/lib/physio/events.py b/python/lib/physio/events.py index 6b4ee1cac..3bf652310 100644 --- a/python/lib/physio/events.py +++ b/python/lib/physio/events.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from datetime import datetime +from datetime import UTC, datetime from decimal import Decimal from pathlib import Path from typing import Any @@ -239,7 +239,7 @@ def insert_physio_task_event( event_task_file = DbPhysioTaskEvent( physio_file_id = physio_file.id, event_file_id = events_file.id, - insert_time = datetime.now(), + insert_time = datetime.now(UTC), onset = onset, duration = duration, event_code = event_code, diff --git a/python/lib/utilities.py b/python/lib/utilities.py index 4666ef02b..d6a894aa3 100644 --- a/python/lib/utilities.py +++ b/python/lib/utilities.py @@ -7,7 +7,7 @@ import sys import tarfile import tempfile -from datetime import datetime +from datetime import UTC, datetime import loris_utils.crypto import mat73 @@ -250,7 +250,7 @@ def create_processing_tmp_dir(template_prefix): env_tmp_dir = os.environ.get("TMPDIR") # append date and time information to the prefix name for the tmp dir - now = datetime.now() + now = datetime.now(UTC) template_prefix += f"_{now.strftime('%Y-%m-%d_%Hh%Mm%Ss_')}" # create the temporary directory and return it diff --git a/python/loris_bids_importer/src/loris_bids_importer/validation/subjects.py b/python/loris_bids_importer/src/loris_bids_importer/validation/subjects.py index 6e2392490..3e0183cbb 100644 --- a/python/loris_bids_importer/src/loris_bids_importer/validation/subjects.py +++ b/python/loris_bids_importer/src/loris_bids_importer/validation/subjects.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from lib.candidate import generate_new_cand_id from lib.db.models.candidate import DbCandidate @@ -103,7 +103,7 @@ def create_bids_candidate(env: Env, participant: BidsParticipantTsvRow) -> DbCan ) ) - now = datetime.now() + now = datetime.now(UTC) candidate = DbCandidate( cand_id = cand_id, diff --git a/python/loris_dicom_importer/src/loris_dicom_importer/dicom_database.py b/python/loris_dicom_importer/src/loris_dicom_importer/dicom_database.py index 17814a2c6..89ea57a7a 100644 --- a/python/loris_dicom_importer/src/loris_dicom_importer/dicom_database.py +++ b/python/loris_dicom_importer/src/loris_dicom_importer/dicom_database.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import UTC, datetime from functools import cmp_to_key from pathlib import Path @@ -26,7 +26,7 @@ def insert_dicom_archive( dicom_archive = DbDicomArchive() populate_dicom_archive(dicom_archive, dicom_summary, dicom_import_log, archive_path) - dicom_archive.date_first_archived = datetime.now() + dicom_archive.date_first_archived = datetime.now(UTC) db.add(dicom_archive) db.commit() insert_files_series(db, dicom_archive, dicom_summary) @@ -75,7 +75,7 @@ def populate_dicom_archive( dicom_archive.center_name = dicom_summary.info.institution or '' dicom_archive.last_update = None dicom_archive.date_acquired = dicom_summary.info.scan_date - dicom_archive.date_last_archived = datetime.now() + dicom_archive.date_last_archived = datetime.now(UTC) dicom_archive.acquisition_count = len(dicom_summary.dicom_series_files) dicom_archive.dicom_file_count = count(flatten(dicom_summary.dicom_series_files.values())) dicom_archive.non_dicom_file_count = len(dicom_summary.other_files) diff --git a/python/loris_dicom_importer/src/loris_dicom_importer/import_log.py b/python/loris_dicom_importer/src/loris_dicom_importer/import_log.py index 069677ef8..40a8bc62d 100644 --- a/python/loris_dicom_importer/src/loris_dicom_importer/import_log.py +++ b/python/loris_dicom_importer/src/loris_dicom_importer/import_log.py @@ -2,7 +2,7 @@ import os import socket from dataclasses import dataclass -from datetime import datetime +from datetime import UTC, datetime from pathlib import Path from loris_dicom_importer.text_dict import DictWriter @@ -69,7 +69,7 @@ def make_dicom_study_import_log(source: Path, target: Path, tarball_md5_sum: str socket.gethostname(), os.uname().sysname, getpass.getuser(), - datetime.strftime(datetime.now(), "%Y-%m-%d %H:%M:%S"), + datetime.strftime(datetime.now(UTC), "%Y-%m-%d %H:%M:%S"), 2, 2, tarball_md5_sum, diff --git a/python/loris_dicom_importer/src/loris_dicom_importer/text.py b/python/loris_dicom_importer/src/loris_dicom_importer/text.py index 9a28d8b53..d54736fd8 100644 --- a/python/loris_dicom_importer/src/loris_dicom_importer/text.py +++ b/python/loris_dicom_importer/src/loris_dicom_importer/text.py @@ -42,14 +42,16 @@ def read_date_none(string: str | None): if string is None: return None - return datetime.strptime(string, '%Y-%m-%d').date() + # User-supplied date: no timezone. + return datetime.strptime(string, '%Y-%m-%d').date() # noqa: DTZ007 def read_dicom_date_none(string: str | None): if string is None: return None - return datetime.strptime(string, '%Y%m%d').date() + # User-supplied date: no timezone. + return datetime.strptime(string, '%Y%m%d').date() # noqa: DTZ007 def read_int_none(string: str | None): diff --git a/python/loris_utils/src/loris_utils/fs.py b/python/loris_utils/src/loris_utils/fs.py index 267fe34c1..89f78b1bf 100644 --- a/python/loris_utils/src/loris_utils/fs.py +++ b/python/loris_utils/src/loris_utils/fs.py @@ -12,7 +12,8 @@ def extract_archive(tar_path: str, prefix: str, dir_path: str) -> str: the new directory location. """ - date_string = datetime.now().strftime('%Y-%m-%d_%Hh%Mm%Ss') + # Use system timezone for log file name. + date_string = datetime.now().strftime('%Y-%m-%d_%Hh%Mm%Ss') # noqa: DTZ005 full_prefix = f'{prefix}_DIR_{date_string}_' extract_path = tempfile.mkdtemp(prefix=full_prefix, dir=dir_path) with tarfile.open(tar_path) as tar_file: diff --git a/python/tests/integration/scripts/test_mass_nifti_pic.py b/python/tests/integration/scripts/test_mass_nifti_pic.py index 443684770..9bdc2d2d1 100644 --- a/python/tests/integration/scripts/test_mass_nifti_pic.py +++ b/python/tests/integration/scripts/test_mass_nifti_pic.py @@ -1,5 +1,5 @@ import os -from datetime import datetime +from datetime import UTC, datetime from pathlib import Path from lib.db.models.file import DbFile @@ -127,7 +127,7 @@ def test_running_on_a_text_file(): file_type = 'txt', session_id = 564, output_type = 'native', - insert_time = datetime.now(), + insert_time = datetime.now(UTC), inserted_by_user_id = 'test' ) @@ -175,7 +175,7 @@ def test_successful_run(): file_pic_data = try_get_parameter_value_with_file_id_parameter_name(db, 2, 'check_pic_filename') assert file_pic_data is None - current_time = datetime.now() + current_time = datetime.now(UTC) process = run_integration_script([ 'mass_nifti_pic.py', diff --git a/python/tests/unit/db/query/test_candidate.py b/python/tests/unit/db/query/test_candidate.py index 4e6dc77f0..836a95367 100644 --- a/python/tests/unit/db/query/test_candidate.py +++ b/python/tests/unit/db/query/test_candidate.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from datetime import datetime +from datetime import UTC, datetime import pytest from sqlalchemy.orm import Session as Database @@ -27,7 +27,7 @@ def setup(): registration_project_id = 1, active = True, user_id = 'admin', - test_date = datetime.now(), + test_date = datetime.now(UTC), entity_type = 'human', ) @@ -38,7 +38,7 @@ def setup(): registration_project_id = 1, active = True, user_id = 'admin', - test_date = datetime.now(), + test_date = datetime.now(UTC), entity_type = 'human', )