mercuto-client 0.3.4a1__tar.gz → 0.3.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/PKG-INFO +2 -1
- mercuto_client-0.3.5/mercuto_client/_tests/test_ingester/test_backup.py +98 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/client.py +1 -1
- mercuto_client-0.3.5/mercuto_client/ingester/__main__.py +275 -0
- mercuto_client-0.3.5/mercuto_client/ingester/backup.py +340 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/ingester/mercuto.py +18 -3
- mercuto_client-0.3.5/mercuto_client/ingester/pid_file.py +66 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/mocks/mock_notifications.py +2 -2
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/modules/core.py +32 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/modules/notifications.py +3 -3
- mercuto_client-0.3.5/mercuto_client/modules/reports.py +222 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client.egg-info/PKG-INFO +2 -1
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client.egg-info/SOURCES.txt +4 -0
- mercuto_client-0.3.5/mercuto_client.egg-info/entry_points.txt +3 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client.egg-info/requires.txt +1 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/pyproject.toml +9 -1
- mercuto_client-0.3.4a1/mercuto_client/ingester/__main__.py +0 -166
- mercuto_client-0.3.4a1/mercuto_client/modules/reports.py +0 -185
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/LICENSE +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/README.md +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/__init__.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_authentication.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_tests/__init__.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_tests/conftest.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_tests/test_ingester/__init__.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_tests/test_ingester/test_file_processor.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_tests/test_ingester/test_ftp.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_tests/test_ingester/test_parsers.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_tests/test_mocking/__init__.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_tests/test_mocking/conftest.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/_tests/test_mocking/test_mock_identity.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/acl.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/exceptions.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/ingester/__init__.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/ingester/ftp.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/ingester/parsers/__init__.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/ingester/parsers/campbell.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/ingester/parsers/generic_csv.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/ingester/parsers/worldsensing.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/ingester/processor.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/mocks/__init__.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/mocks/_utility.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/mocks/mock_core.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/mocks/mock_data.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/mocks/mock_fatigue.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/mocks/mock_identity.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/mocks/mock_media.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/modules/__init__.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/modules/_util.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/modules/data.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/modules/fatigue.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/modules/identity.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/modules/media.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/py.typed +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client/util.py +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client.egg-info/dependency_links.txt +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/mercuto_client.egg-info/top_level.txt +0 -0
- {mercuto_client-0.3.4a1 → mercuto_client-0.3.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mercuto-client
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Library for interfacing with Rockfield's Mercuto API
|
|
5
5
|
Author-email: Daniel Whipp <daniel.whipp@rocktech.com.au>
|
|
6
6
|
License-Expression: AGPL-3.0-only
|
|
@@ -22,6 +22,7 @@ Requires-Dist: pyftpdlib>=2.0.1
|
|
|
22
22
|
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
23
23
|
Requires-Dist: pytz>=2025.2
|
|
24
24
|
Requires-Dist: schedule>=1.2.2
|
|
25
|
+
Requires-Dist: zc.lockfile>=4.0
|
|
25
26
|
Requires-Dist: pydantic>=2.0
|
|
26
27
|
Dynamic: license-file
|
|
27
28
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from threading import Thread
|
|
5
|
+
from typing import Iterator, TypedDict
|
|
6
|
+
from urllib.parse import urlparse
|
|
7
|
+
|
|
8
|
+
import pyftpdlib.authorizers # type: ignore[import-untyped]
|
|
9
|
+
import pyftpdlib.handlers # type: ignore[import-untyped]
|
|
10
|
+
import pyftpdlib.servers # type: ignore[import-untyped]
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from ...ingester.backup import FileBackup, FTPBackup
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_file_backup():
|
|
17
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
18
|
+
uri = Path(temp_dir).as_uri()
|
|
19
|
+
bak = FileBackup(urlparse(uri))
|
|
20
|
+
assert bak.process_file(__file__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_file_backup_does_not_exist_create_it():
|
|
24
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
25
|
+
test_dir = Path(temp_dir) / "test_dir"
|
|
26
|
+
uri = test_dir.as_uri() + "?create=true"
|
|
27
|
+
assert not test_dir.exists()
|
|
28
|
+
bak = FileBackup(urlparse(uri))
|
|
29
|
+
assert bak.process_file(__file__)
|
|
30
|
+
dest = test_dir / Path(__file__).name
|
|
31
|
+
assert test_dir.exists()
|
|
32
|
+
assert dest.exists()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_file_backup_does_not_exist():
|
|
36
|
+
uri = (Path(__file__).parent / "I_DO_NOT_EXIST").as_uri()
|
|
37
|
+
with pytest.raises(ValueError, match="backup path does not exist"):
|
|
38
|
+
FileBackup(urlparse(uri))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TestFTPServerConfig(TypedDict):
|
|
42
|
+
host: str
|
|
43
|
+
port: int
|
|
44
|
+
user: str
|
|
45
|
+
passwd: str
|
|
46
|
+
ftp_root: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.fixture(scope="function")
|
|
50
|
+
def mock_ftp_server() -> Iterator[TestFTPServerConfig]:
|
|
51
|
+
# Create a temporary directory to act as the FTP root
|
|
52
|
+
temp_dir = tempfile.TemporaryDirectory()
|
|
53
|
+
ftp_root = temp_dir.name
|
|
54
|
+
|
|
55
|
+
# Set up user authentication
|
|
56
|
+
authorizer = pyftpdlib.authorizers.DummyAuthorizer()
|
|
57
|
+
authorizer.add_user("user", "12345", ftp_root, perm="elradfmwMT")
|
|
58
|
+
|
|
59
|
+
# Set up FTP handler
|
|
60
|
+
handler = pyftpdlib.handlers.FTPHandler
|
|
61
|
+
handler.authorizer = authorizer
|
|
62
|
+
|
|
63
|
+
# Start the FTP server in a separate thread
|
|
64
|
+
server = pyftpdlib.servers.FTPServer(("127.0.0.1", 0), handler)
|
|
65
|
+
ip, port = server.socket.getsockname()
|
|
66
|
+
|
|
67
|
+
server_thread = Thread(target=server.serve_forever)
|
|
68
|
+
server_thread.daemon = True
|
|
69
|
+
server_thread.start()
|
|
70
|
+
|
|
71
|
+
yield {
|
|
72
|
+
"host": ip,
|
|
73
|
+
"port": port,
|
|
74
|
+
"user": "user",
|
|
75
|
+
"passwd": "12345",
|
|
76
|
+
"ftp_root": ftp_root
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
server.close_all()
|
|
80
|
+
temp_dir.cleanup()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_ftp_backup(mock_ftp_server: TestFTPServerConfig):
|
|
84
|
+
url = f"ftp://{mock_ftp_server['user']}:{mock_ftp_server['passwd']}@{mock_ftp_server['host']}:{mock_ftp_server['port']}/my_dir?create=true"
|
|
85
|
+
os.makedirs(Path(mock_ftp_server['ftp_root']) / "my_dir", exist_ok=True)
|
|
86
|
+
bak = FTPBackup(urlparse(url))
|
|
87
|
+
bak.process_file(__file__)
|
|
88
|
+
assert bak.process_file(__file__)
|
|
89
|
+
dest = Path(mock_ftp_server['ftp_root']) / "my_dir" / Path(__file__).name
|
|
90
|
+
assert dest.exists()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_ftp_backup_fails_if_dir_not_exists(mock_ftp_server: TestFTPServerConfig):
|
|
94
|
+
url = f"ftp://{mock_ftp_server['user']}:{mock_ftp_server['passwd']}@{mock_ftp_server['host']}:{mock_ftp_server['port']}/my_dir"
|
|
95
|
+
bak = FTPBackup(urlparse(url))
|
|
96
|
+
assert not bak.process_file(__file__)
|
|
97
|
+
dest = Path(mock_ftp_server['ftp_root']) / "my_dir" / Path(__file__).name
|
|
98
|
+
assert not dest.exists()
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import logging
|
|
3
|
+
import logging.handlers
|
|
4
|
+
import os
|
|
5
|
+
import signal
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
10
|
+
from urllib.parse import ParseResult, urlparse
|
|
11
|
+
|
|
12
|
+
import schedule
|
|
13
|
+
|
|
14
|
+
from ..util import get_free_space_excluding_files
|
|
15
|
+
from .backup import get_backup_handler
|
|
16
|
+
from .ftp import simple_ftp_server
|
|
17
|
+
from .mercuto import MercutoIngester
|
|
18
|
+
from .pid_file import PidFile
|
|
19
|
+
from .processor import FileProcessor
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
T = TypeVar('T')
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def call_and_log_error(func: Callable[[], T]) -> T | None:
|
|
28
|
+
"""
|
|
29
|
+
Call a function and log any exceptions that occur.
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
return func()
|
|
33
|
+
except Exception:
|
|
34
|
+
logging.exception(f"Error in {func.__name__}")
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Status:
|
|
39
|
+
"""
|
|
40
|
+
Status class to handle running state of the ingester.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self):
|
|
44
|
+
self.running = True
|
|
45
|
+
|
|
46
|
+
def stop(self, code: Any, frame: Any):
|
|
47
|
+
self.running = False
|
|
48
|
+
print("Stopping")
|
|
49
|
+
|
|
50
|
+
def is_running(self):
|
|
51
|
+
return self.running
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def launch_mercuto_ingester(
|
|
55
|
+
project: str,
|
|
56
|
+
api_key: str,
|
|
57
|
+
hostname: str = 'https://api.rockfieldcloud.com.au',
|
|
58
|
+
verify_ssl: bool = True,
|
|
59
|
+
pid_file: Optional[Path] = None,
|
|
60
|
+
workdir: Optional[str] = '~/.mercuto-ingester',
|
|
61
|
+
verbose: bool = False,
|
|
62
|
+
logfile: Optional[str] = None,
|
|
63
|
+
directory: Optional[str] = None,
|
|
64
|
+
target_free_space_mb: Optional[float] = None,
|
|
65
|
+
max_files: Optional[int] = None,
|
|
66
|
+
mapping: Optional[str] = None,
|
|
67
|
+
clean: bool = False,
|
|
68
|
+
ftp_server_username: str = 'logger',
|
|
69
|
+
ftp_server_password: str = 'password',
|
|
70
|
+
ftp_server_port: int = 2121,
|
|
71
|
+
ftp_server_rename: bool = True,
|
|
72
|
+
max_attempts: int = 1000,
|
|
73
|
+
backup_location: Optional[list[ParseResult]] = None,
|
|
74
|
+
timezone: Optional[str] = None,
|
|
75
|
+
):
|
|
76
|
+
|
|
77
|
+
if backup_location is None:
|
|
78
|
+
backup_location = []
|
|
79
|
+
|
|
80
|
+
with PidFile(pid_file):
|
|
81
|
+
if workdir is None:
|
|
82
|
+
workdir = os.path.join(os.path.expanduser('~'), ".mercuto-ingester")
|
|
83
|
+
elif workdir.startswith("~"):
|
|
84
|
+
workdir = os.path.expanduser(workdir)
|
|
85
|
+
else:
|
|
86
|
+
workdir = workdir
|
|
87
|
+
if not os.path.exists(workdir):
|
|
88
|
+
raise ValueError(f"Work directory {workdir} does not exist")
|
|
89
|
+
|
|
90
|
+
os.makedirs(workdir, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
if verbose:
|
|
93
|
+
level = logging.DEBUG
|
|
94
|
+
else:
|
|
95
|
+
level = logging.INFO
|
|
96
|
+
|
|
97
|
+
handlers: list[logging.Handler] = []
|
|
98
|
+
handlers.append(logging.StreamHandler(sys.stderr))
|
|
99
|
+
|
|
100
|
+
if logfile is not None:
|
|
101
|
+
logfile = logfile
|
|
102
|
+
else:
|
|
103
|
+
logfile = os.path.join(workdir, 'log.txt')
|
|
104
|
+
handlers.append(logging.handlers.RotatingFileHandler(
|
|
105
|
+
logfile, maxBytes=1000000, backupCount=3))
|
|
106
|
+
|
|
107
|
+
logging.basicConfig(format='[PID %(process)d] %(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
|
|
108
|
+
datefmt='%d/%m/%Y %H:%M:%S',
|
|
109
|
+
level=level,
|
|
110
|
+
handlers=handlers)
|
|
111
|
+
|
|
112
|
+
if directory is None:
|
|
113
|
+
buffer_directory = os.path.join(workdir, "buffered-files")
|
|
114
|
+
else:
|
|
115
|
+
buffer_directory = directory
|
|
116
|
+
os.makedirs(buffer_directory, exist_ok=True)
|
|
117
|
+
|
|
118
|
+
ftp_dir = os.path.join(workdir, 'temp-ftp-data')
|
|
119
|
+
os.makedirs(ftp_dir, exist_ok=True)
|
|
120
|
+
|
|
121
|
+
if target_free_space_mb is None and max_files is None:
|
|
122
|
+
target_free_space_mb = get_free_space_excluding_files(buffer_directory) * 0.25 // (1024 * 1024) # Convert to MB
|
|
123
|
+
logging.info(f"Target remaining free space set to {target_free_space_mb} MB based on available disk space.")
|
|
124
|
+
|
|
125
|
+
logger.info(f"Using work directory: {workdir}")
|
|
126
|
+
|
|
127
|
+
database_path = os.path.join(workdir, "buffer.db")
|
|
128
|
+
if clean and os.path.exists(database_path):
|
|
129
|
+
logging.info(f"Dropping existing database at {database_path}")
|
|
130
|
+
os.remove(database_path)
|
|
131
|
+
|
|
132
|
+
ingester = MercutoIngester(
|
|
133
|
+
project_code=project,
|
|
134
|
+
api_key=api_key,
|
|
135
|
+
hostname=hostname,
|
|
136
|
+
verify_ssl=verify_ssl,
|
|
137
|
+
timezone=timezone
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if mapping is not None:
|
|
141
|
+
import json
|
|
142
|
+
with open(mapping, 'r') as f:
|
|
143
|
+
mapping = json.load(f)
|
|
144
|
+
if not isinstance(mapping, dict):
|
|
145
|
+
raise ValueError(f"Mapping file {mapping} must contain a JSON object")
|
|
146
|
+
ingester.update_mapping(mapping)
|
|
147
|
+
|
|
148
|
+
pre_processing_handlers: list[Callable[[str], bool]] = [get_backup_handler(loc) for loc in backup_location]
|
|
149
|
+
processor_callbacks: list[Callable[[str], bool]] = [ingester.process_file]
|
|
150
|
+
post_processing_handlers: list[Callable[[str], bool]] = []
|
|
151
|
+
|
|
152
|
+
all_handlers = pre_processing_handlers + processor_callbacks + post_processing_handlers
|
|
153
|
+
|
|
154
|
+
processor = FileProcessor(
|
|
155
|
+
buffer_dir=buffer_directory,
|
|
156
|
+
db_path=database_path,
|
|
157
|
+
process_callback=lambda filename: all(handler(filename) for handler in all_handlers),
|
|
158
|
+
max_attempts=max_attempts,
|
|
159
|
+
target_free_space_mb=target_free_space_mb,
|
|
160
|
+
max_files=max_files)
|
|
161
|
+
|
|
162
|
+
processor.scan_existing_files()
|
|
163
|
+
|
|
164
|
+
with simple_ftp_server(directory=buffer_directory,
|
|
165
|
+
username=ftp_server_username, password=ftp_server_password, port=ftp_server_port,
|
|
166
|
+
callback=processor.add_file_to_db, rename=ftp_server_rename,
|
|
167
|
+
workdir=ftp_dir):
|
|
168
|
+
call_and_log_error(ingester.ping)
|
|
169
|
+
schedule.every(60).seconds.do(call_and_log_error, ingester.ping) # type: ignore[attr-defined]
|
|
170
|
+
schedule.every(5).seconds.do(call_and_log_error, processor.process_next_file) # type: ignore[attr-defined]
|
|
171
|
+
schedule.every(2).minutes.do(call_and_log_error, processor.cleanup_old_files) # type: ignore[attr-defined]
|
|
172
|
+
|
|
173
|
+
status = Status()
|
|
174
|
+
signal.signal(signal.SIGTERM, status.stop)
|
|
175
|
+
|
|
176
|
+
while status.is_running():
|
|
177
|
+
schedule.run_pending()
|
|
178
|
+
sleep_period = schedule.idle_seconds()
|
|
179
|
+
if sleep_period is None or sleep_period < 0:
|
|
180
|
+
sleep_period = 0
|
|
181
|
+
# We need to wake up to handle ctrl-c etc
|
|
182
|
+
if sleep_period > 1:
|
|
183
|
+
sleep_period = 1
|
|
184
|
+
time.sleep(sleep_period)
|
|
185
|
+
|
|
186
|
+
logger.warning("Shutting Down...")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def main():
|
|
190
|
+
parser = argparse.ArgumentParser(description='Mercuto Ingester CLI')
|
|
191
|
+
parser.add_argument('-p', '--project', type=str,
|
|
192
|
+
required=True, help='Mercuto project code')
|
|
193
|
+
parser.add_argument('-k', '--api-key', type=str,
|
|
194
|
+
required=True, help='API key for Mercuto')
|
|
195
|
+
parser.add_argument('-v', '--verbose', action='store_true',
|
|
196
|
+
help='Enable verbose output')
|
|
197
|
+
parser.add_argument('-d', '--directory', type=str,
|
|
198
|
+
help='Directory to store ingested files. Default is a directory called `buffered-files` in the workdir.')
|
|
199
|
+
parser.add_argument('-s', '--target-free-space-mb', type=int,
|
|
200
|
+
help='Size in MB for total amount of remaining free space to keep available. \
|
|
201
|
+
Default is 25% of the available disk space on the buffer partition excluding the directory itself', default=None)
|
|
202
|
+
parser.add_argument('--max-files', type=int,
|
|
203
|
+
help='Maximum number of files to keep in the buffer. Default is to use the size param.', default=None)
|
|
204
|
+
parser.add_argument('--max-attempts', type=int,
|
|
205
|
+
help='Maximum number of attempts to process a file before giving up. Default is 1000.',
|
|
206
|
+
default=1000)
|
|
207
|
+
parser.add_argument('--workdir', type=str,
|
|
208
|
+
help='Working directory for the ingester. Default is ~/.mercuto-ingester',)
|
|
209
|
+
parser.add_argument('--logfile', type=str,
|
|
210
|
+
help='Log file path. No logs written if not provided. Maximum of 4 log files of 1MB each will be kept.\
|
|
211
|
+
Default is log.txt in the workdir.')
|
|
212
|
+
parser.add_argument('--mapping', type=str,
|
|
213
|
+
help='Path to a JSON file with channel label to channel code mapping.\
|
|
214
|
+
If not provided, the ingester will try to detect the channels from the project.',
|
|
215
|
+
default=None)
|
|
216
|
+
parser.add_argument('--hostname', type=str,
|
|
217
|
+
help='Hostname to use for the Mercuto server. Default is "https://api.rockfieldcloud.com.au".',
|
|
218
|
+
default='https://api.rockfieldcloud.com.au')
|
|
219
|
+
parser.add_argument('--clean',
|
|
220
|
+
help='Drop the database before starting. This will not remove any buffer files and will rescan them on startup.',
|
|
221
|
+
action='store_true')
|
|
222
|
+
parser.add_argument('--username', type=str,
|
|
223
|
+
help='Username for the FTP server. Default is "logger".',
|
|
224
|
+
default='logger')
|
|
225
|
+
parser.add_argument('--password', type=str,
|
|
226
|
+
help='Password for the FTP server. Default is "password".',
|
|
227
|
+
default='password')
|
|
228
|
+
parser.add_argument('--port', type=int,
|
|
229
|
+
help='Port for the FTP server. Default is 2121.',
|
|
230
|
+
default=2121)
|
|
231
|
+
parser.add_argument('--no-rename', action='store_true',
|
|
232
|
+
help='Add the current timestamp to the end of the files received via FTP. \
|
|
233
|
+
This is useful to avoid overwriting files with the same name.')
|
|
234
|
+
parser.add_argument('-i', '--insecure', action="store_true",
|
|
235
|
+
help='Disable SSL verification',
|
|
236
|
+
default=False)
|
|
237
|
+
parser.add_argument('-b', '--backup-location', action="append",
|
|
238
|
+
help='Backup location to store ingested files. Must be a valid URL, e.g. scp://user@host/path. '
|
|
239
|
+
'Can be specified multiple times.',
|
|
240
|
+
type=urlparse)
|
|
241
|
+
parser.add_argument('-e', '--pid-file', help='Ths location to create the PID file', type=Path, default=None)
|
|
242
|
+
parser.add_argument('--timezone', type=str,
|
|
243
|
+
help='Timezone to use for data uploads (e.g. "Australia/Melbourne"). \
|
|
244
|
+
If not provided, no timezone will be sent on uploads. \
|
|
245
|
+
Only needed if data files do not contain timezone information (E.g. Campbell Scientific loggers).',
|
|
246
|
+
default=None)
|
|
247
|
+
|
|
248
|
+
args = parser.parse_args()
|
|
249
|
+
|
|
250
|
+
launch_mercuto_ingester(
|
|
251
|
+
project=args.project,
|
|
252
|
+
api_key=args.api_key,
|
|
253
|
+
verify_ssl=not args.insecure,
|
|
254
|
+
pid_file=args.pid_file,
|
|
255
|
+
workdir=args.workdir,
|
|
256
|
+
verbose=args.verbose,
|
|
257
|
+
logfile=args.logfile,
|
|
258
|
+
directory=args.directory,
|
|
259
|
+
target_free_space_mb=args.target_free_space_mb,
|
|
260
|
+
max_files=args.max_files,
|
|
261
|
+
mapping=args.mapping,
|
|
262
|
+
clean=args.clean,
|
|
263
|
+
ftp_server_username=args.username,
|
|
264
|
+
ftp_server_password=args.password,
|
|
265
|
+
ftp_server_port=args.port,
|
|
266
|
+
ftp_server_rename=not args.no_rename,
|
|
267
|
+
max_attempts=args.max_attempts,
|
|
268
|
+
backup_location=args.backup_location,
|
|
269
|
+
hostname=args.hostname,
|
|
270
|
+
timezone=args.timezone
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
if __name__ == '__main__':
|
|
275
|
+
main()
|