feat: добавил подписчиков на удаленные директории с выгрузкой новых файлов
This commit is contained in:
parent
5a8423e266
commit
0fd36bf550
6
data/connection.json
Normal file
6
data/connection.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"host": "172.21.5.240",
|
||||
"port": 22,
|
||||
"username": "smart",
|
||||
"password": "00000000"
|
||||
}
|
||||
14
data/path.json
Normal file
14
data/path.json
Normal file
@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"source": "/home/smart/PN/TWC/trace/Weld Point",
|
||||
"buffer": "/home/andrei/PycharmProjects/traceDelivery/buffer",
|
||||
"destination": "/home/andrei/Desktop/bla",
|
||||
"apply_filter": false
|
||||
},
|
||||
{
|
||||
"source": "/home/smart/PN/TWC/trace/test1",
|
||||
"buffer": "/home/andrei/PycharmProjects/traceDelivery/buffer",
|
||||
"destination": "/home/andrei/Desktop/bla",
|
||||
"apply_filter": false
|
||||
}
|
||||
]
|
||||
13
requirements.txt
Normal file
13
requirements.txt
Normal file
@ -0,0 +1,13 @@
|
||||
bcrypt==4.2.0
|
||||
cffi==1.17.1
|
||||
cryptography==43.0.3
|
||||
loguru==0.7.2
|
||||
numpy==2.1.3
|
||||
pandas==2.2.3
|
||||
paramiko==3.5.0
|
||||
pycparser==2.22
|
||||
PyNaCl==1.5.0
|
||||
python-dateutil==2.9.0.post0
|
||||
pytz==2024.2
|
||||
six==1.16.0
|
||||
tzdata==2024.2
|
||||
50
src/main.py
50
src/main.py
@ -0,0 +1,50 @@
|
||||
import json
|
||||
import os
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from remote.client import SSHClient
|
||||
from remote.connection_data import SSHConnectionParams
|
||||
from subscription.subscription_object import SubscriptionObject
|
||||
from subscription.subscriber import Subscriber
|
||||
from tuning.tuner import Tuner
|
||||
|
||||
|
||||
def init_files_buffer(subscription_objects) -> None:
|
||||
for element in subscription_objects:
|
||||
if not os.path.exists(element.buffer):
|
||||
os.makedirs(element.buffer)
|
||||
|
||||
|
||||
def read_json(filepath: str) -> dict:
|
||||
if not os.path.exists(filepath):
|
||||
raise FileNotFoundError(f"File {filepath} not found!")
|
||||
|
||||
with open(filepath, 'r') as json_file:
|
||||
content = json.load(json_file)
|
||||
return content
|
||||
|
||||
|
||||
def start_subscription(ssh_connection_object: SSHConnectionParams,
|
||||
subscription_objects: list[SubscriptionObject]) -> None:
|
||||
tuner = Tuner()
|
||||
executor = ThreadPoolExecutor()
|
||||
|
||||
for element in subscription_objects:
|
||||
client = SSHClient(ssh_connection_object)
|
||||
client.connect()
|
||||
subscriber = Subscriber(client.sftp, element)
|
||||
tuner.accept(subscriber)
|
||||
executor.submit(subscriber.subscribe)
|
||||
|
||||
|
||||
def main():
|
||||
ssh_connection_dict = read_json("../data/connection.json")
|
||||
ssh_connection_object = SSHConnectionParams(**ssh_connection_dict)
|
||||
subscription_objects_list = read_json("../data/path.json")
|
||||
subscription_objects = [SubscriptionObject(**element) for element in subscription_objects_list]
|
||||
init_files_buffer(subscription_objects)
|
||||
start_subscription(ssh_connection_object, subscription_objects)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
36
src/remote/client.py
Normal file
36
src/remote/client.py
Normal file
@ -0,0 +1,36 @@
|
||||
from typing import Optional
|
||||
|
||||
import paramiko
|
||||
|
||||
from remote.connection_data import SSHConnectionParams
|
||||
|
||||
|
||||
class SSHClient:
|
||||
def __init__(self, connection_params: SSHConnectionParams):
|
||||
self._connection_params = connection_params
|
||||
|
||||
self._transport: Optional[paramiko.Transport] = None
|
||||
self._sftp: Optional[paramiko.SFTPClient] = None
|
||||
|
||||
@property
|
||||
def connection_params(self) -> SSHConnectionParams:
|
||||
return self._connection_params
|
||||
|
||||
@property
|
||||
def sftp(self) -> Optional[paramiko.SFTPClient]:
|
||||
return self._sftp
|
||||
|
||||
@property
|
||||
def transport(self) -> Optional[paramiko.Transport]:
|
||||
return self._transport
|
||||
|
||||
def connect(self) -> None:
|
||||
self._transport = paramiko.Transport((self._connection_params.host,
|
||||
self._connection_params.port))
|
||||
self._transport.connect(username=self._connection_params.username,
|
||||
password=self._connection_params.password)
|
||||
self._sftp = paramiko.SFTPClient.from_transport(self._transport)
|
||||
|
||||
def disconnect(self) -> None:
|
||||
self._sftp.close()
|
||||
self._transport.close()
|
||||
8
src/remote/connection_data.py
Normal file
8
src/remote/connection_data.py
Normal file
@ -0,0 +1,8 @@
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class SSHConnectionParams(NamedTuple):
|
||||
host: str
|
||||
port: int
|
||||
username: str
|
||||
password: str
|
||||
35
src/subscription/base.py
Normal file
35
src/subscription/base.py
Normal file
@ -0,0 +1,35 @@
|
||||
from typing import Optional
|
||||
|
||||
import paramiko
|
||||
|
||||
from subscription.subscription_object import SubscriptionObject
|
||||
from tuning.tuner import Tuner
|
||||
|
||||
|
||||
class BaseSubscriber:
|
||||
|
||||
def __init__(self,
|
||||
sftp_client: paramiko.SFTPClient,
|
||||
subscription_object: SubscriptionObject,
|
||||
tuner: Optional[Tuner] = None):
|
||||
self._sftp_client = sftp_client
|
||||
self._subscription_object = subscription_object
|
||||
self._tuner = tuner
|
||||
|
||||
self._history = []
|
||||
|
||||
@property
|
||||
def sftp_client(self) -> paramiko.SFTPClient:
|
||||
return self._sftp_client
|
||||
|
||||
@property
|
||||
def subscription_object(self) -> SubscriptionObject:
|
||||
return self._subscription_object
|
||||
|
||||
@property
|
||||
def tuner(self) -> Optional[Tuner]:
|
||||
return self._tuner
|
||||
|
||||
@tuner.setter
|
||||
def tuner(self, tuner: Tuner) -> None:
|
||||
self._tuner = tuner
|
||||
34
src/subscription/subscriber.py
Normal file
34
src/subscription/subscriber.py
Normal file
@ -0,0 +1,34 @@
|
||||
from os import path
|
||||
from time import sleep
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from subscription.base import BaseSubscriber
|
||||
|
||||
|
||||
class Subscriber(BaseSubscriber):
|
||||
|
||||
def _init_state(self):
|
||||
files = self._sftp_client.listdir(self._subscription_object.source)
|
||||
self._history = files
|
||||
logger.info(f"Subscription to {self._subscription_object.source} done!")
|
||||
|
||||
def _download_file(self, file: str):
|
||||
remote_path = path.join(self._subscription_object.source, file)
|
||||
local_path = path.join(self._subscription_object.buffer, file)
|
||||
self._sftp_client.get(remotepath=remote_path, localpath=local_path)
|
||||
logger.info(f"Download: {remote_path} --> {local_path}")
|
||||
self._tuner.notify(self._subscription_object, file)
|
||||
|
||||
def subscribe(self):
|
||||
self._init_state()
|
||||
|
||||
while True:
|
||||
files = self._sftp_client.listdir(self._subscription_object.source)
|
||||
new_files = list(filter(lambda x: x not in self._history, files))
|
||||
if new_files:
|
||||
logger.info(f"New files: {new_files} in {self._subscription_object.source} found!")
|
||||
self._history = files
|
||||
for file in new_files:
|
||||
self._download_file(file)
|
||||
sleep(0.01)
|
||||
8
src/subscription/subscription_object.py
Normal file
8
src/subscription/subscription_object.py
Normal file
@ -0,0 +1,8 @@
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class SubscriptionObject(NamedTuple):
|
||||
source: str
|
||||
buffer: str
|
||||
destination: str
|
||||
apply_filter: bool
|
||||
16
src/tuning/abstraction.py
Normal file
16
src/tuning/abstraction.py
Normal file
@ -0,0 +1,16 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from subscription.subscription_object import SubscriptionObject
|
||||
|
||||
|
||||
class AbstractTuner(ABC):
|
||||
|
||||
busy: bool = False
|
||||
|
||||
@abstractmethod
|
||||
def accept(self, subscriber) -> None:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def notify(self, obj: SubscriptionObject, filename: str):
|
||||
...
|
||||
11
src/tuning/tools.py
Normal file
11
src/tuning/tools.py
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
def filter_signal(signal_name: str):
|
||||
...
|
||||
|
||||
|
||||
def move_signal(signal_name: str):
|
||||
...
|
||||
|
||||
|
||||
def convert_bool_to_int(signal_name: str):
|
||||
...
|
||||
21
src/tuning/tuner.py
Normal file
21
src/tuning/tuner.py
Normal file
@ -0,0 +1,21 @@
|
||||
from os import path
|
||||
import shutil
|
||||
|
||||
from tuning.abstraction import AbstractTuner
|
||||
from subscription.subscription_object import SubscriptionObject
|
||||
import tuning.tools
|
||||
|
||||
|
||||
class Tuner(AbstractTuner):
|
||||
|
||||
def accept(self, subscriber) -> None:
|
||||
subscriber.tuner = self
|
||||
|
||||
def notify(self, obj: SubscriptionObject, filename: str):
|
||||
local_path = path.join(obj.buffer, filename)
|
||||
destination_path = path.join(obj.destination, filename)
|
||||
|
||||
if obj.apply_filter:
|
||||
...
|
||||
else:
|
||||
shutil.move(src=local_path, dst=destination_path)
|
||||
Loading…
Reference in New Issue
Block a user