-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/epmc batches #56
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 all commits
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,7 +8,9 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from ftplib import FTP | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BATCH_SIZE = 5000 # number of links per XML file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def setup_logging(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -28,8 +30,7 @@ def read_tsv(file_path: str) -> List[Dict[str, str]]: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def build_xml(data: List[Dict[str, str]]) -> ET.Element: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Build the XML structure from the TSV data.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.info("Building XML structure") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Build XML for a batch of TSV rows.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| root = ET.Element("links") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for row in data: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| emdb_id = row["EMDB_ID"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -49,12 +50,10 @@ def build_xml(data: List[Dict[str, str]]) -> ET.Element: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def prettify_xml(elem: ET.Element) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Return a pretty-printed XML string for the Element.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return minidom.parseString(ET.tostring(elem, 'utf-8')).toprettyxml(indent=" ") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def write_xml(xml_root: ET.Element, output_file: str) -> None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def write_xml(xml_root: ET.Element, output_file: str) -> None: | |
| def write_xml(xml_root: ET.Element, output_file: str) -> None: | |
| """Write the XML tree to a file with pretty formatting.""" |
Copilot
AI
Nov 14, 2025
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.
The comprehensive docstring was removed from this function. Given that this function has multiple parameters with specific purposes and interacts with external FTP servers, it would be beneficial to restore the docstring to help future maintainers understand the function's purpose, parameters, and behavior.
| ) -> None: | |
| ) -> None: | |
| """ | |
| Upload a local file to a remote FTP server. | |
| Connects to the specified FTP server using the provided credentials, | |
| changes to the specified remote directory, and uploads the given local file. | |
| Parameters: | |
| server (str): The FTP server address. | |
| username (str): Username for FTP authentication. | |
| password (str): Password for FTP authentication. | |
| local_file_path (str): Path to the local file to upload. | |
| remote_dir (str, optional): Remote directory to upload the file to. Defaults to ".". | |
| remote_filename (str, optional): Name to use for the file on the server. Defaults to the local file's basename. | |
| Returns: | |
| None | |
| Logs errors if the local file does not exist or if the upload fails. | |
| """ |
Copilot
AI
Nov 14, 2025
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.
The function is missing a return type hint. For consistency with other functions in this file (which use type hints), consider adding -> Generator[List[Dict[str, str]], None, None] as the return type. You'll also need to import Generator from the typing module.
Copilot
AI
Nov 14, 2025
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.
The output directory is not validated or created before use. If the directory doesn't exist, the script will fail when attempting to write the first batch XML file. Consider adding directory creation logic (e.g., os.makedirs(output_dir, exist_ok=True)) before the batch processing loop.
| output_dir = "/hps/nobackup/gerard/emdb/annotations/output/EPMC" | |
| output_dir = "/hps/nobackup/gerard/emdb/annotations/output/EPMC" | |
| os.makedirs(output_dir, exist_ok=True) |
Copilot
AI
Nov 14, 2025
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.
The FTP connection is established and torn down for each batch. For better performance, consider establishing a single FTP connection before the loop and reusing it for all uploads, then closing it after all batches are processed. This would significantly reduce connection overhead, especially when processing many batches.
Copilot
AI
Nov 14, 2025
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.
The batch processing loop lacks error handling. If an exception occurs during XML generation, file writing, or FTP upload for any batch, the entire script will fail and subsequent batches won't be processed. Consider adding try-except blocks around the batch processing operations to log failures and continue with remaining batches, or at minimum, provide clear error context about which batch failed.
| xml_root = build_xml(batch) | |
| output_file = os.path.join( | |
| output_dir, | |
| f"EMDB_linkFile_providerID_2057_part{part}.xml" | |
| ) | |
| write_xml(xml_root, output_file) | |
| upload_file_via_ftp( | |
| server=ftp_server, | |
| username=ftp_user, | |
| password=ftp_pass, | |
| local_file_path=output_file, | |
| remote_dir=ftp_dir | |
| ) | |
| logging.info(f"Batch {part} completed.") | |
| part += 1 | |
| try: | |
| xml_root = build_xml(batch) | |
| output_file = os.path.join( | |
| output_dir, | |
| f"EMDB_linkFile_providerID_2057_part{part}.xml" | |
| ) | |
| write_xml(xml_root, output_file) | |
| upload_file_via_ftp( | |
| server=ftp_server, | |
| username=ftp_user, | |
| password=ftp_pass, | |
| local_file_path=output_file, | |
| remote_dir=ftp_dir | |
| ) | |
| logging.info(f"Batch {part} completed.") | |
| except Exception as e: | |
| logging.error(f"Error processing batch {part}: {e}", exc_info=True) | |
| part += 1 |
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.
The docstring was removed from this function. While the function is simple, it's a best practice to maintain docstrings for all functions, especially in a codebase that previously had them. Consider adding a brief docstring like
"""Return a pretty-printed XML string for the Element."""