Module signals_notebook.materials.material

Expand source code
import cgi
import json
import logging
from typing import Any, cast, Optional, TYPE_CHECKING

from pydantic import PrivateAttr

from signals_notebook.api import SignalsNotebookApi
from signals_notebook.common_types import ChemicalDrawingFormat, File, MaterialType, MID
from signals_notebook.materials.base_entity import BaseMaterialEntity
from signals_notebook.materials.field import FieldContainer

if TYPE_CHECKING:
    from signals_notebook.materials.library import Library

log = logging.getLogger(__name__)


class Material(BaseMaterialEntity):

    _material_fields: FieldContainer = PrivateAttr(default={})
    _library: Optional['Library'] = PrivateAttr(default=None)

    def __init__(self, _library: Optional['Library'] = None, **data):
        super().__init__(**data)
        self._library = _library

    def __getitem__(self, key: str) -> Any:
        return self._material_fields[key]

    def __setitem__(self, key: str, value: Any) -> None:
        self._material_fields[key] = value

    @property
    def library(self) -> 'Library':
        """Fetch material library.

        Returns:
            Library
        """
        if not self._library:
            from signals_notebook.materials.material_store import MaterialStore

            library = MaterialStore.get(MID(f'{MaterialType.LIBRARY}:{self.asset_type_id}'))
            self._library = cast('Library', library)

        return self._library

    def get_chemical_drawing(self, format: Optional[ChemicalDrawingFormat] = None) -> File:
        """Export chemical drawing or image of a specified material.

        Args:
            format: Output type of chemical drawing.

        Returns:
            File
        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Get Chemical Drawing as File for %s', self.eid)

        response = api.call(
            method='GET',
            path=(self._get_endpoint(), self.eid, 'drawing'),
            params={
                'format': format,
            },
        )

        content_disposition = response.headers.get('content-disposition', '')
        _, params = cgi.parse_header(content_disposition)

        return File(
            name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
        )

    def get_image(self) -> File:
        """Export image of a specified material except Compounds/Reagents (SNB).

        Returns:
            File
        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Get Image as File for %s', self.eid)

        response = api.call(
            method='GET',
            path=(self._get_endpoint(), self.eid, 'image'),
        )

        content_disposition = response.headers.get('content-disposition', '')
        _, params = cgi.parse_header(content_disposition)

        return File(
            name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
        )

    def get_bio_sequence(self) -> File:
        """Export biological sequence file of a specified material with type is DNA or Protein."

        Returns:
            File
        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Get Bio Sequence as File for %s', self.eid)

        response = api.call(
            method='GET',
            path=(self._get_endpoint(), self.eid, 'bioSequence'),
        )

        content_disposition = response.headers.get('content-disposition', '')
        _, params = cgi.parse_header(content_disposition)

        return File(
            name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
        )

    def get_attachment(self, field_id: str) -> File:
        """Export an attachment for a specified field of the specific material.

        Args:
            field_id: Unique material field identifier.

        Returns:
            File
        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Get Attachment as File for %s', self.eid)

        response = api.call(
            method='GET',
            path=(self._get_endpoint(), self.eid, 'attachments', field_id),
        )

        content_disposition = response.headers.get('content-disposition', '')
        _, params = cgi.parse_header(content_disposition)

        return File(
            name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
        )

    def save(self, force: bool = True) -> None:
        """Update properties of a specified material.

        Args:
            force: Force to update properties without digest check.

        Returns:

        """
        request_body = []

        for field_name, field in self._material_fields.items():
            if field.is_changed:
                request_body.append(
                    {
                        'attributes': {
                            'name': field_name,
                            'value': field.value,
                        }
                    }
                )
        log.debug('Save %s: %s', self.__class__.__name__, self.eid)

        api = SignalsNotebookApi.get_default_api()

        api.call(
            method='PATCH',
            path=(self._get_endpoint(), self.eid, 'properties'),
            params={
                'digest': None if force else self.digest,
                'force': json.dumps(force),
            },
            json={
                'data': request_body,
            },
        )

    def delete(self, digest: str = None, force: bool = True) -> None:
        """Delete Material by ID

        Args:
            eid: material ID
            digest: Indicate digest of entity. It is used to avoid conflict while concurrent editing.
                If the parameter 'force' is true, this parameter is optional.
                If the parameter 'force' is false, this parameter is required.
            force: Force to delete without doing digest check.

        Returns:

        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Deleting Material: %s', self.eid)

        api.call(
            method='DELETE',
            path=('entities', self.eid),
            params={
                'digest': digest,
                'force': json.dumps(force),
            },
        )
        log.debug('Library: %s was deleted successfully', self.eid)

Classes

class Material (**data)

https://github.com/samuelcolvin/pydantic/issues/1577 Adds ability to use properties with setters

Create a new model by parsing and validating input data from keyword arguments.

Raises ValidationError if the input data cannot be parsed to form a valid model.

Expand source code
class Material(BaseMaterialEntity):

    _material_fields: FieldContainer = PrivateAttr(default={})
    _library: Optional['Library'] = PrivateAttr(default=None)

    def __init__(self, _library: Optional['Library'] = None, **data):
        super().__init__(**data)
        self._library = _library

    def __getitem__(self, key: str) -> Any:
        return self._material_fields[key]

    def __setitem__(self, key: str, value: Any) -> None:
        self._material_fields[key] = value

    @property
    def library(self) -> 'Library':
        """Fetch material library.

        Returns:
            Library
        """
        if not self._library:
            from signals_notebook.materials.material_store import MaterialStore

            library = MaterialStore.get(MID(f'{MaterialType.LIBRARY}:{self.asset_type_id}'))
            self._library = cast('Library', library)

        return self._library

    def get_chemical_drawing(self, format: Optional[ChemicalDrawingFormat] = None) -> File:
        """Export chemical drawing or image of a specified material.

        Args:
            format: Output type of chemical drawing.

        Returns:
            File
        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Get Chemical Drawing as File for %s', self.eid)

        response = api.call(
            method='GET',
            path=(self._get_endpoint(), self.eid, 'drawing'),
            params={
                'format': format,
            },
        )

        content_disposition = response.headers.get('content-disposition', '')
        _, params = cgi.parse_header(content_disposition)

        return File(
            name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
        )

    def get_image(self) -> File:
        """Export image of a specified material except Compounds/Reagents (SNB).

        Returns:
            File
        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Get Image as File for %s', self.eid)

        response = api.call(
            method='GET',
            path=(self._get_endpoint(), self.eid, 'image'),
        )

        content_disposition = response.headers.get('content-disposition', '')
        _, params = cgi.parse_header(content_disposition)

        return File(
            name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
        )

    def get_bio_sequence(self) -> File:
        """Export biological sequence file of a specified material with type is DNA or Protein."

        Returns:
            File
        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Get Bio Sequence as File for %s', self.eid)

        response = api.call(
            method='GET',
            path=(self._get_endpoint(), self.eid, 'bioSequence'),
        )

        content_disposition = response.headers.get('content-disposition', '')
        _, params = cgi.parse_header(content_disposition)

        return File(
            name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
        )

    def get_attachment(self, field_id: str) -> File:
        """Export an attachment for a specified field of the specific material.

        Args:
            field_id: Unique material field identifier.

        Returns:
            File
        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Get Attachment as File for %s', self.eid)

        response = api.call(
            method='GET',
            path=(self._get_endpoint(), self.eid, 'attachments', field_id),
        )

        content_disposition = response.headers.get('content-disposition', '')
        _, params = cgi.parse_header(content_disposition)

        return File(
            name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
        )

    def save(self, force: bool = True) -> None:
        """Update properties of a specified material.

        Args:
            force: Force to update properties without digest check.

        Returns:

        """
        request_body = []

        for field_name, field in self._material_fields.items():
            if field.is_changed:
                request_body.append(
                    {
                        'attributes': {
                            'name': field_name,
                            'value': field.value,
                        }
                    }
                )
        log.debug('Save %s: %s', self.__class__.__name__, self.eid)

        api = SignalsNotebookApi.get_default_api()

        api.call(
            method='PATCH',
            path=(self._get_endpoint(), self.eid, 'properties'),
            params={
                'digest': None if force else self.digest,
                'force': json.dumps(force),
            },
            json={
                'data': request_body,
            },
        )

    def delete(self, digest: str = None, force: bool = True) -> None:
        """Delete Material by ID

        Args:
            eid: material ID
            digest: Indicate digest of entity. It is used to avoid conflict while concurrent editing.
                If the parameter 'force' is true, this parameter is optional.
                If the parameter 'force' is false, this parameter is required.
            force: Force to delete without doing digest check.

        Returns:

        """
        api = SignalsNotebookApi.get_default_api()
        log.debug('Deleting Material: %s', self.eid)

        api.call(
            method='DELETE',
            path=('entities', self.eid),
            params={
                'digest': digest,
                'force': json.dumps(force),
            },
        )
        log.debug('Library: %s was deleted successfully', self.eid)

Ancestors

Subclasses

Instance variables

var library : Library

Fetch material library.

Returns

Library

Expand source code
@property
def library(self) -> 'Library':
    """Fetch material library.

    Returns:
        Library
    """
    if not self._library:
        from signals_notebook.materials.material_store import MaterialStore

        library = MaterialStore.get(MID(f'{MaterialType.LIBRARY}:{self.asset_type_id}'))
        self._library = cast('Library', library)

    return self._library

Methods

def delete(self, digest: str = None, force: bool = True) ‑> None

Delete Material by ID

Args

eid
material ID
digest
Indicate digest of entity. It is used to avoid conflict while concurrent editing. If the parameter 'force' is true, this parameter is optional. If the parameter 'force' is false, this parameter is required.
force
Force to delete without doing digest check.

Returns:

Expand source code
def delete(self, digest: str = None, force: bool = True) -> None:
    """Delete Material by ID

    Args:
        eid: material ID
        digest: Indicate digest of entity. It is used to avoid conflict while concurrent editing.
            If the parameter 'force' is true, this parameter is optional.
            If the parameter 'force' is false, this parameter is required.
        force: Force to delete without doing digest check.

    Returns:

    """
    api = SignalsNotebookApi.get_default_api()
    log.debug('Deleting Material: %s', self.eid)

    api.call(
        method='DELETE',
        path=('entities', self.eid),
        params={
            'digest': digest,
            'force': json.dumps(force),
        },
    )
    log.debug('Library: %s was deleted successfully', self.eid)
def get_attachment(self, field_id: str) ‑> File

Export an attachment for a specified field of the specific material.

Args

field_id
Unique material field identifier.

Returns

File

Expand source code
def get_attachment(self, field_id: str) -> File:
    """Export an attachment for a specified field of the specific material.

    Args:
        field_id: Unique material field identifier.

    Returns:
        File
    """
    api = SignalsNotebookApi.get_default_api()
    log.debug('Get Attachment as File for %s', self.eid)

    response = api.call(
        method='GET',
        path=(self._get_endpoint(), self.eid, 'attachments', field_id),
    )

    content_disposition = response.headers.get('content-disposition', '')
    _, params = cgi.parse_header(content_disposition)

    return File(
        name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
    )
def get_bio_sequence(self) ‑> File

Export biological sequence file of a specified material with type is DNA or Protein."

Returns

File

Expand source code
def get_bio_sequence(self) -> File:
    """Export biological sequence file of a specified material with type is DNA or Protein."

    Returns:
        File
    """
    api = SignalsNotebookApi.get_default_api()
    log.debug('Get Bio Sequence as File for %s', self.eid)

    response = api.call(
        method='GET',
        path=(self._get_endpoint(), self.eid, 'bioSequence'),
    )

    content_disposition = response.headers.get('content-disposition', '')
    _, params = cgi.parse_header(content_disposition)

    return File(
        name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
    )
def get_chemical_drawing(self, format: Optional[ChemicalDrawingFormat] = None) ‑> File

Export chemical drawing or image of a specified material.

Args

format
Output type of chemical drawing.

Returns

File

Expand source code
def get_chemical_drawing(self, format: Optional[ChemicalDrawingFormat] = None) -> File:
    """Export chemical drawing or image of a specified material.

    Args:
        format: Output type of chemical drawing.

    Returns:
        File
    """
    api = SignalsNotebookApi.get_default_api()
    log.debug('Get Chemical Drawing as File for %s', self.eid)

    response = api.call(
        method='GET',
        path=(self._get_endpoint(), self.eid, 'drawing'),
        params={
            'format': format,
        },
    )

    content_disposition = response.headers.get('content-disposition', '')
    _, params = cgi.parse_header(content_disposition)

    return File(
        name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
    )
def get_image(self) ‑> File

Export image of a specified material except Compounds/Reagents (SNB).

Returns

File

Expand source code
def get_image(self) -> File:
    """Export image of a specified material except Compounds/Reagents (SNB).

    Returns:
        File
    """
    api = SignalsNotebookApi.get_default_api()
    log.debug('Get Image as File for %s', self.eid)

    response = api.call(
        method='GET',
        path=(self._get_endpoint(), self.eid, 'image'),
    )

    content_disposition = response.headers.get('content-disposition', '')
    _, params = cgi.parse_header(content_disposition)

    return File(
        name=params['filename'], content=response.content, content_type=response.headers.get('content-type')
    )
def save(self, force: bool = True) ‑> None

Update properties of a specified material.

Args

force
Force to update properties without digest check.

Returns:

Expand source code
def save(self, force: bool = True) -> None:
    """Update properties of a specified material.

    Args:
        force: Force to update properties without digest check.

    Returns:

    """
    request_body = []

    for field_name, field in self._material_fields.items():
        if field.is_changed:
            request_body.append(
                {
                    'attributes': {
                        'name': field_name,
                        'value': field.value,
                    }
                }
            )
    log.debug('Save %s: %s', self.__class__.__name__, self.eid)

    api = SignalsNotebookApi.get_default_api()

    api.call(
        method='PATCH',
        path=(self._get_endpoint(), self.eid, 'properties'),
        params={
            'digest': None if force else self.digest,
            'force': json.dumps(force),
        },
        json={
            'data': request_body,
        },
    )