Module app.graphql_model

Model to inject a another graphene model, to manage the haystack layer. See the blueprint_graphql to see how to integrate this part of global GraphQL model.

Expand source code
# -*- coding: utf-8 -*-
# Haystack API Provider module
# See the accompanying LICENSE file.
# (C) 2021 Engie Digital
#
# vim: set ts=4 sts=4 et tw=78 sw=4 si:
"""
Model to inject a another graphene model, to manage the haystack layer.
See the `blueprint_graphql` to see how to integrate this part of global GraphQL model.
"""
import json
import logging
import os
from datetime import datetime, date, time
from typing import Optional, List, Any, Union, Type, Dict, cast

import graphene
import pytz
from graphql import ResolveInfo
from graphql.language.ast import StringValue, IntValue, FloatValue, BooleanValue, EnumValue

import shaystack
from shaystack import Ref, Uri, Coordinate, parse_hs_datetime_format, Grid
from shaystack.grid_filter import parse_hs_time_format, parse_hs_date_format
from shaystack.providers.haystack_interface import get_singleton_provider, parse_date_range
from shaystack.type import Entity
# noinspection PyProtectedMember,PyProtectedMember,PyProtectedMember
from shaystack.zincdumper import _dump_hs_date_time, _dump_hs_time, _dump_hs_date

BOTO3_AVAILABLE = False
try:
    # Check the presence of boto3
    import boto3  # pylint: disable=unused-import

    BOTO3_AVAILABLE = True
except ImportError:
    pass

log = logging.getLogger("shaystack")


class HSScalar(graphene.Scalar):
    """Haystack Scalar"""

    class Meta:  # pylint: disable=too-few-public-methods
        """Update name for AWS"""
        name = "AWSJSON" if BOTO3_AVAILABLE else "JSONString"

    @staticmethod
    def serialize(hs_scalar: Any) -> Any:
        """
        Convert scala value to Json
        Args:
            hs_scalar: Any value
        """
        return json.loads(shaystack.dump_scalar(hs_scalar,
                                                shaystack.MODE_JSON,
                                                version=shaystack.VER_3_0))

    @staticmethod
    def parse_literal(node: Union[IntValue, FloatValue, StringValue, BooleanValue, EnumValue]) -> Any:
        """
        Parse to scalar value
        Args:
            node: The graphql node
        """
        if isinstance(node, StringValue):
            str_value = node.value
            if len(str_value) >= 2 and str_value[1] == ':':
                return shaystack.parse_scalar(node.value,
                                              shaystack.MODE_JSON)
        return node.value

    @staticmethod
    def parse_value(value: str) -> Any:
        """
        Args:
            value: The value to parse
        """
        return shaystack.parse_scalar(value, shaystack.MODE_JSON)


class HSDateTime(graphene.String):
    """Haystack compatible date format."""

    class Meta:  # pylint: disable=missing-class-docstring
        name = "AWSDateTime" if BOTO3_AVAILABLE else "DateTime"

    @staticmethod
    def serialize(date_time: datetime) -> str:
        """
        Convert date_time to json compatible string
        Args:
            date_time: The date time to convert to json
        """
        assert isinstance(date_time, datetime), \
            'Received not compatible datetime "{}"'.format(repr(date_time))
        return _dump_hs_date_time(date_time)

    @staticmethod
    def parse_literal(node: StringValue) -> datetime:  # pylint: disable=arguments-differ
        # Call to convert graphql parameter to python object
        """
        Convert a haystack json string to data time
        Args:
            node: GraphQL node to convert
        """
        assert isinstance(node, StringValue), \
            'Received not compatible datetime "{}"'.format(repr(node))
        return HSDateTime.parse_value(node.value)

    @staticmethod
    def parse_value(value: Union[datetime, date, str]) -> datetime:
        # Call to convert graphql variable to python object
        """
        Args:
            value:
        """
        if isinstance(value, datetime):
            return value
        if isinstance(value, date):
            return datetime.combine(value, datetime.max.time()).replace(tzinfo=pytz.UTC)
        return parse_hs_datetime_format(value, pytz.UTC)


class HSDate(graphene.String):
    """Haystack date for GraphQL"""

    class Meta:  # pylint: disable=missing-class-docstring
        name = "AWSDate" if BOTO3_AVAILABLE else "Date"

    @staticmethod
    def serialize(a_date: date) -> str:
        """
        Convert date to haystack json string.
        Args:
            a_date: A date
        """
        assert isinstance(a_date, date), 'Received not compatible date "{}"'.format(repr(a_date))
        return _dump_hs_date(a_date)

    @staticmethod
    def parse_literal(node: StringValue) -> date:  # pylint: disable=arguments-differ
        """
        Convert the Graphql string to date.
        Args:
            node: The graphql string
        """
        assert isinstance(node, StringValue), 'Received not compatible date "{}"'.format(repr(node))
        return HSDate.parse_value(node.value)

    @staticmethod
    def parse_value(value: Union[date, str]) -> date:
        """
        Convert a date to haystack json.
        Args:
            value: The date
        """
        if isinstance(value, date):
            return value
        return parse_hs_date_format(value)


class HSTime(graphene.String):
    """Haystack time for GraphQL"""

    class Meta:  # pylint: disable=missing-class-docstring
        name = "AWSTime" if BOTO3_AVAILABLE else "Time"

    @staticmethod
    def serialize(a_time: time) -> str:
        """
        Convert a time to Haystack json string
        Args:
            a_time: The time.
        """
        assert isinstance(a_time, time), 'Received not compatible time "{}"'.format(repr(a_time))
        return _dump_hs_time(a_time)

    @staticmethod
    def parse_literal(node: StringValue) -> time:  # pylint: disable=arguments-differ
        """
        Convert a haystack json string to time
        Args:
            node: The string
        """
        assert isinstance(node, StringValue), 'Received not compatible time "{}"'.format(repr(node))
        return HSTime.parse_value(node.value)

    @staticmethod
    def parse_value(value: Union[time, str]) -> time:
        """
        Args:
            value:
        """
        if isinstance(value, time):
            return value
        return parse_hs_time_format(value)


class HSUri(graphene.String):
    """Haystack URI for GraphQL"""

    class Meta:  # pylint: disable=too-few-public-methods,missing-class-docstring
        name = "AWSURL" if BOTO3_AVAILABLE else "HSURL"

    @staticmethod
    def serialize(a_uri: Uri) -> str:
        """
        Convert an Uri to Haystack json string
        Args:
            a_uri: The Uri
        """
        assert isinstance(a_uri, Uri), 'Received not compatible uri "{}"'.format(repr(a_uri))
        return str(a_uri)

    @staticmethod
    def parse_literal(node: StringValue) -> Uri:  # pylint: disable=arguments-differ
        """
        Convert the Graphql string to Uri
        Args:
            node: The string
        """
        return HSUri.parse_value(node.value)

    @staticmethod
    def parse_value(value: str) -> Uri:
        """
        Convert a haystack json string to Uri.
        Args:
            value: The string
        """
        if value.startswith("u:"):
            return shaystack.parse_scalar(value, shaystack.MODE_JSON, version=shaystack.VER_3_0)
        return Uri(value)


class HSCoordinate(graphene.ObjectType):  # pylint: disable=too-few-public-methods
    """Haystack coordinate for GraphQL"""
    latitude = graphene.Float(required=True,
                              description="Latitude")
    longitude = graphene.Float(required=True,
                               description="Longitude")


class HSAbout(graphene.ObjectType):  # pylint: disable=too-few-public-methods
    """Result of 'about' haystack operation"""
    haystackVersion = graphene.String(required=True,
                                      description="Haystack version implemented")
    tz = graphene.String(required=True,
                         description="Server time zone")
    serverName = graphene.String(required=True,
                                 description="Server name")
    serverTime = graphene.Field(graphene.NonNull(HSDateTime),
                                description="Server current time")
    serverBootTime = graphene.Field(graphene.NonNull(HSDateTime),
                                    description="Server boot time")
    productName = graphene.String(required=True,
                                  description="Server Product name")
    productUri = graphene.Field(graphene.NonNull(HSUri),
                                description="Server URL")
    productVersion = graphene.String(required=True,
                                     description="Product version")
    moduleName = graphene.String(required=True,
                                 description="Module name")
    moduleVersion = graphene.String(required=True,
                                    description="Module version")


class HSOps(graphene.ObjectType):  # pylint: disable=too-few-public-methods
    """Result of 'ops' haystack operation"""

    name = graphene.String(description="Name of operation (see https://project-haystack.org/doc/docHaystack/Ops)")
    summary = graphene.String(description="Summary of operation")


class HSTS(graphene.ObjectType):  # pylint: disable=too-few-public-methods,too-many-instance-attributes
    """Result of 'hisRead' haystack operation"""
    ts = graphene.Field(HSDateTime, description="Date time of event")
    val = graphene.Field(HSScalar, description="Haystack JSON format of value")

    int = graphene.Int(required=False, description="Integer version of the value")
    float = graphene.Float(required=False, description="Float version of the value")
    str = graphene.String(required=False, description="Float version of the value")
    bool = graphene.Boolean(required=False, description="Boolean version of the value")
    uri = graphene.String(required=False, description="URI version of the value")
    ref = graphene.String(required=False, description="Reference version of the value")
    date = HSDate(required=False, description="Date version of the value")
    time = HSTime(required=False, description="Time version of the value")
    datetime = HSDateTime(required=False, description="Date time version of the value")
    coord = graphene.Field(HSCoordinate,
                           description="Geographic Coordinate")


class HSPointWrite(graphene.ObjectType):  # pylint: disable=too-few-public-methods
    """Result of 'pointWrite' haystack operation"""
    level = graphene.Int(description="Current level")
    levelDis = graphene.String(description="Description of level")
    val = graphene.Field(HSScalar, description="Value")
    who = graphene.String(description="Who has updated the value")


# PPR: see the batch approach
class ReadHaystack(graphene.ObjectType):
    """Ontology conform with Haystack project"""

    class Meta:  # pylint: disable=too-few-public-methods,missing-class-docstring
        name = "Haystack"

    about = graphene.NonNull(HSAbout,
                             description="Versions of api")

    ops = graphene.NonNull(graphene.List(
        graphene.NonNull(HSOps)),
        description="List of operation implemented")

    tag_values = graphene.NonNull(graphene.List(graphene.NonNull(graphene.String),
                                                ),
                                  tag=graphene.String(required=True,
                                                      description="Tag name"),
                                  version=HSDateTime(description="Date of the version "
                                                                 "or nothing for the last version"),
                                  description="All values for a specific tag")

    versions = graphene.NonNull(graphene.List(graphene.NonNull(HSDateTime)),
                                description="All versions of data")

    entities = graphene.List(
        graphene.NonNull(HSScalar),
        ids=graphene.List(graphene.ID,
                          description="List of ids to return (if set, ignore filter and limit)"),
        select=graphene.String(default_value='*',
                               description="List of tags to return"),
        limit=graphene.Int(default_value=0,
                           description="Maximum number of items to return"),
        filter=graphene.String(default_value='',
                               description="Filter or item (see https://project-haystack.org/doc/docHaystack/Filters"),
        version=HSDateTime(description="Date of the version or nothing for the last version"),
        description="Selected entities of ontology"
    )

    histories = graphene.List(graphene.NonNull(graphene.List(graphene.NonNull(HSTS))),
                              ids=graphene.List(graphene.ID,
                                                description="List of ids to return"),
                              dates_range=graphene.String(description="today, yesterday, "
                                                                      "{date}, {date},{date}, "
                                                                      "{datetime}, "
                                                                      "{datetime},{datetime}"
                                                          ),
                              version=HSDateTime(
                                  description="Date of the version or nothing for the last version"),
                              description="Selected time series")

    point_write = graphene.List(
        graphene.NonNull(HSPointWrite),
        id=graphene.ID(required=True,
                       description="Id to read (accept @xxx, r:xxx or xxx)"),
        version=HSDateTime(description="Date of the version or nothing for the last version"),
        description="Point write values"
    )

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_about(parent: 'ReadHaystack',
                      info: ResolveInfo):
        log.debug("resolve_about(parent,info)")
        envs = cast(Dict[str, str], os.environ)
        grid = get_singleton_provider(envs).about("http://localhost")
        result = ReadHaystack._conv_entity(HSAbout, grid[0])
        result.serverTime = grid[0]["serverTime"]  # pylint: disable=invalid-name
        result.bootTime = grid[0]["serverBootTime"]  # pylint: disable=invalid-name, attribute-defined-outside-init
        return result

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_ops(parent: 'ReadHaystack',
                    info: ResolveInfo):
        log.debug("resolve_about(parent,info)")
        envs = cast(Dict[str, str], os.environ)
        grid = get_singleton_provider(envs).ops()
        return ReadHaystack._conv_list_to_object_type(HSOps, grid)

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_tag_values(parent: 'ReadHaystack',
                           info: ResolveInfo,
                           tag: str,
                           version: Optional[HSDateTime] = None):
        log.debug("resolve_values(parent,info,%s)", tag)
        envs = cast(Dict[str, str], os.environ)
        return get_singleton_provider(envs).values_for_tag(tag, version)

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_versions(parent: 'ReadHaystack',
                         info: ResolveInfo):
        log.debug("resolve_versions(parent,info)")
        envs = cast(Dict[str, str], os.environ)
        return get_singleton_provider(envs).versions()

    # noinspection PyShadowingBuiltins
    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_entities(parent: 'ReadHaystack',
                         info: ResolveInfo,
                         ids: Optional[List[str]] = None,
                         select: str = '*',
                         filter: str = '',  # pylint: disable=redefined-builtin
                         limit: int = 0,
                         version: Optional[HSDateTime] = None):
        log.debug(
            "resolve_entities(parent,info,ids=%s, "
            "select=%s, filter=%s, "
            "limit=%s, version=%s)", ids, select, filter, limit, version)
        if ids:
            ids = [Ref(ReadHaystack._filter_id(entity_id)) for entity_id in ids]
        envs = cast(Dict[str, str], os.environ)
        grid = get_singleton_provider(envs).read(limit, select, ids, filter, version)
        return grid.purge()

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_histories(parent: 'ReadHaystack',
                          info: ResolveInfo,
                          ids: Optional[List[str]] = None,
                          dates_range: Optional[str] = None,
                          version: Union[str, datetime, date, None] = None):
        if version:
            version = HSDateTime.parse_value(version)
        log.debug("resolve_histories(parent,info,ids=%s, range=%s, version=%s)",
                  ids, dates_range, version)
        envs = cast(Dict[str, str], os.environ)
        provider = get_singleton_provider(envs)
        grid_date_range = parse_date_range(dates_range, provider.get_tz())
        return [ReadHaystack._conv_history(
            provider.his_read(Ref(ReadHaystack._filter_id(entity_id)), grid_date_range, version),
            info
        )
            for entity_id in ids]

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_point_write(parent: 'ReadHaystack',
                            info: ResolveInfo,
                            entity_id: str,
                            version: Union[datetime, str, None] = None):
        if version:
            version = HSDateTime.parse_value(version)
        log.debug("resolve_point_write(parent,info, entity_id=%s, version=%s)",
                  entity_id, version)
        ref = Ref(ReadHaystack._filter_id(entity_id))
        envs = cast(Dict[str, str], os.environ)
        grid = get_singleton_provider(envs).point_write_read(ref, version)
        return ReadHaystack._conv_list_to_object_type(HSPointWrite, grid)

    @staticmethod
    def _conv_value(entity: Entity,
                    info: ResolveInfo) -> HSTS:
        selection = info.field_asts[0].selection_set.selections
        cast_value = HSTS()
        value = entity["val"]
        cast_value.ts = entity["ts"]  # pylint: disable=invalid-name
        cast_value.val = value
        for sel in selection:
            name = sel.name.value
            if name in ['ts', 'val']:
                continue

            if name == 'int' and isinstance(value, (int, float)):
                cast_value.int = int(value)
            elif name == 'float' and isinstance(value, float):
                cast_value.float = value
            elif name == 'str':
                cast_value.str = str(value)
            elif name == 'bool':
                cast_value.bool = bool(value)
            elif name == 'uri' and isinstance(value, Uri):
                cast_value.uri = str(value)
            elif name == 'ref' and isinstance(value, Ref):
                cast_value.ref = '@' + value.name
            elif name == 'date' and isinstance(value, date):
                cast_value.date = value
            elif name == 'date' and isinstance(value, datetime):
                cast_value.date = value.date()
            elif name == 'time' and isinstance(value, time):
                cast_value.time = value
            elif name == 'time' and isinstance(value, datetime):
                cast_value.time = value.time()
            elif name == 'datetime' and isinstance(value, datetime):
                cast_value.datetime = value
            elif name == 'coord' and isinstance(value, Coordinate):
                cast_value.coord = HSCoordinate(value.latitude, value.longitude)
        return cast_value

    @staticmethod
    def _conv_history(entities: Grid, info: ResolveInfo):
        return [ReadHaystack._conv_value(entity, info) for entity in entities]

    @staticmethod
    def _filter_id(entity_id: str) -> str:
        if entity_id.startswith("r:"):
            return entity_id[2:]
        if entity_id.startswith('@'):
            return entity_id[1:]
        return entity_id

    @staticmethod
    def _conv_entity(target_class: Type, entity: Entity):
        entity_result = target_class()
        for key, val in entity.items():
            if key in entity:
                entity_result.__setattr__(key, val)
        return entity_result

    @staticmethod
    def _conv_list_to_object_type(target_class: Type, grid: Grid):
        result = []
        for row in grid:
            result.append(ReadHaystack._conv_entity(target_class, row))
        return result

Classes

class HSAbout (*args, **kwargs)

Result of 'about' haystack operation

Expand source code
class HSAbout(graphene.ObjectType):  # pylint: disable=too-few-public-methods
    """Result of 'about' haystack operation"""
    haystackVersion = graphene.String(required=True,
                                      description="Haystack version implemented")
    tz = graphene.String(required=True,
                         description="Server time zone")
    serverName = graphene.String(required=True,
                                 description="Server name")
    serverTime = graphene.Field(graphene.NonNull(HSDateTime),
                                description="Server current time")
    serverBootTime = graphene.Field(graphene.NonNull(HSDateTime),
                                    description="Server boot time")
    productName = graphene.String(required=True,
                                  description="Server Product name")
    productUri = graphene.Field(graphene.NonNull(HSUri),
                                description="Server URL")
    productVersion = graphene.String(required=True,
                                     description="Product version")
    moduleName = graphene.String(required=True,
                                 description="Module name")
    moduleVersion = graphene.String(required=True,
                                    description="Module version")

Ancestors

  • graphene.types.objecttype.ObjectType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Class variables

var haystackVersion
var moduleName
var moduleVersion
var productName
var productUri
var productVersion
var serverBootTime
var serverName
var serverTime
var tz
class HSCoordinate (*args, **kwargs)

Haystack coordinate for GraphQL

Expand source code
class HSCoordinate(graphene.ObjectType):  # pylint: disable=too-few-public-methods
    """Haystack coordinate for GraphQL"""
    latitude = graphene.Float(required=True,
                              description="Latitude")
    longitude = graphene.Float(required=True,
                               description="Longitude")

Ancestors

  • graphene.types.objecttype.ObjectType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Class variables

var latitude
var longitude
class HSDate (*args, **kwargs)

Haystack date for GraphQL

Expand source code
class HSDate(graphene.String):
    """Haystack date for GraphQL"""

    class Meta:  # pylint: disable=missing-class-docstring
        name = "AWSDate" if BOTO3_AVAILABLE else "Date"

    @staticmethod
    def serialize(a_date: date) -> str:
        """
        Convert date to haystack json string.
        Args:
            a_date: A date
        """
        assert isinstance(a_date, date), 'Received not compatible date "{}"'.format(repr(a_date))
        return _dump_hs_date(a_date)

    @staticmethod
    def parse_literal(node: StringValue) -> date:  # pylint: disable=arguments-differ
        """
        Convert the Graphql string to date.
        Args:
            node: The graphql string
        """
        assert isinstance(node, StringValue), 'Received not compatible date "{}"'.format(repr(node))
        return HSDate.parse_value(node.value)

    @staticmethod
    def parse_value(value: Union[date, str]) -> date:
        """
        Convert a date to haystack json.
        Args:
            value: The date
        """
        if isinstance(value, date):
            return value
        return parse_hs_date_format(value)

Ancestors

  • graphene.types.scalars.String
  • graphene.types.scalars.Scalar
  • graphene.types.unmountedtype.UnmountedType
  • graphene.utils.orderedtype.OrderedType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Static methods

def parse_literal(node: graphql.language.ast.StringValue) ‑> datetime.date

Convert the Graphql string to date.

Args

node
The graphql string
Expand source code
@staticmethod
def parse_literal(node: StringValue) -> date:  # pylint: disable=arguments-differ
    """
    Convert the Graphql string to date.
    Args:
        node: The graphql string
    """
    assert isinstance(node, StringValue), 'Received not compatible date "{}"'.format(repr(node))
    return HSDate.parse_value(node.value)
def parse_value(value: Union[datetime.date, str]) ‑> datetime.date

Convert a date to haystack json.

Args

value
The date
Expand source code
@staticmethod
def parse_value(value: Union[date, str]) -> date:
    """
    Convert a date to haystack json.
    Args:
        value: The date
    """
    if isinstance(value, date):
        return value
    return parse_hs_date_format(value)
def serialize(a_date: datetime.date) ‑> str

Convert date to haystack json string.

Args

a_date
A date
Expand source code
@staticmethod
def serialize(a_date: date) -> str:
    """
    Convert date to haystack json string.
    Args:
        a_date: A date
    """
    assert isinstance(a_date, date), 'Received not compatible date "{}"'.format(repr(a_date))
    return _dump_hs_date(a_date)
class HSDateTime (*args, **kwargs)

Haystack compatible date format.

Expand source code
class HSDateTime(graphene.String):
    """Haystack compatible date format."""

    class Meta:  # pylint: disable=missing-class-docstring
        name = "AWSDateTime" if BOTO3_AVAILABLE else "DateTime"

    @staticmethod
    def serialize(date_time: datetime) -> str:
        """
        Convert date_time to json compatible string
        Args:
            date_time: The date time to convert to json
        """
        assert isinstance(date_time, datetime), \
            'Received not compatible datetime "{}"'.format(repr(date_time))
        return _dump_hs_date_time(date_time)

    @staticmethod
    def parse_literal(node: StringValue) -> datetime:  # pylint: disable=arguments-differ
        # Call to convert graphql parameter to python object
        """
        Convert a haystack json string to data time
        Args:
            node: GraphQL node to convert
        """
        assert isinstance(node, StringValue), \
            'Received not compatible datetime "{}"'.format(repr(node))
        return HSDateTime.parse_value(node.value)

    @staticmethod
    def parse_value(value: Union[datetime, date, str]) -> datetime:
        # Call to convert graphql variable to python object
        """
        Args:
            value:
        """
        if isinstance(value, datetime):
            return value
        if isinstance(value, date):
            return datetime.combine(value, datetime.max.time()).replace(tzinfo=pytz.UTC)
        return parse_hs_datetime_format(value, pytz.UTC)

Ancestors

  • graphene.types.scalars.String
  • graphene.types.scalars.Scalar
  • graphene.types.unmountedtype.UnmountedType
  • graphene.utils.orderedtype.OrderedType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Static methods

def parse_literal(node: graphql.language.ast.StringValue) ‑> datetime.datetime

Convert a haystack json string to data time

Args

node
GraphQL node to convert
Expand source code
@staticmethod
def parse_literal(node: StringValue) -> datetime:  # pylint: disable=arguments-differ
    # Call to convert graphql parameter to python object
    """
    Convert a haystack json string to data time
    Args:
        node: GraphQL node to convert
    """
    assert isinstance(node, StringValue), \
        'Received not compatible datetime "{}"'.format(repr(node))
    return HSDateTime.parse_value(node.value)
def parse_value(value: Union[datetime.datetime, datetime.date, str]) ‑> datetime.datetime

Args

value:

Expand source code
@staticmethod
def parse_value(value: Union[datetime, date, str]) -> datetime:
    # Call to convert graphql variable to python object
    """
    Args:
        value:
    """
    if isinstance(value, datetime):
        return value
    if isinstance(value, date):
        return datetime.combine(value, datetime.max.time()).replace(tzinfo=pytz.UTC)
    return parse_hs_datetime_format(value, pytz.UTC)
def serialize(date_time: datetime.datetime) ‑> str

Convert date_time to json compatible string

Args

date_time
The date time to convert to json
Expand source code
@staticmethod
def serialize(date_time: datetime) -> str:
    """
    Convert date_time to json compatible string
    Args:
        date_time: The date time to convert to json
    """
    assert isinstance(date_time, datetime), \
        'Received not compatible datetime "{}"'.format(repr(date_time))
    return _dump_hs_date_time(date_time)
class HSOps (*args, **kwargs)

Result of 'ops' haystack operation

Expand source code
class HSOps(graphene.ObjectType):  # pylint: disable=too-few-public-methods
    """Result of 'ops' haystack operation"""

    name = graphene.String(description="Name of operation (see https://project-haystack.org/doc/docHaystack/Ops)")
    summary = graphene.String(description="Summary of operation")

Ancestors

  • graphene.types.objecttype.ObjectType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Class variables

var name
var summary
class HSPointWrite (*args, **kwargs)

Result of 'pointWrite' haystack operation

Expand source code
class HSPointWrite(graphene.ObjectType):  # pylint: disable=too-few-public-methods
    """Result of 'pointWrite' haystack operation"""
    level = graphene.Int(description="Current level")
    levelDis = graphene.String(description="Description of level")
    val = graphene.Field(HSScalar, description="Value")
    who = graphene.String(description="Who has updated the value")

Ancestors

  • graphene.types.objecttype.ObjectType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Class variables

var level
var levelDis
var val
var who
class HSScalar (*args, **kwargs)

Haystack Scalar

Expand source code
class HSScalar(graphene.Scalar):
    """Haystack Scalar"""

    class Meta:  # pylint: disable=too-few-public-methods
        """Update name for AWS"""
        name = "AWSJSON" if BOTO3_AVAILABLE else "JSONString"

    @staticmethod
    def serialize(hs_scalar: Any) -> Any:
        """
        Convert scala value to Json
        Args:
            hs_scalar: Any value
        """
        return json.loads(shaystack.dump_scalar(hs_scalar,
                                                shaystack.MODE_JSON,
                                                version=shaystack.VER_3_0))

    @staticmethod
    def parse_literal(node: Union[IntValue, FloatValue, StringValue, BooleanValue, EnumValue]) -> Any:
        """
        Parse to scalar value
        Args:
            node: The graphql node
        """
        if isinstance(node, StringValue):
            str_value = node.value
            if len(str_value) >= 2 and str_value[1] == ':':
                return shaystack.parse_scalar(node.value,
                                              shaystack.MODE_JSON)
        return node.value

    @staticmethod
    def parse_value(value: str) -> Any:
        """
        Args:
            value: The value to parse
        """
        return shaystack.parse_scalar(value, shaystack.MODE_JSON)

Ancestors

  • graphene.types.scalars.Scalar
  • graphene.types.unmountedtype.UnmountedType
  • graphene.utils.orderedtype.OrderedType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Static methods

def parse_literal(node: Union[graphql.language.ast.IntValue, graphql.language.ast.FloatValue, graphql.language.ast.StringValue, graphql.language.ast.BooleanValue, graphql.language.ast.EnumValue]) ‑> Any

Parse to scalar value

Args

node
The graphql node
Expand source code
@staticmethod
def parse_literal(node: Union[IntValue, FloatValue, StringValue, BooleanValue, EnumValue]) -> Any:
    """
    Parse to scalar value
    Args:
        node: The graphql node
    """
    if isinstance(node, StringValue):
        str_value = node.value
        if len(str_value) >= 2 and str_value[1] == ':':
            return shaystack.parse_scalar(node.value,
                                          shaystack.MODE_JSON)
    return node.value
def parse_value(value: str) ‑> Any

Args

value
The value to parse
Expand source code
@staticmethod
def parse_value(value: str) -> Any:
    """
    Args:
        value: The value to parse
    """
    return shaystack.parse_scalar(value, shaystack.MODE_JSON)
def serialize(hs_scalar: Any) ‑> Any

Convert scala value to Json

Args

hs_scalar
Any value
Expand source code
@staticmethod
def serialize(hs_scalar: Any) -> Any:
    """
    Convert scala value to Json
    Args:
        hs_scalar: Any value
    """
    return json.loads(shaystack.dump_scalar(hs_scalar,
                                            shaystack.MODE_JSON,
                                            version=shaystack.VER_3_0))
class HSTS (*args, **kwargs)

Result of 'hisRead' haystack operation

Expand source code
class HSTS(graphene.ObjectType):  # pylint: disable=too-few-public-methods,too-many-instance-attributes
    """Result of 'hisRead' haystack operation"""
    ts = graphene.Field(HSDateTime, description="Date time of event")
    val = graphene.Field(HSScalar, description="Haystack JSON format of value")

    int = graphene.Int(required=False, description="Integer version of the value")
    float = graphene.Float(required=False, description="Float version of the value")
    str = graphene.String(required=False, description="Float version of the value")
    bool = graphene.Boolean(required=False, description="Boolean version of the value")
    uri = graphene.String(required=False, description="URI version of the value")
    ref = graphene.String(required=False, description="Reference version of the value")
    date = HSDate(required=False, description="Date version of the value")
    time = HSTime(required=False, description="Time version of the value")
    datetime = HSDateTime(required=False, description="Date time version of the value")
    coord = graphene.Field(HSCoordinate,
                           description="Geographic Coordinate")

Ancestors

  • graphene.types.objecttype.ObjectType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Class variables

var bool
var coord
var date
var datetime
var float
var int
var ref
var str
var time
var ts
var uri
var val
class HSTime (*args, **kwargs)

Haystack time for GraphQL

Expand source code
class HSTime(graphene.String):
    """Haystack time for GraphQL"""

    class Meta:  # pylint: disable=missing-class-docstring
        name = "AWSTime" if BOTO3_AVAILABLE else "Time"

    @staticmethod
    def serialize(a_time: time) -> str:
        """
        Convert a time to Haystack json string
        Args:
            a_time: The time.
        """
        assert isinstance(a_time, time), 'Received not compatible time "{}"'.format(repr(a_time))
        return _dump_hs_time(a_time)

    @staticmethod
    def parse_literal(node: StringValue) -> time:  # pylint: disable=arguments-differ
        """
        Convert a haystack json string to time
        Args:
            node: The string
        """
        assert isinstance(node, StringValue), 'Received not compatible time "{}"'.format(repr(node))
        return HSTime.parse_value(node.value)

    @staticmethod
    def parse_value(value: Union[time, str]) -> time:
        """
        Args:
            value:
        """
        if isinstance(value, time):
            return value
        return parse_hs_time_format(value)

Ancestors

  • graphene.types.scalars.String
  • graphene.types.scalars.Scalar
  • graphene.types.unmountedtype.UnmountedType
  • graphene.utils.orderedtype.OrderedType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Static methods

def parse_literal(node: graphql.language.ast.StringValue) ‑> datetime.time

Convert a haystack json string to time

Args

node
The string
Expand source code
@staticmethod
def parse_literal(node: StringValue) -> time:  # pylint: disable=arguments-differ
    """
    Convert a haystack json string to time
    Args:
        node: The string
    """
    assert isinstance(node, StringValue), 'Received not compatible time "{}"'.format(repr(node))
    return HSTime.parse_value(node.value)
def parse_value(value: Union[datetime.time, str]) ‑> datetime.time

Args

value:

Expand source code
@staticmethod
def parse_value(value: Union[time, str]) -> time:
    """
    Args:
        value:
    """
    if isinstance(value, time):
        return value
    return parse_hs_time_format(value)
def serialize(a_time: datetime.time) ‑> str

Convert a time to Haystack json string

Args

a_time
The time.
Expand source code
@staticmethod
def serialize(a_time: time) -> str:
    """
    Convert a time to Haystack json string
    Args:
        a_time: The time.
    """
    assert isinstance(a_time, time), 'Received not compatible time "{}"'.format(repr(a_time))
    return _dump_hs_time(a_time)
class HSUri (*args, **kwargs)

Haystack URI for GraphQL

Expand source code
class HSUri(graphene.String):
    """Haystack URI for GraphQL"""

    class Meta:  # pylint: disable=too-few-public-methods,missing-class-docstring
        name = "AWSURL" if BOTO3_AVAILABLE else "HSURL"

    @staticmethod
    def serialize(a_uri: Uri) -> str:
        """
        Convert an Uri to Haystack json string
        Args:
            a_uri: The Uri
        """
        assert isinstance(a_uri, Uri), 'Received not compatible uri "{}"'.format(repr(a_uri))
        return str(a_uri)

    @staticmethod
    def parse_literal(node: StringValue) -> Uri:  # pylint: disable=arguments-differ
        """
        Convert the Graphql string to Uri
        Args:
            node: The string
        """
        return HSUri.parse_value(node.value)

    @staticmethod
    def parse_value(value: str) -> Uri:
        """
        Convert a haystack json string to Uri.
        Args:
            value: The string
        """
        if value.startswith("u:"):
            return shaystack.parse_scalar(value, shaystack.MODE_JSON, version=shaystack.VER_3_0)
        return Uri(value)

Ancestors

  • graphene.types.scalars.String
  • graphene.types.scalars.Scalar
  • graphene.types.unmountedtype.UnmountedType
  • graphene.utils.orderedtype.OrderedType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Static methods

def parse_literal(node: graphql.language.ast.StringValue) ‑> shaystack.datatypes.Uri

Convert the Graphql string to Uri

Args

node
The string
Expand source code
@staticmethod
def parse_literal(node: StringValue) -> Uri:  # pylint: disable=arguments-differ
    """
    Convert the Graphql string to Uri
    Args:
        node: The string
    """
    return HSUri.parse_value(node.value)
def parse_value(value: str) ‑> shaystack.datatypes.Uri

Convert a haystack json string to Uri.

Args

value
The string
Expand source code
@staticmethod
def parse_value(value: str) -> Uri:
    """
    Convert a haystack json string to Uri.
    Args:
        value: The string
    """
    if value.startswith("u:"):
        return shaystack.parse_scalar(value, shaystack.MODE_JSON, version=shaystack.VER_3_0)
    return Uri(value)
def serialize(a_uri: shaystack.datatypes.Uri) ‑> str

Convert an Uri to Haystack json string

Args

a_uri
The Uri
Expand source code
@staticmethod
def serialize(a_uri: Uri) -> str:
    """
    Convert an Uri to Haystack json string
    Args:
        a_uri: The Uri
    """
    assert isinstance(a_uri, Uri), 'Received not compatible uri "{}"'.format(repr(a_uri))
    return str(a_uri)
class ReadHaystack (*args, **kwargs)

Ontology conform with Haystack project

Expand source code
class ReadHaystack(graphene.ObjectType):
    """Ontology conform with Haystack project"""

    class Meta:  # pylint: disable=too-few-public-methods,missing-class-docstring
        name = "Haystack"

    about = graphene.NonNull(HSAbout,
                             description="Versions of api")

    ops = graphene.NonNull(graphene.List(
        graphene.NonNull(HSOps)),
        description="List of operation implemented")

    tag_values = graphene.NonNull(graphene.List(graphene.NonNull(graphene.String),
                                                ),
                                  tag=graphene.String(required=True,
                                                      description="Tag name"),
                                  version=HSDateTime(description="Date of the version "
                                                                 "or nothing for the last version"),
                                  description="All values for a specific tag")

    versions = graphene.NonNull(graphene.List(graphene.NonNull(HSDateTime)),
                                description="All versions of data")

    entities = graphene.List(
        graphene.NonNull(HSScalar),
        ids=graphene.List(graphene.ID,
                          description="List of ids to return (if set, ignore filter and limit)"),
        select=graphene.String(default_value='*',
                               description="List of tags to return"),
        limit=graphene.Int(default_value=0,
                           description="Maximum number of items to return"),
        filter=graphene.String(default_value='',
                               description="Filter or item (see https://project-haystack.org/doc/docHaystack/Filters"),
        version=HSDateTime(description="Date of the version or nothing for the last version"),
        description="Selected entities of ontology"
    )

    histories = graphene.List(graphene.NonNull(graphene.List(graphene.NonNull(HSTS))),
                              ids=graphene.List(graphene.ID,
                                                description="List of ids to return"),
                              dates_range=graphene.String(description="today, yesterday, "
                                                                      "{date}, {date},{date}, "
                                                                      "{datetime}, "
                                                                      "{datetime},{datetime}"
                                                          ),
                              version=HSDateTime(
                                  description="Date of the version or nothing for the last version"),
                              description="Selected time series")

    point_write = graphene.List(
        graphene.NonNull(HSPointWrite),
        id=graphene.ID(required=True,
                       description="Id to read (accept @xxx, r:xxx or xxx)"),
        version=HSDateTime(description="Date of the version or nothing for the last version"),
        description="Point write values"
    )

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_about(parent: 'ReadHaystack',
                      info: ResolveInfo):
        log.debug("resolve_about(parent,info)")
        envs = cast(Dict[str, str], os.environ)
        grid = get_singleton_provider(envs).about("http://localhost")
        result = ReadHaystack._conv_entity(HSAbout, grid[0])
        result.serverTime = grid[0]["serverTime"]  # pylint: disable=invalid-name
        result.bootTime = grid[0]["serverBootTime"]  # pylint: disable=invalid-name, attribute-defined-outside-init
        return result

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_ops(parent: 'ReadHaystack',
                    info: ResolveInfo):
        log.debug("resolve_about(parent,info)")
        envs = cast(Dict[str, str], os.environ)
        grid = get_singleton_provider(envs).ops()
        return ReadHaystack._conv_list_to_object_type(HSOps, grid)

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_tag_values(parent: 'ReadHaystack',
                           info: ResolveInfo,
                           tag: str,
                           version: Optional[HSDateTime] = None):
        log.debug("resolve_values(parent,info,%s)", tag)
        envs = cast(Dict[str, str], os.environ)
        return get_singleton_provider(envs).values_for_tag(tag, version)

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_versions(parent: 'ReadHaystack',
                         info: ResolveInfo):
        log.debug("resolve_versions(parent,info)")
        envs = cast(Dict[str, str], os.environ)
        return get_singleton_provider(envs).versions()

    # noinspection PyShadowingBuiltins
    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_entities(parent: 'ReadHaystack',
                         info: ResolveInfo,
                         ids: Optional[List[str]] = None,
                         select: str = '*',
                         filter: str = '',  # pylint: disable=redefined-builtin
                         limit: int = 0,
                         version: Optional[HSDateTime] = None):
        log.debug(
            "resolve_entities(parent,info,ids=%s, "
            "select=%s, filter=%s, "
            "limit=%s, version=%s)", ids, select, filter, limit, version)
        if ids:
            ids = [Ref(ReadHaystack._filter_id(entity_id)) for entity_id in ids]
        envs = cast(Dict[str, str], os.environ)
        grid = get_singleton_provider(envs).read(limit, select, ids, filter, version)
        return grid.purge()

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_histories(parent: 'ReadHaystack',
                          info: ResolveInfo,
                          ids: Optional[List[str]] = None,
                          dates_range: Optional[str] = None,
                          version: Union[str, datetime, date, None] = None):
        if version:
            version = HSDateTime.parse_value(version)
        log.debug("resolve_histories(parent,info,ids=%s, range=%s, version=%s)",
                  ids, dates_range, version)
        envs = cast(Dict[str, str], os.environ)
        provider = get_singleton_provider(envs)
        grid_date_range = parse_date_range(dates_range, provider.get_tz())
        return [ReadHaystack._conv_history(
            provider.his_read(Ref(ReadHaystack._filter_id(entity_id)), grid_date_range, version),
            info
        )
            for entity_id in ids]

    # noinspection PyUnusedLocal
    @staticmethod
    def resolve_point_write(parent: 'ReadHaystack',
                            info: ResolveInfo,
                            entity_id: str,
                            version: Union[datetime, str, None] = None):
        if version:
            version = HSDateTime.parse_value(version)
        log.debug("resolve_point_write(parent,info, entity_id=%s, version=%s)",
                  entity_id, version)
        ref = Ref(ReadHaystack._filter_id(entity_id))
        envs = cast(Dict[str, str], os.environ)
        grid = get_singleton_provider(envs).point_write_read(ref, version)
        return ReadHaystack._conv_list_to_object_type(HSPointWrite, grid)

    @staticmethod
    def _conv_value(entity: Entity,
                    info: ResolveInfo) -> HSTS:
        selection = info.field_asts[0].selection_set.selections
        cast_value = HSTS()
        value = entity["val"]
        cast_value.ts = entity["ts"]  # pylint: disable=invalid-name
        cast_value.val = value
        for sel in selection:
            name = sel.name.value
            if name in ['ts', 'val']:
                continue

            if name == 'int' and isinstance(value, (int, float)):
                cast_value.int = int(value)
            elif name == 'float' and isinstance(value, float):
                cast_value.float = value
            elif name == 'str':
                cast_value.str = str(value)
            elif name == 'bool':
                cast_value.bool = bool(value)
            elif name == 'uri' and isinstance(value, Uri):
                cast_value.uri = str(value)
            elif name == 'ref' and isinstance(value, Ref):
                cast_value.ref = '@' + value.name
            elif name == 'date' and isinstance(value, date):
                cast_value.date = value
            elif name == 'date' and isinstance(value, datetime):
                cast_value.date = value.date()
            elif name == 'time' and isinstance(value, time):
                cast_value.time = value
            elif name == 'time' and isinstance(value, datetime):
                cast_value.time = value.time()
            elif name == 'datetime' and isinstance(value, datetime):
                cast_value.datetime = value
            elif name == 'coord' and isinstance(value, Coordinate):
                cast_value.coord = HSCoordinate(value.latitude, value.longitude)
        return cast_value

    @staticmethod
    def _conv_history(entities: Grid, info: ResolveInfo):
        return [ReadHaystack._conv_value(entity, info) for entity in entities]

    @staticmethod
    def _filter_id(entity_id: str) -> str:
        if entity_id.startswith("r:"):
            return entity_id[2:]
        if entity_id.startswith('@'):
            return entity_id[1:]
        return entity_id

    @staticmethod
    def _conv_entity(target_class: Type, entity: Entity):
        entity_result = target_class()
        for key, val in entity.items():
            if key in entity:
                entity_result.__setattr__(key, val)
        return entity_result

    @staticmethod
    def _conv_list_to_object_type(target_class: Type, grid: Grid):
        result = []
        for row in grid:
            result.append(ReadHaystack._conv_entity(target_class, row))
        return result

Ancestors

  • graphene.types.objecttype.ObjectType
  • graphene.types.base.BaseType
  • graphene.utils.subclass_with_meta.SubclassWithMeta

Class variables

var about
var entities
var histories
var ops
var point_write
var tag_values
var versions

Static methods

def resolve_about(parent: ReadHaystack, info: graphql.execution.base.ResolveInfo)
Expand source code
@staticmethod
def resolve_about(parent: 'ReadHaystack',
                  info: ResolveInfo):
    log.debug("resolve_about(parent,info)")
    envs = cast(Dict[str, str], os.environ)
    grid = get_singleton_provider(envs).about("http://localhost")
    result = ReadHaystack._conv_entity(HSAbout, grid[0])
    result.serverTime = grid[0]["serverTime"]  # pylint: disable=invalid-name
    result.bootTime = grid[0]["serverBootTime"]  # pylint: disable=invalid-name, attribute-defined-outside-init
    return result
def resolve_entities(parent: ReadHaystack, info: graphql.execution.base.ResolveInfo, ids: Union[List[str], NoneType] = None, select: str = '*', filter: str = '', limit: int = 0, version: Union[HSDateTime, NoneType] = None)
Expand source code
@staticmethod
def resolve_entities(parent: 'ReadHaystack',
                     info: ResolveInfo,
                     ids: Optional[List[str]] = None,
                     select: str = '*',
                     filter: str = '',  # pylint: disable=redefined-builtin
                     limit: int = 0,
                     version: Optional[HSDateTime] = None):
    log.debug(
        "resolve_entities(parent,info,ids=%s, "
        "select=%s, filter=%s, "
        "limit=%s, version=%s)", ids, select, filter, limit, version)
    if ids:
        ids = [Ref(ReadHaystack._filter_id(entity_id)) for entity_id in ids]
    envs = cast(Dict[str, str], os.environ)
    grid = get_singleton_provider(envs).read(limit, select, ids, filter, version)
    return grid.purge()
def resolve_histories(parent: ReadHaystack, info: graphql.execution.base.ResolveInfo, ids: Union[List[str], NoneType] = None, dates_range: Union[str, NoneType] = None, version: Union[str, datetime.datetime, datetime.date, NoneType] = None)
Expand source code
@staticmethod
def resolve_histories(parent: 'ReadHaystack',
                      info: ResolveInfo,
                      ids: Optional[List[str]] = None,
                      dates_range: Optional[str] = None,
                      version: Union[str, datetime, date, None] = None):
    if version:
        version = HSDateTime.parse_value(version)
    log.debug("resolve_histories(parent,info,ids=%s, range=%s, version=%s)",
              ids, dates_range, version)
    envs = cast(Dict[str, str], os.environ)
    provider = get_singleton_provider(envs)
    grid_date_range = parse_date_range(dates_range, provider.get_tz())
    return [ReadHaystack._conv_history(
        provider.his_read(Ref(ReadHaystack._filter_id(entity_id)), grid_date_range, version),
        info
    )
        for entity_id in ids]
def resolve_ops(parent: ReadHaystack, info: graphql.execution.base.ResolveInfo)
Expand source code
@staticmethod
def resolve_ops(parent: 'ReadHaystack',
                info: ResolveInfo):
    log.debug("resolve_about(parent,info)")
    envs = cast(Dict[str, str], os.environ)
    grid = get_singleton_provider(envs).ops()
    return ReadHaystack._conv_list_to_object_type(HSOps, grid)
def resolve_point_write(parent: ReadHaystack, info: graphql.execution.base.ResolveInfo, entity_id: str, version: Union[datetime.datetime, str, NoneType] = None)
Expand source code
@staticmethod
def resolve_point_write(parent: 'ReadHaystack',
                        info: ResolveInfo,
                        entity_id: str,
                        version: Union[datetime, str, None] = None):
    if version:
        version = HSDateTime.parse_value(version)
    log.debug("resolve_point_write(parent,info, entity_id=%s, version=%s)",
              entity_id, version)
    ref = Ref(ReadHaystack._filter_id(entity_id))
    envs = cast(Dict[str, str], os.environ)
    grid = get_singleton_provider(envs).point_write_read(ref, version)
    return ReadHaystack._conv_list_to_object_type(HSPointWrite, grid)
def resolve_tag_values(parent: ReadHaystack, info: graphql.execution.base.ResolveInfo, tag: str, version: Union[HSDateTime, NoneType] = None)
Expand source code
@staticmethod
def resolve_tag_values(parent: 'ReadHaystack',
                       info: ResolveInfo,
                       tag: str,
                       version: Optional[HSDateTime] = None):
    log.debug("resolve_values(parent,info,%s)", tag)
    envs = cast(Dict[str, str], os.environ)
    return get_singleton_provider(envs).values_for_tag(tag, version)
def resolve_versions(parent: ReadHaystack, info: graphql.execution.base.ResolveInfo)
Expand source code
@staticmethod
def resolve_versions(parent: 'ReadHaystack',
                     info: ResolveInfo):
    log.debug("resolve_versions(parent,info)")
    envs = cast(Dict[str, str], os.environ)
    return get_singleton_provider(envs).versions()