-
Notifications
You must be signed in to change notification settings - Fork 4
VED-227- FHIR-FLAT-JSON #396
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
bf855ea
849e0c8
ad31467
8d84c0e
ddb3b09
81720ac
f7c9fdd
ee77b77
ca8fbf8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -110,99 +110,35 @@ def _log_error(self, fieldName, fieldValue, e, code=ExceptionMessages.RECORD_CHE | |
| }) | ||
|
|
||
| def _convertToDate(self, expressionRule, fieldName, fieldValue, summarise, report_unexpected_exception): | ||
| if not fieldValue: | ||
| return "" | ||
|
|
||
| if not isinstance(fieldValue, str): | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, "Value is not a string") | ||
| return "" | ||
|
|
||
| # Normalize expression rule | ||
| format_str = expressionRule.replace("format:", "").strip() | ||
|
|
||
| # Reject partial ISO dates like "2024" or "2024-05" | ||
| if format_str == "%Y%m%d" and re.match(r"^\d{4}(-\d{2})?$", fieldValue): | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, "Partial date not accepted") | ||
| return "" | ||
|
|
||
| # Handle only the recorded field with extended ISO + timezone support | ||
| if fieldName == "recorded": | ||
| # Accept "YYYY-MM-DD" and return as is | ||
| if re.match(r"^\d{4}-\d{2}-\d{2}$", fieldValue): | ||
| try: | ||
| dt = datetime.strptime(fieldValue, "%Y-%m-%d") | ||
| if dt.date() > datetime.now(ZoneInfo("UTC")).date(): | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, "Date cannot be in the future") | ||
| return "" | ||
| return fieldValue | ||
| except ValueError: | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, "Invalid date format") | ||
| return "" | ||
| try: | ||
| # Parse ISO format with or without microseconds and TZ | ||
| dt = datetime.fromisoformat(fieldValue) | ||
| except ValueError: | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, "Invalid date format") | ||
| return "" | ||
|
|
||
| # Assign UTC if tzinfo is missing | ||
| if dt.tzinfo is None: | ||
| dt = dt.replace(tzinfo=ZoneInfo("UTC")) | ||
|
|
||
| now_utc = datetime.now(ZoneInfo("UTC")) | ||
| if dt.astimezone(ZoneInfo("UTC")) > now_utc: | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, "Date cannot be in the future") | ||
| return "" | ||
|
|
||
| # Validate timezone offset | ||
| offset = dt.utcoffset() | ||
| allowed_offsets = [ | ||
| ZoneInfo("UTC").utcoffset(dt), | ||
| ZoneInfo("Europe/London").utcoffset(dt), | ||
| ] | ||
|
|
||
| if offset not in allowed_offsets: | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, f"Unsupported offset: {offset}") | ||
| """ | ||
| Convert a date string according to match YYYYMMDD format. | ||
| """ | ||
| if not fieldValue: | ||
| return "" | ||
|
|
||
| dt_utc = dt.astimezone(ZoneInfo("UTC")).replace(microsecond=0) | ||
|
|
||
| # Format and return with custom suffix | ||
| formatted = dt_utc.strftime("%Y%m%dT%H%M%S%z") | ||
| return formatted.replace("+0000", "00").replace("+0100", "01") | ||
|
|
||
| # For all other fields, apply standard %Y%m%d processing | ||
| if format_str == "%Y%m%d": | ||
| fieldValue = fieldValue.replace("-", "").replace("/", "") | ||
| # Validate expected raw input format if using %Y%m%d | ||
| if not re.match(r"^\d{8}$", fieldValue): | ||
| # 1. Data type must be a string | ||
| if not isinstance(fieldValue, str): | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, "Date must be in YYYYMMDD format") | ||
| self._log_error(fieldName, fieldValue, "Value is not a string") | ||
| return "" | ||
|
|
||
| try: | ||
| dt = datetime.strptime(fieldValue, format_str) | ||
|
|
||
| # Reject future dates if the field is BirthDate | ||
| if fieldName in "contained|#:Patient|birthDate": | ||
| today_utc = datetime.now(ZoneInfo("UTC")).date() | ||
| if dt.date() > today_utc: | ||
| # 2. Use Expression Rule Format to parse the date, remove dashes and slashes | ||
| if expressionRule == "%Y%m%d": | ||
| fieldValue = fieldValue.split("T")[0] | ||
| fieldValue = fieldValue.replace("-", "").replace("/", "") | ||
| if not re.match(r"^\d{8}$", fieldValue): | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, "Birthdate cannot be in the future") | ||
| self._log_error(fieldName, fieldValue, "Date must be in YYYYMMDD format") | ||
| return "" | ||
|
|
||
| return dt.strftime(format_str) | ||
| except ValueError as e: | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, e) | ||
| return "" | ||
| try: | ||
| # Converts raw fieldvalue without delimiters to a date-time object | ||
| dt = datetime.strptime(fieldValue, expressionRule) | ||
nhsdevws marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return dt.strftime(expressionRule) | ||
| except ValueError as e: | ||
| # 5. Unexpected parsing errors | ||
| if report_unexpected_exception: | ||
| self._log_error(fieldName, fieldValue, e) | ||
| return "" | ||
|
|
||
| # Convert FHIR datetime into CSV-safe UTC format | ||
| def _convertToDateTime(self, expressionRule, fieldName, fieldValue, summarise, report_unexpected_exception): | ||
|
|
@@ -246,6 +182,8 @@ def _convertToDateTime(self, expressionRule, fieldName, fieldValue, summarise, r | |
|
|
||
| # Not Empty Validate - Returns exactly what is in the extracted fields no parsing or logic needed | ||
| def _convertToNotEmpty(self, expressionRule, fieldName, fieldValue, summarise, report_unexpected_exception): | ||
| if not fieldValue: | ||
|
||
| return "" | ||
| try: | ||
| if isinstance(fieldValue, str) and fieldValue.strip(): | ||
| return fieldValue | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As below, it seems odd that we only handle
YYYYMMDD,YYYY-MM-DDorYYYY-MM-DDT...here. Why not just parse any valid ISO formatted date / datetime? Will check with PaulThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E.g. what about partial dates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorted