فهرست منبع

Sync to cloud.

Cixo Develop 1 ماه پیش
والد
کامیت
793a16ee95

+ 2 - 0
requirements.txt

@@ -1 +1,3 @@
 tortoise-orm
+aiofiles
+fastapi

+ 2 - 0
server_source/__init__.py

@@ -25,6 +25,8 @@ from .product_type import product_type_proxy
 
 from .attachment import attachment 
 from .attachment import attachment_proxy
+from .attachment_file import attachment_file
+from .attachments_manager import attachments_manager
 
 from .item import item
 

+ 20 - 0
server_source/aleatory_file_name.py

@@ -1,5 +1,6 @@
 import os
 import string
+import pathlib
 
 class aleatory_file_name:
     """
@@ -37,6 +38,25 @@ class aleatory_file_name:
         name = cls.__get_random_name(name_lenght)
         return cls.__get_file_name(name, extension)
 
+    @classmethod
+    def path(cls, *args, **kwargs) -> pathlib.Path:
+        """
+        That function work same as initializer, but return pathlib.Path,
+        instread of string.
+
+        Parameters
+        ----------
+        *args, **kwargs
+            Same as __new__ parameters.
+
+        Returns
+        -------
+        pathlib.Path
+            Path instread of string.
+        """
+
+        return pathlib.Path(cls(*args, **kwargs))
+
     @classmethod
     def __get_chars(cls) -> str:
         """

+ 19 - 83
server_source/attachment.py

@@ -5,7 +5,6 @@ from .proxy import proxy
 from .field_generator import field_generator
 from .constants import constants
 from .validators import validators
-from .attachment_file import attachment_file
 from .exceptions import resources_not_exists
 
 class attachment(model):
@@ -26,7 +25,10 @@ class attachment(model):
     name = field_generator.name()
     description = field_generator.description()
     resources = field_generator.path()
-    
+   
+    @property
+    def resources_path(self) -> pathlib.Path:
+        return pathlib.Path(self.resources)
 
     def _validators(self) -> dict:
         """
@@ -46,95 +48,29 @@ class attachment(model):
         }
 
 class attachment_proxy(proxy):   
-    """
-    That class is proxy for the attachment. It could be used to working
-    witch attachments in cleaner and better way.
-
-    Methods
-    -------
-    @classmethod create : proxy
-        That create new attachment from that name.
-
-    set_name : None
-        That change visible name of the attachment.
-
-    set_description : None
-        That change description of the attachment.
-    """
-
     @classmethod
-    def create(cls, name: str, resources: attachment_file) -> proxy:
-        """
-        That create new proxy with new object, which does not exists in 
-        database inside.
-
-        Parameters
-        ----------
-        name : str
-            Name of the new attachment.
-        resources : str
-            Path in the resources directory to the attachment file.
-
-        Raises
-        ------
-        resources_not_exists
-            When attachment file does not exists on disk.
-
-        Returns
-        -------
-        proxy
-            New proxy with new attachment inside it.
-        """
-
-        if not resources.exists():
-            raise resources_not_exists(resources.path)
-
+    def create(cls, resources: str) -> proxy:
         return cls(attachment(
-            name = name,
-            description = constants.empty_text(),
-            resources = resources.name
+            name = cls.__extract_name(resources),
+            resources = resources,
+            description = constants.empty_text()
         ))
 
-    def set_resources(self, target: attachment_file) -> None:
-        """
-        That change path to the attachment file in the resources directory.
-
-        Parameters
-        ----------
-        target : pathlib.Path
-            Path to new attachment file in the resources directory.
-
-        Raises
-        ------
-        resources_not_exists
-            When attachment file does not exists on disk.
-        """
-        
-        if not target.exists():
-            raise resources_not_exists(target.path)
+    @classmethod
+    def __extract_name(cls, resources: str) -> str:
+        splited = resources.split(".")
 
-        self._target.resources = target.name
+        if len(splited) == 1:
+            return splited[0]
 
-    def set_name(self, target: str) -> None:
-        """
-        That change name of the attachment
-
-        Parameters
-        ----------
-        target : str
-            New name of the attachment to set.
-        """
+        return str(".").join(splited[:-1])
 
+    def set_name(self, target: str) -> object:
         self._target.name = target
+        return self
 
-    def set_description(self, target: str) -> None:
-        """
-        That chanhe description of the attachment.
+    def set_description(self, target: str) -> object:
+        self._target.description = target
+        return self
 
-        Parameters
-        ----------
-        target : str
-            New description of the attachment to set.
-        """
 
-        self._target.description = target

+ 33 - 29
server_source/attachment_file.py

@@ -1,49 +1,53 @@
 import pathlib 
 import aiofiles
 
+from .attachment import attachment
+from .attachment import attachment_proxy
 from .aleatory_file_name import aleatory_file_name
 
 class attachment_file:
+    @classmethod
+    def create(cls, directory: pathlib.Path, extension: str) -> object:
+        while True:
+            new_name = aleatory_file_name.path(extension)
+            new_path = directory / new_name
+
+            if not new_path.exists() and not new_path.is_file():
+                return cls(new_name, directory)
+    
     def __init__(
         self, 
-        extension: str,
-        root: pathlib.Path
+        file_name: pathlib.Path, 
+        directory: pathlib.Path
     ) -> None:
-        self.__root = root
-        self.__name = self.__get_name(extension)
-
-    async def load(self) -> bytes:
-        async with aiofiles.open(self.path, "rb") as handler:
-            return await handler.read()
+        self.__file_name = file_name
+        self.__directory = directory
+        self.__file_path = directory / file_name
 
-    async def store(self, content: bytes) -> object:
-        async with aiofiles.open(self.path, "wb") as handler:
-            await handler.write(content)
-        
-        return self
+    @property
+    def path(self) -> pathlib.Path:
+        return self.__file_path
 
-    def __get_name(self, extension: str) -> str:
-        while True:
-            new_name = aleatory_file_name(extension)
-            new_path = self.root / pathlib.Path(new_name)
+    @property
+    def directory(self) -> pathlib.Path:
+        return self.__directory
 
-            if not new_path.is_file() and not new_path.exists():
-                return new_name 
-    
-    def exists(self) -> bool:
-        return self.path.exists() and self.path.is_file()
-     
     @property
     def name(self) -> str:
-        return self.__name
+        return str(self.__file_name)
 
     @property
-    def root(self) -> pathlib.Path: 
-        return self.__root
+    def relative_path(self) -> pathlib.Path:
+        return self.__file_name
 
-    @property
-    def path(self) -> pathlib.Path:
-        return self.root / pathlib.Path(self.name)
+    async def store(self, content: bytes) -> attachment_proxy:
+        async with aiofiles.open(self.path, "wb") as handler:
+            await handler.write(content)
+
+        return attachment_proxy.create(self.name)
 
+    async def load(self) -> bytes:
+        async with aiofiles.open(self.path, "rb") as handler:
+            return await handler.read()
         
         

+ 18 - 7
server_source/attachments_manager.py

@@ -1,6 +1,9 @@
-import pathlib
 import base64
+import pathlib
+import asyncio
 
+from .attachment import attachment
+from .attachment import attachment_proxy
 from .attachment_file import attachment_file
 from .exceptions import resources_directory_not_exists
 
@@ -16,15 +19,23 @@ class attachments_manager:
         if not resources.is_dir() or not resources.exists():
             raise resources_directory_not_exists(resources)
 
-        self.__resources = resouces
+        self.__resources = resources
 
     @property
     def resources(self) -> pathlib.Path:
         return self.__resources
 
-    def uploaded(self, content: str, extension: str) -> attachment_file:
-        content_bytes = content.encode("ascii")
-        decoded = base64.b64decode(content_bytes)
+    async def __decode(self, content: str) -> bytes:
+        return await asyncio.to_thread(
+            base64.b64decode,
+            content.encode("ascii")
+        )   
+
+    async def upload(self, content: str, extension: str) -> attachment_file:
+        decoded = await self.__decode(content)
+        file_handler = attachment_file.create(self.resources, extension)
+
+        return await file_handler.store(decoded)
 
-        return attachment_file(decoded, extension, self.__resources)
-        
+    def restore(self, file: attachment) -> attachment_file:
+        return attachment_file(file.resources_path, self.resources) 

+ 34 - 0
server_source/model.py

@@ -46,6 +46,40 @@ class model(tortoise.models.Model):
 
     id = field_generator.id()
 
+    def __repr__(self) -> str:
+        result = "Dump of " + self.__class__.__name__
+        
+        if self.id is not None:
+            result = result + " #" + str(self.id)
+        
+        result = result + "\n"
+
+        for count in dir(self):
+            if count[0] == "_":
+                continue
+
+            if count == "id":
+                continue
+
+            if count == "pk":
+                continue
+
+            field = self.__getattribute__(count)
+
+            if type(field) is str:
+                result = result + " " + count + ": \"" + field + "\"\n"
+                continue
+
+            if type(field) is int or type(field) is float:
+                result = result + " " + count + ": " + str(field) + "\n"
+                continue
+
+            if type(field) is bool:
+                result = result + " " + count + ": " + str(field) + "\n"
+                continue
+
+        return result
+        
     def _validators(self) -> dict:
         """
         That return dict of fields, which must be validated, with validators

+ 1 - 0
server_tests/008-random_name.py

@@ -12,3 +12,4 @@ from test import test
 print(source.aleatory_file_name("txt"))
 print(source.aleatory_file_name())
 print(source.aleatory_file_name("jpg"))
+print(source.aleatory_file_name.path("jpg"))

+ 69 - 0
server_tests/009-attachment.py

@@ -0,0 +1,69 @@
+import sys
+import pathlib
+
+test_file = pathlib.Path(__file__)
+project = test_file.parent.parent
+
+sys.path.append(str(project))
+
+import server_source as source
+from test import test
+
+import asyncio
+import tortoise
+
+def prepare_dir() -> pathlib.Path:
+    test = pathlib.Path(__file__).parent
+    resources = test / pathlib.Path("test_resources")
+
+    if not resources.exists() or not resources.is_dir():
+        resources.mkdir()
+    
+    for count in resources.iterdir():
+        if count.is_file():
+            count.unlink()
+
+    return resources
+
+async def main():
+    modules = {
+        source.model.Meta.app: [ "server_source" ]
+    }
+
+    await tortoise.Tortoise.init(
+        db_url = "sqlite://:memory:", 
+        modules = modules
+    )
+
+    await tortoise.Tortoise.generate_schemas()
+
+    content = "IyBVd1UKICogRmlyc3QgcG9pbnQKICogU2Vjb25kIHBvaW50Cg=="
+
+    manager = source.attachments_manager(prepare_dir())
+    proxy = await manager.upload(content, "md")
+    attachment = proxy \
+    .set_name("sample_file") \
+    .set_description("This describe it.") \
+    .result()
+
+    print("Attachment before insert to DB:")
+    print(repr(attachment))
+    print()
+
+    await attachment.save()
+    
+    print("Attachment after inserting to DB:")
+    print(repr(attachment))
+    print()
+
+    readed = await manager.restore(attachment).load()
+
+    print("Data loaded from Base64:")
+    print("\"\"\"")
+    print(readed.decode("UTF-8"))
+    print("\"\"\"")
+    print()
+
+    await tortoise.Tortoise.close_connections()
+
+asyncio.run(main())

+ 3 - 0
server_tests/test_resources/MuVVeqsRh8fEg7KTjcri7uLFVo6by.md

@@ -0,0 +1,3 @@
+# UwU
+ * First point
+ * Second point