send_to

Organize files using Window’s shell:sendto.

release v0.4.0


This project aids in the creation of scripts that organize files, passed to it as arguments. The scripts can be placed inside Window’s shell:sendto directory.

How it works?

The function send_to(cfg) follows the configuration that is set in the passed Cfg object. This object holds configuration such as: the destination path, wheter the files will be moved or copied, the names of the user defined functions, etc.

The user defined functions are used to determine: if the files will be further categorized in a subdirectory, wheter a file will be skipped, wheter a file will be renamed and a function for doing post-processing on the destination files.

How to use it?

This project is meant to be used as a library module, create the actual Python script file and import the function send_to(cfg), the classes Cfg and Info and the enum Operation from the send_to module.

from send_to import send_to, Cfg, Info, Operation

Setting the configuration (Cfg)

Instantiate an object from the Cfg class and set it data members.

Example:
 cfg = Cfg()
 cfg.version = 0
 cfg.dst_path = os.path.dirname(sys.argv[0])  # use script's location
 cfg.operation = Operation.MOVE

 # user defined functions (explained later)
 cfg.subdir = subdir
 cfg.skip = skip
 cfg.rename = rename
 cfg.post_process = post_process

Cfg member variables:

Cfg.version: int = 0

Set it to the major version of the send_to module that the script is currently developed for.

Cfg.dst_path: str

Destination path for the processed files.

Cfg.operation: Operation = 1

Operation the will be performed on the files - copy or move.

Cfg.date_fmt: str = '%Y-%m-%d'

String format of the date.

Cfg.ask_for_date: bool = True

Prompt the user to input a date/date shift.

Cfg.ask_for_desc: bool = True

Prompt the user to input a description.

Cfg.overwrite_file: bool = False

Overwrite files with the same name in the destination.

Cfg.dry_run: bool = False

Just print the console messages without actually doing anything.

Cfg.debug: bool = True

Print more console messages.

Cfg.subdir: Callable[[Info], str]

User defined function for determining the name of the subdirectory under dst_path where the files will be placed.

Cfg.skip: Callable[[Info], bool]

User defined function for determining if a file will be skipped.

Cfg.rename: Callable[[Info], str]

User defined function for determining how the destination file will be named.

Cfg.post_process: Callable[[str], None]

User defined function for doing post-processing on a destination file.

Creating the callback functions

There are 4 (optional) user defined functions that’ll be called by send_to(cfg). They can be created in the script file and their names are set in the Cfg object. These functions usually take an Info object as an argument.

The Info class

Information such as: source file path, destination path, date and description is stored in an Info object. This object is then passed to some of the user defined functions so this information can be used in e.g. naming the subdirectory, the file etc.

Info.file_path: str = ''

The path of the currently processed file.

Info.dst_path: str = ''

Destination path

Info.date: str = ''

Date

Info.desc: str = ''

Description

Subdirectory function

def subdir(info: Info) -> str:
"""Use this function to construct a subdirectory name based on the
information provided by the passed `Info` object. If no subdirectory is
required - return an empty string.

Args:
    info (Info)

Returns:
    str: subdirectory name or empty string
"""
Example “subdir” function:
def my_subdir_func(info: Info) -> str:
    """Tell `send_to(cfg)` to create a subdirectory named "{date}
    {description}" e.g. "2023-08-26 summer"."""
    return (f'{info.date} {info.desc}').strip()

cfg.subdir = my_sudir_func

Skip function

def skip(info: Info) -> bool:
"""Use this function to determine if the currently processed file should
be skipped or not.

Args:
    info (Info)

Returns:
    bool: True to skip the current file, False otherwise
"""
Example “skip” function:
def skip_jpgs(info: Info) -> str:
    """Tell `send_to(cfg)` to skip JPGs."""
    file = os.path.basename(info.file_path)
    file_name, file_ext = os.path.splitext(file)

    if file_ext.lower() == '.jpg':
        return True
    else:
        return False

cfg.skip = skip_jpgs

Rename function

def rename(info: Info) -> str:
"""Use this function to determine how the currently processed file should
be renamed.

Args:
    info (Info)

Returns:
    str: new file name or empty string to keep the original name
"""
Example “rename” function:
def append_desc(info: Info) -> str:
    """Tell `send_to(cfg)` to append description to the destination file
    name."""
    file = os.path.basename(info.file_path)
    file_name, file_ext = os.path.splitext(file)

    return (f"{file_name} {info.desc}{file_ext}")

cfg.rename = append_desc

Post-processing function

def post_process(file_path: str) -> None:
"""Use this function to do post-processing on the destination file.

Args:
    file_path (str): path of the destination file
"""
Example “post_process” function:
def resize(file_path: str) -> None:
    """Resize destination files to 1080p."""
    resize_img(file_path, file_path + "_1080p", "1920x1080")

cfg.post_process = resize

Executing send_to(cfg)

Call send_to(cfg), passing it the configuration object Cfg.

send_to(cfg)