Compare commits
3 Commits
0.1.0.post
...
0.1.0.post
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79518946ac | ||
|
|
6aea5311bb | ||
|
|
b4292a6f57 |
@@ -8,7 +8,8 @@ from uuid import UUID
|
||||
from pathlib import Path
|
||||
import os
|
||||
import tarfile
|
||||
from tempfile import NamedTemporaryFile
|
||||
from tempfile import NamedTemporaryFile, TemporaryDirectory
|
||||
import shutil
|
||||
|
||||
from pydantic import BaseModel
|
||||
from webdav3.client import Client as webdav3_Client
|
||||
@@ -38,6 +39,10 @@ class A_DataSync_Compressor(ABC):
|
||||
def compress(self, path_in: Path, file_out: IO):
|
||||
"""compress method - virtual"""
|
||||
|
||||
@abstractmethod
|
||||
def decompress(self, path_in: Path, path_out: Path):
|
||||
"""decompress method - virtual"""
|
||||
|
||||
|
||||
class DataSync_Compressor_targz(A_DataSync_Compressor):
|
||||
"""Concrete compressor class - .tar.gz compressor"""
|
||||
@@ -47,6 +52,11 @@ class DataSync_Compressor_targz(A_DataSync_Compressor):
|
||||
with tarfile.open(fileobj=file_out, mode="w:gz") as tar:
|
||||
tar.add(path_in, arcname=os.path.basename(path_in))
|
||||
|
||||
def decompress(self, path_in: Path, path_out: Path):
|
||||
"""decompress method - .tar.gz concrete"""
|
||||
with tarfile.open(path_in, "r") as tar:
|
||||
tar.extractall(path_out)
|
||||
|
||||
|
||||
class A_DataSync_Record(BaseModel, ABC):
|
||||
"""Abstract DataSync Record class"""
|
||||
@@ -54,11 +64,16 @@ class A_DataSync_Record(BaseModel, ABC):
|
||||
name: str
|
||||
rec_type: str
|
||||
value: str
|
||||
suffix: str
|
||||
|
||||
@abstractmethod
|
||||
def compress(self, compressor: A_DataSync_Compressor, file_out: IO) -> None:
|
||||
"""compress the record - virtual"""
|
||||
|
||||
@abstractmethod
|
||||
def decompress(self, compressor: A_DataSync_Compressor, path_in: Path) -> None:
|
||||
"""compress the record - virtual"""
|
||||
|
||||
|
||||
class DataSync_Record_Factory:
|
||||
"""DataSync Record Factory"""
|
||||
@@ -85,6 +100,7 @@ class C_DataSync_Record_FS(A_DataSync_Record):
|
||||
"""Concrete DataSync Record class - FileSystem"""
|
||||
|
||||
rec_type: str = "fs"
|
||||
suffix: str = ".tar.gz"
|
||||
path: Optional[Path] = None
|
||||
|
||||
def model_post_init(self, __context) -> None:
|
||||
@@ -96,6 +112,16 @@ class C_DataSync_Record_FS(A_DataSync_Record):
|
||||
assert isinstance(self.path, Path)
|
||||
compressor.compress(self.path, file_out)
|
||||
|
||||
def decompress(self, compressor: A_DataSync_Compressor, path_in: Path) -> None:
|
||||
"""compress the record - concrete FS implementation"""
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(self.path, Path)
|
||||
if os.path.isdir(self.path):
|
||||
shutil.rmtree(self.path)
|
||||
if os.path.isfile(self.path):
|
||||
os.remove(self.path)
|
||||
compressor.decompress(path_in, self.path.parent)
|
||||
|
||||
|
||||
class I_DataSync(ABC):
|
||||
"""Abstract DataSync class"""
|
||||
@@ -105,23 +131,23 @@ class I_DataSync(ABC):
|
||||
|
||||
@classmethod
|
||||
@final
|
||||
def get_manifest_data(cls) -> dict[Any, Any]:
|
||||
def get_manifest_data(cls) -> dict[str, Any]:
|
||||
"""utilitary method to get manifest"""
|
||||
with open(cls.manifest_path, encoding="utf-8") as f_DAB_manifest:
|
||||
return json.load(f_DAB_manifest)
|
||||
|
||||
@classmethod
|
||||
@final
|
||||
def try_get_instance(cls, manifest) -> Self | None:
|
||||
def try_get_instance(cls, manifest: dict[str, Any]) -> Self | None:
|
||||
"""try to get an instance of a concrete class"""
|
||||
if cls.test_applicable(manifest):
|
||||
return cls(manifest)
|
||||
return None
|
||||
|
||||
def __init__(self, manifest: dict[Any, Any]) -> None:
|
||||
def __init__(self, manifest: dict[str, Any]) -> None:
|
||||
self.connected: bool = False
|
||||
self.compressor: A_DataSync_Compressor = type(self).cls_compressor()
|
||||
self.manifest: dict[Any, Any] = manifest
|
||||
self.manifest: dict[str, Any] = manifest
|
||||
self.app_id: UUID = UUID(manifest["APP_ID"])
|
||||
self.ar_datasync_record: list[A_DataSync_Record] = []
|
||||
if "FSSYNC_RECORD" in manifest["Args"]:
|
||||
@@ -135,8 +161,8 @@ class I_DataSync(ABC):
|
||||
self.ar_datasync_record.append(record)
|
||||
|
||||
@classmethod
|
||||
def test_applicable(cls, manifest: dict[Any, Any]) -> bool:
|
||||
"""check if a concrete class is applicable"""
|
||||
def test_applicable(cls, manifest: dict[str, Any]) -> bool:
|
||||
"""check if a concrete class is applicable - generic"""
|
||||
del manifest # quality warning removal
|
||||
return False
|
||||
|
||||
@@ -161,18 +187,22 @@ class I_DataSync(ABC):
|
||||
def read_data(self) -> None:
|
||||
"""read data from the service"""
|
||||
self.connect()
|
||||
self._impl_read_data()
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
for datasync_record in self.ar_datasync_record:
|
||||
self._impl_read_data(Path(datasync_record.name + datasync_record.suffix), Path(tmpdir))
|
||||
datasync_record.decompress(self.compressor, Path(tmpdir) / (datasync_record.name + datasync_record.suffix))
|
||||
|
||||
@abstractmethod
|
||||
def _impl_read_data(self) -> None:
|
||||
def _impl_read_data(self, file_in: Path, file_out: Path) -> None:
|
||||
"""read data from the service - virtual"""
|
||||
|
||||
def write_data(self) -> None:
|
||||
"""write data to the service"""
|
||||
self.connect()
|
||||
self._impl_wipe_data()
|
||||
for datasync_record in self.ar_datasync_record:
|
||||
try:
|
||||
with NamedTemporaryFile("wb", suffix=".tar.gz", delete=False) as tmp_file:
|
||||
with NamedTemporaryFile("wb", suffix=datasync_record.suffix, delete=False) as tmp_file:
|
||||
datasync_record.compress(self.compressor, tmp_file)
|
||||
tmp_file.seek(0)
|
||||
tmp_file.close()
|
||||
@@ -185,6 +215,15 @@ class I_DataSync(ABC):
|
||||
def _impl_write_data(self, record_name: str, file_in: IO) -> None:
|
||||
"""write data to the service - virtual"""
|
||||
|
||||
def wipe_data(self) -> None:
|
||||
"""wipe data on the service"""
|
||||
self.connect()
|
||||
self._impl_wipe_data()
|
||||
|
||||
@abstractmethod
|
||||
def _impl_wipe_data(self) -> None:
|
||||
"""wipe data on the service - virtual"""
|
||||
|
||||
|
||||
class DataSync_Factory:
|
||||
"""DataSync Factory"""
|
||||
@@ -222,7 +261,8 @@ class C_DataSync_NextCloud(I_DataSync):
|
||||
self.connected: bool = False
|
||||
|
||||
@classmethod
|
||||
def test_applicable(cls, manifest) -> bool:
|
||||
def test_applicable(cls, manifest: dict[str, Any]) -> bool:
|
||||
"""check if a concrete class is applicable - Nextcloud override"""
|
||||
if "Args" in manifest:
|
||||
if "FSSync_NextCloud_Enabled" in manifest["Args"]:
|
||||
if manifest["Args"]["FSSync_NextCloud_Enabled"]["value"] is True:
|
||||
@@ -231,7 +271,7 @@ class C_DataSync_NextCloud(I_DataSync):
|
||||
raise DataSyncException_InvalidManifest()
|
||||
|
||||
def _impl_configure(self) -> None:
|
||||
"""configure the class instance - concrete implementation"""
|
||||
"""configure the class instance - Nextcloud concrete implementation"""
|
||||
if "FSSync_NextCloud_Address" in self.manifest["Args"]:
|
||||
self.nextcloud_address = self.manifest["Args"]["FSSync_NextCloud_Address"]["value"]
|
||||
else:
|
||||
@@ -253,7 +293,7 @@ class C_DataSync_NextCloud(I_DataSync):
|
||||
raise DataSyncException_InvalidManifest()
|
||||
|
||||
def _impl_connect(self) -> None:
|
||||
"""connect to the remote service - concrete implementation"""
|
||||
"""connect to the remote service - Nextcloud concrete implementation"""
|
||||
full_adress = urljoin(self.nextcloud_address, "remote.php/dav/files/", self.nextcloud_user)
|
||||
self.client = webdav3_Client(
|
||||
{"webdav_hostname": full_adress, "webdav_login": self.nextcloud_user, "webdav_password": self.nextcloud_password}
|
||||
@@ -267,14 +307,22 @@ class C_DataSync_NextCloud(I_DataSync):
|
||||
if not self.client.check(url_accumulator):
|
||||
self.client.mkdir(url_accumulator)
|
||||
|
||||
def _impl_read_data(self) -> None:
|
||||
"""read data from the remote service - concrete implementation"""
|
||||
def _impl_read_data(self, file_in: Path, file_out: Path) -> None:
|
||||
"""read data from the remote service - Nextcloud concrete implementation"""
|
||||
self._check_create_dir()
|
||||
self.client.download_sync(
|
||||
remote_path=str(self.nextcloud_path / file_in).replace(os.sep, "/"), local_path=str(file_out / file_in).replace(os.sep, "/")
|
||||
)
|
||||
|
||||
def _impl_write_data(self, record_name: str, file_in: IO) -> None:
|
||||
"""write data to the remote service - concrete implementation"""
|
||||
"""write data to the remote service - Nextcloud concrete implementation"""
|
||||
self._check_create_dir()
|
||||
self.client.upload_sync(
|
||||
remote_path=str(Path(self.nextcloud_path) / record_name).replace(os.sep, "/"),
|
||||
local_path=file_in.name,
|
||||
)
|
||||
|
||||
def _impl_wipe_data(self) -> None:
|
||||
"""wipe data on the service - concrete implementation"""
|
||||
if self.client.check(self.nextcloud_path):
|
||||
self.client.clean(self.nextcloud_path)
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
|
||||
import unittest
|
||||
from os import chdir
|
||||
from io import StringIO
|
||||
from contextlib import redirect_stdout, redirect_stderr
|
||||
from pathlib import Path
|
||||
import pprint
|
||||
import shutil
|
||||
|
||||
print(__name__)
|
||||
print(__package__)
|
||||
@@ -25,6 +24,14 @@ chdir(testdir_path.parent.resolve())
|
||||
class TestDabDataSync(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
chdir(testdir_path.parent.resolve())
|
||||
shutil.rmtree(testdir_path / "test_data", ignore_errors=True)
|
||||
shutil.rmtree(testdir_path / "test_data2", ignore_errors=True)
|
||||
shutil.copytree(testdir_path / "test_data_origin", testdir_path / "test_data")
|
||||
shutil.copytree(testdir_path / "test_data_origin", testdir_path / "test_data2")
|
||||
|
||||
def tearDown(self) -> None:
|
||||
shutil.rmtree(testdir_path / "test_data", ignore_errors=True)
|
||||
shutil.rmtree(testdir_path / "test_data2", ignore_errors=True)
|
||||
|
||||
def test_version(self):
|
||||
self.assertNotEqual(dabdatasync.__version__, "?.?.?")
|
||||
@@ -34,10 +41,75 @@ class TestDabDataSync(unittest.TestCase):
|
||||
datasync = dabdatasync.DataSync_Factory.get_DataSync()
|
||||
self.assertIsInstance(datasync, dabdatasync.I_DataSync)
|
||||
self.assertIsInstance(datasync, dabdatasync.C_DataSync_NextCloud)
|
||||
datasync.read_data()
|
||||
pprint.pprint(datasync.ar_datasync_record)
|
||||
|
||||
self.assertEqual(len(datasync.ar_datasync_record), 2)
|
||||
self.assertEqual(datasync.ar_datasync_record[0].name, "SOTF_map")
|
||||
self.assertEqual(datasync.ar_datasync_record[1].name, "SOTF_map2")
|
||||
self.assertEqual(datasync.ar_datasync_record[0].rec_type, "fs")
|
||||
self.assertEqual(datasync.ar_datasync_record[1].rec_type, "fs")
|
||||
self.assertEqual(datasync.ar_datasync_record[0].value, "test/test_data")
|
||||
self.assertEqual(datasync.ar_datasync_record[1].value, "test/test_data2/SAVE_FILE.txt")
|
||||
self.assertEqual(datasync.ar_datasync_record[0].suffix, ".tar.gz")
|
||||
self.assertEqual(datasync.ar_datasync_record[1].suffix, ".tar.gz")
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "SAVED_VALUE")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "SAVED_VALUE")
|
||||
|
||||
datasync.write_data()
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "SAVED_VALUE")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "SAVED_VALUE")
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "w", encoding="utf-8") as testfile:
|
||||
testfile.write("MODIFIED_VALUE")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "w", encoding="utf-8") as testfile:
|
||||
testfile.write("MODIFIED_VALUE2")
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "MODIFIED_VALUE")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "MODIFIED_VALUE2")
|
||||
|
||||
datasync.read_data()
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "SAVED_VALUE")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "SAVED_VALUE")
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "w", encoding="utf-8") as testfile:
|
||||
testfile.write("MODIFIED_VALUE3")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "w", encoding="utf-8") as testfile:
|
||||
testfile.write("MODIFIED_VALUE32")
|
||||
|
||||
datasync.write_data()
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "MODIFIED_VALUE3")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "MODIFIED_VALUE32")
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "w", encoding="utf-8") as testfile:
|
||||
testfile.write("MODIFIED_VALUE")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "w", encoding="utf-8") as testfile:
|
||||
testfile.write("MODIFIED_VALUE")
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "MODIFIED_VALUE")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "MODIFIED_VALUE")
|
||||
|
||||
datasync.read_data()
|
||||
|
||||
with open(testdir_path / "test_data" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "MODIFIED_VALUE3")
|
||||
with open(testdir_path / "test_data2" / "SAVE_FILE.txt", "rt", encoding="utf-8") as testfile:
|
||||
self.assertEqual(testfile.read(), "MODIFIED_VALUE32")
|
||||
|
||||
def test_load_empty(self):
|
||||
dabdatasync.I_DataSync.manifest_path = testdir_path / "test_manifest_empty.json"
|
||||
datasync = dabdatasync.DataSync_Factory.get_DataSync()
|
||||
|
||||
@@ -220,6 +220,23 @@
|
||||
"value": "test/test_data"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "T_FSSYNC_RECORD",
|
||||
"value": {
|
||||
"name": {
|
||||
"type": "SIMPLE_STRING",
|
||||
"value": "SOTF_map2"
|
||||
},
|
||||
"type": {
|
||||
"type": "SIMPLE_STRING",
|
||||
"value": "fs"
|
||||
},
|
||||
"value": {
|
||||
"type": "STRING",
|
||||
"value": "test/test_data2/SAVE_FILE.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user