Skip to content

DOESN'T WORK -- Log file created by default for uploads and downloads #737

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

Draft
wants to merge 40 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9c0f024
log file required unless force-no-log
i-oden Mar 4, 2025
8980d25
remove sys exit
i-oden Mar 4, 2025
828dd8a
Update help info to option
i-oden Mar 4, 2025
1d355c8
black
i-oden Mar 4, 2025
0d983c7
generate file name if put or get is used
i-oden Mar 5, 2025
65bd650
black
i-oden Mar 5, 2025
0df622f
remove some comments
i-oden Mar 5, 2025
19aeac4
error message if both used because I didn't get the mutually exclusiv…
i-oden Mar 5, 2025
082249d
removed one sys exit
i-oden Mar 5, 2025
017fc99
black
i-oden Mar 5, 2025
601285f
remove cloup
i-oden Mar 5, 2025
6e4fb2e
update help message
i-oden Mar 5, 2025
3d4bc8f
Merge branch 'dev' into HMS-2292-should-we-reintroduce-the-log-file-o…
i-oden Mar 6, 2025
eb8aad4
Create folder to store all automatically generated logs
i-oden Mar 6, 2025
a5b87bf
sprintlog
i-oden Mar 6, 2025
22bbb50
new line in warning
i-oden Mar 6, 2025
981fadf
less general exception
i-oden Mar 6, 2025
d536030
lazy logging
i-oden Mar 6, 2025
6dbdc7f
remove sys exit
i-oden Mar 6, 2025
1bbcfa1
black
i-oden Mar 6, 2025
a3e0fb4
prettier
i-oden Mar 6, 2025
add61b7
replace slash and backslash with _
i-oden Mar 7, 2025
c392ebb
black
i-oden Mar 7, 2025
b85321e
mount_dir to staging_dir
i-oden Mar 25, 2025
2c776d0
staging location and staging dir created in data_putter
i-oden Mar 25, 2025
c2a9dae
staging dir is destination moved to datagetter
i-oden Mar 25, 2025
4763fb0
remove duplicate code in base class
i-oden Mar 25, 2025
13c19f5
remove unused options
i-oden Mar 25, 2025
07507e8
remove commented option
i-oden Mar 25, 2025
fdd7c36
too many empty lines
i-oden Mar 25, 2025
2294a8f
remove method check - unneccesary
i-oden Mar 25, 2025
650f728
clarify default log in context object
i-oden Mar 25, 2025
f187ecb
start logging to deault file
i-oden Mar 25, 2025
73c627e
save command to log file as first item
i-oden Mar 26, 2025
93ebdbf
remove imports
i-oden Mar 27, 2025
d47c385
comment regarding command
i-oden Mar 27, 2025
bdfe5b4
comment regarding command
i-oden Mar 27, 2025
972bb15
add utils function for setting up logging to file
i-oden Mar 27, 2025
065577c
logging started, but doesn't log from all modules
i-oden Mar 27, 2025
0b15ae0
force-no-log to work on its own
i-oden Apr 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions SPRINTLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,7 @@ _Empty sprint_
# 2025-02-17 – 2025-02-28

- Remove executable for Ubuntu 20.04 and add one for Ubuntu 22.04 ([#733](https://github.com/ScilifelabDataCentre/dds_cli/pull/733)).

# 2025-03-03 - 2025-03-14

- Generate log file by default ([#737](https://github.com/ScilifelabDataCentre/dds_cli/pull/737))
2 changes: 0 additions & 2 deletions dds_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ class FileSegment:
SEGMENT_SIZE_RAW = 65536 # Size of chunk to read from raw file
SEGMENT_SIZE_CIPHER = SEGMENT_SIZE_RAW + 16 # Size of chunk to read from encrypted file


# Custom styles for questionary
dds_questionary_styles = prompt_toolkit.styles.Style(
[
Expand All @@ -181,7 +180,6 @@ class FileSegment:
# Determine if the user is on an old terminal without proper Unicode support
dds_on_legacy_console = rich.console.detect_legacy_windows()


# Required to make the standalone executables build with PyInstaller work.
if __name__ == "__main__":
from dds_cli.__main__ import dds_main
Expand Down
72 changes: 53 additions & 19 deletions dds_cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# Own modules
import dds_cli
import dds_cli.account_manager
import dds_cli.directory
import dds_cli.unit_manager
import dds_cli.motd_manager
import dds_cli.superadmin_helper
Expand Down Expand Up @@ -107,7 +108,19 @@
@click.option(
"-v", "--verbose", is_flag=True, default=False, help="Print verbose output to the console."
)
@click.option("-l", "--log-file", help="Save a log to a file.", metavar="<filename>")
@click.option("--force-no-log", help="[NOT RECOMMENDED] Do not save logs to a file.", is_flag=True)
@click.option(
"-l",
"--log-file",
help=(
"Save logs to file. Define a custom path with this option. "
"Recommended format: <command>_<date>_<time>.log."
"In the case of opening a support ticket regarding the DDS, attach this file. "
"Note that a log file will be generated by default for uploads and downloads."
),
metavar="<filename>",
required=False,
)
@click.option(
"--no-prompt", is_flag=True, default=False, help="Run without any interactive features."
)
Expand All @@ -121,7 +134,7 @@
help="List the options of any DDS subcommand and its default settings.",
)
@click.pass_context
def dds_main(click_ctx, verbose, log_file, no_prompt, token_path):
def dds_main(click_ctx, verbose, force_no_log, log_file, no_prompt, token_path):
"""SciLifeLab Data Delivery System (DDS) command line interface.

Access token is saved in a .dds_cli_token file in the home directory.
Expand All @@ -138,6 +151,8 @@ def dds_main(click_ctx, verbose, log_file, no_prompt, token_path):
f"[green]Current user:[/] [red]{username}", highlight=False
)

# Create context object and save command to context
click_ctx.obj = {"NO_PROMPT": no_prompt, "TOKEN_PATH": token_path, "COMMAND": sys.argv}
if "--help" not in sys.argv:
# Set the base logger to output DEBUG
LOG.setLevel(logging.DEBUG)
Expand All @@ -153,20 +168,25 @@ def dds_main(click_ctx, verbose, log_file, no_prompt, token_path):
)
)

# Set up logs to a file if we asked for one
if log_file:
log_fh = logging.FileHandler(log_file, encoding="utf-8")
log_fh.setLevel(logging.DEBUG)
log_fh.setFormatter(
logging.Formatter(
fmt="[%(asctime)s] %(name)-15s %(lineno)-5s [%(levelname)-7s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
)
LOG.addHandler(log_fh)
# Do not setup log at default location if log_file is specified
click_ctx.obj.update({"DEFAULT_LOG": False})

# Create context object
click_ctx.obj = {"NO_PROMPT": no_prompt, "TOKEN_PATH": token_path}
if force_no_log:
LOG.warning(
"You have used both the '--log-file' and '--force-no-log' option, you can only use one."
)
sys.exit(1)
else:
file_handler = dds_cli.utils.setup_logging_to_file(filename=log_file)
LOG.addHandler(file_handler)
else:
click_ctx.obj.update({"DEFAULT_LOG": True})
if force_no_log:
LOG.warning(
"You have chosen the '--force-no-log' option; No log will be generated for this run."
)
click_ctx.obj.update({"DEFAULT_LOG": False})


# ************************************************************************************************ #
Expand Down Expand Up @@ -1564,12 +1584,12 @@ def data_group_command(_):
@data_group_command.command(name="put", no_args_is_help=True)
# Options
@click.option(
"--mount-dir",
"-md",
"--staging-location",
"-sl",
required=False,
type=click_pathlib.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True),
help=(
"New directory where the files will be mounted before upload "
"New directory where the files will be staged before upload "
"and any error log files will be saved for a specific upload."
),
)
Expand All @@ -1596,7 +1616,7 @@ def data_group_command(_):
@click.pass_obj
def put_data(
click_ctx,
mount_dir,
staging_location,
project,
source,
source_path_file,
Expand Down Expand Up @@ -1625,9 +1645,21 @@ def put_data(
delivery to finish. To avoid that a delivery fails because of an expired token, we recommend
reauthenticating yourself before uploading data.
"""
# # Setup logging
# # Define staging directory path
# staging_dir: pathlib.Path = pathlib.Path(f"DataDelivery_{dds_cli.timestamp.TimeStamp().timestamp}_{project}_upload")
# if staging_location:
# staging_dir = staging_location / staging_dir
# else:
# staging_dir = pathlib.Path.cwd() / staging_dir

# # Generate staging directory
# stag_dir_obj = dds_cli.directory.DDSDirectory(path=staging_dir, default_log=click_ctx.get("DEFAULT_LOG"), command=click_ctx.get("COMMAND"))

# Run upload
try:
dds_cli.data_putter.put(
mount_dir=mount_dir,
staging_location=staging_location,
project=project,
source=source,
source_path_file=source_path_file,
Expand All @@ -1638,6 +1670,8 @@ def put_data(
no_prompt=click_ctx.get("NO_PROMPT", False),
token_path=click_ctx.get("TOKEN_PATH"),
destination=destination,
default_log=click_ctx.get("DEFAULT_LOG"),
command=click_ctx.get("COMMAND"),
)
except (
dds_cli.exceptions.AuthenticationError,
Expand Down
1 change: 0 additions & 1 deletion dds_cli/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def __init__(
# Initiate DDSBaseClass to authenticate user
super().__init__(
authenticate=authenticate,
method_check=False,
force_renew_token=force_renew_token,
token_path=token_path,
totp=totp,
Expand Down
30 changes: 0 additions & 30 deletions dds_cli/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,8 @@ class DDSBaseClass:
def __init__(
self,
project=None,
dds_directory: pathlib.Path = None,
mount_dir: pathlib.Path = None,
method: str = None,
authenticate: bool = True,
method_check: bool = True,
force_renew_token: bool = False,
totp: str = None,
no_prompt: bool = False,
Expand All @@ -58,37 +55,10 @@ def __init__(
):
"""Initialize Base class for authenticating the user and preparing for DDS action."""
self.project = project
self.method_check = method_check
self.method = method
self.no_prompt = no_prompt
self.token_path = token_path

if self.method_check:
# Get attempted operation e.g. put/ls/rm/get
if self.method not in DDS_METHODS:
raise exceptions.InvalidMethodError(attempted_method=self.method)
LOG.debug("Attempted operation: %s", self.method)

# Use user defined destination if any specified
if self.method in DDS_DIR_REQUIRED_METHODS:
default_dir = pathlib.Path(
f"DataDelivery_{dds_cli.timestamp.TimeStamp().timestamp}_{self.project}_"
f"{'upload' if self.method == 'put' else 'download'}"
)
if mount_dir:
new_directory = mount_dir / default_dir
elif dds_directory:
new_directory = dds_directory
else:
new_directory = pathlib.Path.cwd() / default_dir

self.temporary_directory = new_directory

self.dds_directory = dds_cli.directory.DDSDirectory(path=new_directory)
self.failed_delivery_log = self.dds_directory.directories["LOGS"] / pathlib.Path(
"dds_failed_delivery.json"
)

# Keyboardinterrupt
self.stop_doing = False

Expand Down
11 changes: 10 additions & 1 deletion dds_cli/data_getter.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,19 @@ def __init__(
token_path: str = None,
):
"""Handle actions regarding downloading data."""
# Define staging directory path
staging_dir: pathlib.Path = pathlib.Path.cwd() / pathlib.Path(f"DataDelivery_{dds_cli.timestamp.TimeStamp().timestamp}_{project}_download")
if destination:
staging_dir = destination

# Generate staging directory
self.temporary_directory = staging_dir
self.dds_directory = dds_cli.directory.DDSDirectory(path=staging_dir)
self.failed_delivery_log = self.dds_directory.directories["LOGS"] / pathlib.Path("dds_failed_delivery.json")

# Initiate DDSBaseClass to authenticate user
super().__init__(
project=project,
dds_directory=destination,
method=method,
no_prompt=no_prompt,
token_path=token_path,
Expand Down
26 changes: 22 additions & 4 deletions dds_cli/data_putter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from dds_cli.custom_decorators import verify_proceed, update_status, subpath_required

import dds_cli
import dds_cli.directory
import dds_cli.utils

###############################################################################
Expand All @@ -43,7 +44,7 @@


def put(
mount_dir,
staging_location,
project,
source,
source_path_file,
Expand All @@ -54,11 +55,13 @@ def put(
no_prompt,
token_path,
destination,
default_log,
command,
):
"""Handle upload of data."""
# Initialize delivery - check user access etc
with DataPutter(
mount_dir=mount_dir,
staging_location=staging_location,
project=project,
source=source,
source_path_file=source_path_file,
Expand All @@ -68,6 +71,8 @@ def put(
no_prompt=no_prompt,
token_path=token_path,
destination=destination,
default_log=default_log,
command=command,
) as putter:
# Progress object to keep track of progress tasks
with Progress(
Expand Down Expand Up @@ -201,7 +206,7 @@ class DataPutter(base.DDSBaseClass):
def __init__(
self,
project: str = None,
mount_dir: pathlib.Path = None,
staging_location: pathlib.Path = None,
break_on_fail: bool = False,
overwrite: bool = False,
source: tuple = (),
Expand All @@ -211,12 +216,25 @@ def __init__(
no_prompt: bool = False,
token_path: str = None,
destination: str = None,
default_log: bool = True,
command: list = [],
):
"""Handle actions regarding upload of data."""
# Define staging directory path
staging_dir: pathlib.Path = pathlib.Path(f"DataDelivery_{dds_cli.timestamp.TimeStamp().timestamp}_{project}_upload")
if staging_location:
staging_dir = staging_location / staging_dir
else:
staging_dir = pathlib.Path.cwd() / staging_dir

# Generate staging directory
self.temporary_directory = staging_dir
self.dds_directory = dds_cli.directory.DDSDirectory(path=staging_dir, default_log=default_log, command=command)
self.failed_delivery_log = self.dds_directory.directories["LOGS"] / pathlib.Path("dds_failed_delivery.json")

# Initiate DDSBaseClass to authenticate user
super().__init__(
project=project,
mount_dir=mount_dir,
method=method,
no_prompt=no_prompt,
token_path=token_path,
Expand Down
Loading
Loading