Skip to content

Commit

Permalink
Vendor connect updates (#8)
Browse files Browse the repository at this point in the history
* removed CLI to move to a separate package

* removed remote_dir from Client attributes
  • Loading branch information
charlottekostelic committed Aug 16, 2024
1 parent eb99b4f commit b474603
Show file tree
Hide file tree
Showing 12 changed files with 96 additions and 450 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
python -m pip install --upgrade pip
python -m pip install -r dev-requirements.txt
- name: Run tests
run: pytest -m "not livetest and not cli_test" --cov=file_retriever/
run: pytest -m "not livetest" --cov=file_retriever/
- name: Send report to Coveralls
uses: AndreMiras/coveralls-python-action@develop
with:
Expand Down
44 changes: 1 addition & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,3 @@
# File Retriever

A tool to connect to and interact with servers via FTP/SFTP clients.

### Usage
The file_retriever tool can be imported and used in other applications or used with its CLI.

#### Commands
The following information is also available using `fetch --help`

##### Get Vendor Files
Retrieves files from one or more vendor servers via FTP/SFTP

`$ fetch vendor-files`

Options:

+ `-v, --vendor`

+ The vendor whose server you would like to connect to and retrieve files from.
Use `-v all` to retrieve files from all configured vendors.
This option can be repeated:
eg. `$ fetch vendor-files -v eastview -v leila`

+ `-d, --days`

+ The number of days to go back to search for files

+ `-h, --hours`

+ The number of hours to go back to search for files

+ `-m, --minutes`

+ The number of minutes to go back to search for files

##### Get Daily Vendor Files
Retrieves files updated within last day from servers of all configured vendors

`$ fetch daily-vendor-files`

##### Get List of Configured Vendors
Lists all vendors whose servers are available to connect to

`$ fetch available-vendors`
A tool to connect to and interact with servers via FTP/SFTP clients.
130 changes: 0 additions & 130 deletions file_retriever/__init__.py
Original file line number Diff line number Diff line change
@@ -1,130 +0,0 @@
import logging
import logging.config
import os
import click
from file_retriever.utils import client_config, logger_config, get_recent_files

logger = logging.getLogger("file_retriever")


@click.group()
@click.pass_context
def file_retriever_cli(ctx: click.Context) -> None:
"""
CLI for interacting with remote servers.
Loggers are configured when the command group is called. The `client_config`
function is used to read a configuration file with credentials and set the
creds as environment variables. `client_config` returns a list of names for
servers whose credentials are stored in the configuration file and loaded
to env vars. This list of names is stored in a `click.Context.obj` that can
be passed to any other commands.
"""
config = logger_config()
logging.config.dictConfig(config)
ctx.obj = client_config(
os.path.join(os.environ["USERPROFILE"], ".cred/.sftp/connections.yaml")
)
pass


@file_retriever_cli.command(
"vendor-files", short_help="Retrieve files from remote server."
)
@click.option(
"--vendor",
"-v",
"vendor",
type=str,
multiple=True,
help="Vendor to retrieve files for.",
)
@click.option(
"--days",
"-d",
"days",
default=0,
type=int,
help="How many days back to retrieve files.",
)
@click.option(
"--hours",
"-h",
"hours",
default=0,
type=int,
help="How many hours back to retrieve files.",
)
@click.option(
"--minutes",
"-m",
"minutes",
default=0,
type=int,
help="How many minutes back to retrieve files.",
)
@click.pass_context
def get_files(
ctx: click.Context, vendor: str, days: int, hours: int, minutes: int
) -> None:
"""
Retrieve files from remote server for specified vendor(s).
Args:
vendor:
name of vendor to retrieve files from. if 'all' then all vendors
listed in config file will be included, otherwise multiple values
can be passed and each will be added to a list. files will be
retrieved for each vendor in the list
days:
number of days to go back and retrieve files from
hours:
number of hours to go back and retrieve files from
minutes:
number of minutes to go back and retrieve files from
"""
if "all" in vendor and isinstance(ctx.obj, list):
vendor_list = [i.upper() for i in ctx.obj if i != "NSDROP"]
else:
vendor_list = [i.upper() for i in vendor]
get_recent_files(vendors=vendor_list, days=days, hours=hours, minutes=minutes)


@file_retriever_cli.command(
"daily-vendor-files", short_help="Retrieve previous day's files from remote server."
)
@click.pass_context
def get_files_today(ctx: click.Context) -> None:
"""
Retrieve files updated within last day from remote server for all vendor(s).
Args:
ctx: click context object that contains a list of vendor names
"""
vendor_list = [i.upper() for i in ctx.obj if i != "NSDROP"]
click.echo(vendor_list)
get_recent_files(vendors=vendor_list, days=1)


@file_retriever_cli.command(
"available-vendors", short_help="List all configured vendors."
)
@click.pass_context
def list_config_vendors(ctx: click.Context) -> None:
"""
List all configured vendors.
Args:
ctx: click context object that contains a list of vendor names
"""
if isinstance(ctx.obj, list) and len(ctx.obj) > 0:
click.echo(f"Available vendors: {ctx.obj}")
else:
click.echo("No vendors available.")


def main():
file_retriever_cli()
31 changes: 8 additions & 23 deletions file_retriever/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ def __init__(
password: str,
host: str,
port: Union[str, int],
remote_dir: str,
):
"""Initializes client instance.
Expand All @@ -44,16 +43,10 @@ def __init__(
server address
port:
port number for server. 21 for FTP, 22 for SFTP
remote_dir:
directory on server to interact with. for most vendor servers
there is a default directory to interact with (eg. 'files' or
'invoices'). this directory will be used in methods that take
`remote_dir` as an arg if another value is not provided.
"""
self.name = name
self.host = host
self.port = port
self.remote_dir = remote_dir

self.session = self.__connect_to_server(username=username, password=password)
logger.info(f"({self.name}) Connected to server")
Expand Down Expand Up @@ -138,7 +131,7 @@ def file_exists(self, file: FileInfo, dir: str, remote: bool) -> bool:
else:
return os.path.exists(f"{dir}/{file.file_name}")

def get_file(self, file: FileInfo, remote_dir: Optional[str] = None) -> File:
def get_file(self, file: FileInfo, remote_dir: str) -> File:
"""
Fetches a file from a server.
Expand All @@ -149,14 +142,10 @@ def get_file(self, file: FileInfo, remote_dir: Optional[str] = None) -> File:
Returns:
file fetched from `remote_dir` as `File` object
"""
if not remote_dir or remote_dir is None:
remote_dir = self.remote_dir
logger.debug(f"({self.name}) Fetching {file.file_name} from " f"`{remote_dir}`")
return self.session.fetch_file(file=file, dir=remote_dir)

def get_file_info(
self, file_name: str, remote_dir: Optional[str] = None
) -> FileInfo:
def get_file_info(self, file_name: str, remote_dir: str) -> FileInfo:
"""
Retrieves metadata for a file on server.
Expand All @@ -167,8 +156,6 @@ def get_file_info(
Returns:
file in `remote_dir` represented as `FileInfo` object
"""
if not remote_dir or remote_dir is None:
remote_dir = self.remote_dir
logger.debug(
f"({self.name}) Retrieving file info for {file_name} " f"from {remote_dir}"
)
Expand All @@ -182,8 +169,8 @@ def get_file_info(

def list_file_info(
self,
time_delta: Union[datetime.timedelta, int] = 0,
remote_dir: Optional[str] = None,
remote_dir: str,
time_delta: Optional[Union[datetime.timedelta, int]] = None,
) -> List[FileInfo]:
"""
Lists each file in a directory on server. If `time_delta`
Expand All @@ -192,12 +179,12 @@ def list_file_info(
days or a `datetime.timedelta` object.
Args:
remote_dir:
directory on server to interact with
time_delta:
how far back to check for files. can be an integer representing
the number of days or a `datetime.timedelta` object. default is 0,
the number of days or a `datetime.timedelta` object. default is None,
ie. all files will be listed.
remote_dir:
directory on server to interact with
Returns:
list of files in `remote_dir` represented as `FileInfo` objects
Expand All @@ -207,11 +194,9 @@ def list_file_info(
time_delta = datetime.timedelta(days=time_delta)
else:
time_delta = time_delta
if not remote_dir or remote_dir is None:
remote_dir = self.remote_dir
logger.debug(f"({self.name}) Retrieving list of files in `{remote_dir}`")
files = self.session.list_file_data(dir=remote_dir)
if time_delta > datetime.timedelta(days=0):
if time_delta and time_delta is not None:
logger.debug(
f"({self.name}) Filtering list for files created "
f"since {datetime.datetime.strftime((today - time_delta), '%Y-%m-%d')}"
Expand Down
1 change: 0 additions & 1 deletion file_retriever/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ def connect(name: str) -> Client:
password=os.environ[f"{client_name}_PASSWORD"],
host=os.environ[f"{client_name}_HOST"],
port=os.environ[f"{client_name}_PORT"],
remote_dir=os.environ[f"{client_name}_SRC"],
)


Expand Down
16 changes: 1 addition & 15 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ paramiko = "^3.4.0"
types-paramiko = "^3.4.0.20240423"
types-pyyaml = "^6.0.12.20240311"
pyyaml = "^6.0.1"
click = "^8.1.7"


[tool.poetry.group.dev.dependencies]
pytest = "^8.2.2"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
fetch = "file_retriever:main"

[tool.pytest.ini_options]
testpaths = ["tests"]
Expand Down
Loading

0 comments on commit b474603

Please sign in to comment.