Skip to content

Code Architecture

nh916 edited this page Jul 31, 2023 · 12 revisions

How Is the Code Structured?

This is a great place to learn more about the code, how it is structured, in what order, and how to read it.

It is often difficult to get 100s of files of code with many directories and be able to know how it all works easily. This documentation attempts to make that process immensely easier.

Data Integrity and Giant JSON

In the previous SDK, nodes had to be saved one at a time, this created issues because if half way through the script the user got an error, then within the database half of the project would have been uploaded and half would not have been. This becomes a bigger issue for update when if the script has a bug half way through upload, then half of the nodes are updated and half are outdated creating bad data in total.


API

The API client class is meant to be used to interact with the API. An instance of the object is available globally for the whole SDK to use when needing to interact with the db schema, know the host the user defined, upload files, or more.

HTTP Headers

_http_headers variable is created in the cript.API() object to be able to interact with the API through http requests

AWS S3

S3 variable/property is created from the api storage_token and this object is only initialized and cached if the user needs to start upload files to cloud storage, otherwise, it is never initialized as it is not needed.

cript.API.save()

Please refer to save & update wiki documentation


Nodes

Node Attributes

All node attributes are encapsulated in Python dataclass to easily serialize and deserialize

    @dataclass(frozen=True)
    class JsonAttributes(PrimaryBaseNode.JsonAttributes):
        """
        all Material attributes
        """
        # identifier sub-object for the material
        identifiers: List[Dict[str, str]] = field(default_factory=dict)  # type: ignore
        component: List["Material"] = field(default_factory=list)
        process: Optional[Process] = None
        property: List[Any] = field(default_factory=list)
        parent_material: Optional["Material"] = None
        computational_forcefield: Optional[Any] = None
        keyword: List[str] = field(default_factory=list)

    _json_attrs: JsonAttributes = JsonAttributes()

all node attributes for the class are manipulated with getters and setters

    @property
    @beartype
    def keyword(self) -> List[str]:
        return self._json_attrs.keyword

    @keyword.setter
    @beartype
    def keyword(self, new_keyword_list: List[str]) -> None:
        new_attrs = replace(self._json_attrs, keyword=new_keyword_list)
        self._update_json_attrs_if_valid(new_attrs)

Property special case

There are some places that the node attribute is named property and python has a reserved keyword called property, for those scenarios we have put the property of the node at the bottom of the file, so that it doesn't create and issues with name clashes

Upgrade: in the future we are planning to use Pydantic and maybe we can use the python convention of calling it property_ to not clash with the name. That way when we turn the node to JSON we call it property but when we are using it within Python we call it property_

Serialization and Deserialization

Please refer to serialization & deserialization wiki

Exceptions

Please refer to Exceptions wiki

Documentation

Please refer to documentation wiki

Tests

please refer to Tests Wiki