<commit_before># coding: utf-8
import numpy as np
import pandas as pd

from .base import Transformer

class TableIndex(object):
    column_index = 'col'
    row_index = 'row'

class DataTablesTransformer(Transformer):
    def __init__(self, table_type=TableIndex.row_index):
        self.table_type = table_type

    def transform(self, data_frame, display_schema):
        dim_ordinal = {name: ordinal
                       for ordinal, name in enumerate(data_frame.index.names)}

        has_references = isinstance(data_frame.columns, pd.MultiIndex)
        data_frame = self._prepare_data_frame(data_frame, display_schema['dimensions'])
        records = len(data_frame)

        data = []
        for row_id, (idx, row) in enumerate(data_frame.iterrows()):
            record = {'DT_RowId': 'row_%d' % row_id}

            for key, value in self._render_index_levels(idx, dim_ordinal, display_schema):
                record[key] = value

            if has_references:
                reference_groups = [(level, row[level]) for level in row.index.levels[0]]
            else:
                reference_groups = [(None, row)]

            for reference_key, reference_data in reference_groups:
                for key, value in self._render_column_levels(reference_data, dim_ordinal, display_schema,
                                                             reference_key):
                    record[key] = value

            data.append(record)

        return {
            'draw': 1,
            'recordsTotal': records,
            'recordsFiltered': records,
            'data': data,
        }

    def _prepare_data_frame(self, data_frame, dimensions):
        # Replaces invalid values and unstacks the data frame for column_index tables.
        data_frame = data_frame.replace([np.inf, -np.inf], np.nan)

        if 1 < len(dimensions) and self.table_type == TableIndex.column_index:
            unstack_levels = list(range(
                len(dimensions[0]['id_fields']),
                len(data_frame.index.levels))
            )
            return data_frame.unstack(level=unstack_levels)

        return data_frame

    def _render_index_levels(self, idx, dim_ordinal, display_schema):
        row_dimensions = display_schema['dimensions'][:None if self.table_type == TableIndex.row_index else 1]
        for dimension in row_dimensions:
            key = dimension['label']

            if not isinstance(idx, tuple):
                value = idx

                if isinstance(value, pd.Timestamp):
                    value = value.isoformat()

            elif 1 < len(dimension['id_fields']) or 'label_field' in dimension:
                fields = dimension['id_fields'] + [dimension.get('label_field')]

                value = {id_field: idx[dim_ordinal[id_field]]
                         for id_field in filter(None, fields)}

            else:
                id_field = dimension['id_fields'][0]
                value = idx[dim_ordinal[id_field]]

                if isinstance(value, pd.Timestamp):
                    value = value.isoformat()

                if value is np.nan:
                    value = 'Total'

                if 'label_options' in dimension:
                    value = dimension['label_options'].get(value, value)

            yield key, value

    def _render_column_levels(self, row, dim_ordinal, display_schema, reference_key=None):
        if isinstance(row.index, pd.MultiIndex):
            # Multi level columns
            for idx, value in row.iteritems():
                label = self._format_series_labels(idx, dim_ordinal, display_schema)
                label = self._format_reference_label(display_schema, label, reference_key)
                yield label, value

        else:
            # Single level columns
            for col in row.index:
                label = display_schema['metrics'][col]
                label = self._format_reference_label(display_schema, label, reference_key)
                yield label, row[col]

    def _format_series_labels(self, idx, dim_ordinal, display_schema):
        metric, dimensions = idx[0], idx[1:]

        if not [d for d in dimensions if d is not np.nan]:
            # If all the dimensions are NaN the data frame, then do not display them.
            return display_schema['metrics'][metric]

        metric_label = display_schema['metrics'].get(idx[0], idx[0])
        dimension_labels = [self._format_dimension_label(idx, dim_ordinal, dimension)
                            for dimension in display_schema['dimensions'][1:]]
        dimension_labels = [dim_label  # filter out the NaNs
                            for dim_label in dimension_labels
                            if dim_label is not np.nan]

        return (
            '{metric} ({dimensions})'.format(metric=metric_label, dimensions=', '.join(dimension_labels))
            if dimension_labels else
            metric_label
        )

    @staticmethod
    def _format_reference_label(display_schema, label, reference_key):
        if reference_key:
            return '{label} {reference}'.format(
                label=label,
                reference=display_schema['references'][reference_key]
            )
        return label

    @staticmethod
    def _format_dimension_label(idx, dim_ordinal, dimension):
        if 'label_field' in dimension:
            label_field = dimension['label_field']
            return idx[dim_ordinal[label_field]]

        if isinstance(idx, tuple):
            id_field = dimension['id_fields'][0]
            dimension_label = idx[dim_ordinal[id_field]]

        else:
            dimension_label = idx

        if 'label_options' in dimension:
            dimension_label = dimension['label_options'].get(dimension_label, dimension_label)

        return dimension_label

class CSVTransformer(DataTablesTransformer):
    def __init__(self, table_type=TableIndex.row_index):
        self.table_type = table_type

    def transform(self, data_frame, display_schema):
        dim_ordinal = {name: ordinal
                       for ordinal, name in enumerate(data_frame.index.names)}

        csv_df = self._format_columns(data_frame, dim_ordinal, display_schema)

        if isinstance(data_frame.index, pd.RangeIndex):
            # If there are no dimensions, just serialize to csv without the index
            return csv_df.to_csv(index=False)

        csv_df = self._format_index(csv_df, dim_ordinal, display_schema)

        row_dimensions = display_schema['dimensions'][:None if self.table_type == TableIndex.row_index else 1]
        return csv_df.to_csv(index_label=[dimension['label']
                                          for dimension in row_dimensions])

    def _format_index(self, csv_df, dim_ordinal, display_schema):
        if isinstance(csv_df.index, pd.MultiIndex):
            csv_df.index = pd.MultiIndex.from_tuples(
                [[self._format_dimension_label(idx, dim_ordinal, dimension)
                  for dimension in display_schema['dimensions']]
                 for idx in list(csv_df.index)]
            )

        else:
            csv_df.reindex(
                self._format_dimension_label(idx, dim_ordinal, display_schema['dimensions'][0])
                for idx in list(csv_df.index)
            )

        return csv_df

    def _format_columns(self, data_frame, dim_ordinal, display_schema):
        if 1 < len(display_schema['dimensions']) and self.table_type == TableIndex.column_index:
            csv_df = self._prepare_data_frame(data_frame, display_schema['dimensions'])

            csv_df.columns = pd.Index([self._format_series_labels(column, dim_ordinal, display_schema)
                                       for column in csv_df.columns])
            return csv_df

        return data_frame.rename(
            columns=lambda metric: display_schema['metrics'].get(metric, metric)
        )
<commit_msg>Issue#7 Made data tables transformer cast np.int64 to python int<commit_after># coding: utf-8
import numpy as np
import pandas as pd

from .base import Transformer

class TableIndex(object):
    column_index = 'col'
    row_index = 'row'

class DataTablesTransformer(Transformer):
    def __init__(self, table_type=TableIndex.row_index):
        self.table_type = table_type

    def transform(self, data_frame, display_schema):
        dim_ordinal = {name: ordinal
                       for ordinal, name in enumerate(data_frame.index.names)}

        has_references = isinstance(data_frame.columns, pd.MultiIndex)
        data_frame = self._prepare_data_frame(data_frame, display_schema['dimensions'])
        records = len(data_frame)

        data = []
        for row_id, (idx, row) in enumerate(data_frame.iterrows()):
            record = {'DT_RowId': 'row_%d' % row_id}

            for key, value in self._render_index_levels(idx, dim_ordinal, display_schema):
                record[key] = value

            if has_references:
                reference_groups = [(level, row[level]) for level in row.index.levels[0]]
            else:
                reference_groups = [(None, row)]

            for reference_key, reference_data in reference_groups:
                for key, value in self._render_column_levels(reference_data, dim_ordinal, display_schema,
                                                             reference_key):
                    record[key] = value

            data.append(record)

        return {
            'draw': 1,
            'recordsTotal': records,
            'recordsFiltered': records,
            'data': data,
        }

    def _prepare_data_frame(self, data_frame, dimensions):
        # Replaces invalid values and unstacks the data frame for column_index tables.
        data_frame = data_frame.replace([np.inf, -np.inf], np.nan)

        if 1 < len(dimensions) and self.table_type == TableIndex.column_index:
            unstack_levels = list(range(
                len(dimensions[0]['id_fields']),
                len(data_frame.index.levels))
            )
            return data_frame.unstack(level=unstack_levels)

        return data_frame

    def _render_index_levels(self, idx, dim_ordinal, display_schema):
        row_dimensions = display_schema['dimensions'][:None if self.table_type == TableIndex.row_index else 1]
        for dimension in row_dimensions:
            key = dimension['label']

            if not isinstance(idx, tuple):
                value = self._format_data_point(idx)

            elif 1 < len(dimension['id_fields']) or 'label_field' in dimension:
                fields = dimension['id_fields'] + [dimension.get('label_field')]

                value = {id_field: idx[dim_ordinal[id_field]]
                         for id_field in filter(None, fields)}

            else:
                id_field = dimension['id_fields'][0]
                value = self._format_data_point(idx[dim_ordinal[id_field]])

                if value is None:
                    value = 'Total'

                if 'label_options' in dimension:
                    value = dimension['label_options'].get(value, value)

            yield key, value

    def _render_column_levels(self, row, dim_ordinal, display_schema, reference_key=None):
        if isinstance(row.index, pd.MultiIndex):
            # Multi level columns
            for idx, value in row.iteritems():
                label = self._format_series_labels(idx, dim_ordinal, display_schema)
                label = self._format_reference_label(display_schema, label, reference_key)
                yield label, self._format_data_point(value)

        else:
            # Single level columns
            for col in row.index:
                label = display_schema['metrics'][col]
                label = self._format_reference_label(display_schema, label, reference_key)
                yield label, self._format_data_point(row[col])

    def _format_series_labels(self, idx, dim_ordinal, display_schema):
        metric, dimensions = idx[0], idx[1:]

        if not [d for d in dimensions if d is not np.nan]:
            # If all the dimensions are NaN the data frame, then do not display them.
            return display_schema['metrics'][metric]

        metric_label = display_schema['metrics'].get(idx[0], idx[0])
        dimension_labels = [self._format_dimension_label(idx, dim_ordinal, dimension)
                            for dimension in display_schema['dimensions'][1:]]
        dimension_labels = [dim_label  # filter out the NaNs
                            for dim_label in dimension_labels
                            if dim_label is not np.nan]

        return (
            '{metric} ({dimensions})'.format(metric=metric_label, dimensions=', '.join(dimension_labels))
            if dimension_labels else
            metric_label
        )

    @staticmethod
    def _format_reference_label(display_schema, label, reference_key):
        if reference_key:
            return '{label} {reference}'.format(
                label=label,
                reference=display_schema['references'][reference_key]
            )
        return label

    @staticmethod
    def _format_dimension_label(idx, dim_ordinal, dimension):
        if 'label_field' in dimension:
            label_field = dimension['label_field']
            return idx[dim_ordinal[label_field]]

        if isinstance(idx, tuple):
            id_field = dimension['id_fields'][0]
            dimension_label = idx[dim_ordinal[id_field]]

        else:
            dimension_label = idx

        if 'label_options' in dimension:
            dimension_label = dimension['label_options'].get(dimension_label, dimension_label)

        return dimension_label

    def _format_data_point(self, value):
        if value is np.nan:
            value = None
        if isinstance(value, pd.Timestamp):
            return value.isoformat()
        if isinstance(value, np.int64):
            # Cannot transform np.int64 to json
            return int(value)
        return value

class CSVTransformer(DataTablesTransformer):
    def __init__(self, table_type=TableIndex.row_index):
        self.table_type = table_type

    def transform(self, data_frame, display_schema):
        dim_ordinal = {name: ordinal
                       for ordinal, name in enumerate(data_frame.index.names)}

        csv_df = self._format_columns(data_frame, dim_ordinal, display_schema)

        if isinstance(data_frame.index, pd.RangeIndex):
            # If there are no dimensions, just serialize to csv without the index
            return csv_df.to_csv(index=False)

        csv_df = self._format_index(csv_df, dim_ordinal, display_schema)

        row_dimensions = display_schema['dimensions'][:None if self.table_type == TableIndex.row_index else 1]
        return csv_df.to_csv(index_label=[dimension['label']
                                          for dimension in row_dimensions])

    def _format_index(self, csv_df, dim_ordinal, display_schema):
        if isinstance(csv_df.index, pd.MultiIndex):
            csv_df.index = pd.MultiIndex.from_tuples(
                [[self._format_dimension_label(idx, dim_ordinal, dimension)
                  for dimension in display_schema['dimensions']]
                 for idx in list(csv_df.index)]
            )

        else:
            csv_df.reindex(
                self._format_dimension_label(idx, dim_ordinal, display_schema['dimensions'][0])
                for idx in list(csv_df.index)
            )

        return csv_df

    def _format_columns(self, data_frame, dim_ordinal, display_schema):
        if 1 < len(display_schema['dimensions']) and self.table_type == TableIndex.column_index:
            csv_df = self._prepare_data_frame(data_frame, display_schema['dimensions'])

            csv_df.columns = pd.Index([self._format_series_labels(column, dim_ordinal, display_schema)
                                       for column in csv_df.columns])
            return csv_df

        return data_frame.rename(
            columns=lambda metric: display_schema['metrics'].get(metric, metric)
        )
<|endoftext|>


<commit_before>
import collections
import struct
import sys

PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3

#
# Byte packing helpers
#

pack_4 = struct.Struct('>L').pack
pack_8 = struct.Struct('>Q').pack
pack_2_8 = struct.Struct('>HQ').pack

def unpack_4(value, _unpack=struct.Struct('>L').unpack):
    return _unpack(value.rjust(4, b'\x00'))[0]

def unpack_8(value, _unpack=struct.Struct('>Q').unpack):
    return _unpack(value.rjust(8, b'\x00'))[0]

#
# Components and types
#
# Composite components like date and time are split to make the
# implementation simpler. Each component is a tuple with these
# components:
#
#   (name, size, mask, min_value, max_value, empty)
#

YEAR_MIN, YEAR_MAX, YEAR_EMPTY, YEAR_MASK = 0, 4094, 4095, 0xfff
MONTH_MIN, MONTH_MAX, MONTH_EMPTY, MONTH_MASK = 0, 11, 15, 0xf
DAY_MIN, DAY_MAX, DAY_EMPTY, DAY_MASK = 0, 30, 31, 0x1f
HOUR_MIN, HOUR_MAX, HOUR_EMPTY, HOUR_MASK = 0, 23, 31, 0x1f
MINUTE_MIN, MINUTE_MAX, MINUTE_EMPTY, MINUTE_MASK = 0, 59, 63, 0x3f
SECOND_MIN, SECOND_MAX, SECOND_EMPTY, SECOND_MASK = 0, 60, 63, 0x3f
MILLISECOND_MIN, MILLISECOND_MAX, MILLISECOND_MASK = 0, 999, 0x3ff
MICROSECOND_MIN, MICROSECOND_MAX, MICROSECOND_MASK = 0, 999999, 0xfffff
NANOSECOND_MIN, NANOSECOND_MAX, NANOSECOND_MASK = 0, 999999999, 0x3fffffff

SUPPORTED_TYPES = set(['D', 'T', 'DT', 'DTZ', 'DTS', 'DTSZ'])

D_MASK = 0x1fffff
T_MASK = 0x1ffff

#
# Public API
#

Value = collections.namedtuple('Value', [
    'year', 'month', 'day',
    'hour', 'minute', 'second',
    'millisecond', 'microsecond', 'nanosecond',
    'tz_hour', 'tz_minute', 'tz_offset'])

def packb(
        type=None,
        year=None, month=None, day=None,
        hour=None, minute=None, second=None,
        millisecond=None, microsecond=None, nanosecond=None):
    """
    Pack date and time information into a byte string.

    :return: encoded temporenc value
    :rtype: bytes
    """

    #
    # Type detection
    #

    if type is None:
        has_d = not (year is None and month is None and day is None)
        has_t = not (hour is None and minute is None and second is None)
        has_s = not (millisecond is None and microsecond is None
                     and nanosecond is None)

        if has_s:
            type = 'DTS'
        elif has_d and has_t:
            type = 'DT'
        elif has_d:
            type = 'D'
        elif has_t:
            type = 'T'
        else:
            # No information at all, just use the smallest type
            type = 'D'

    elif type not in SUPPORTED_TYPES:
        raise ValueError("invalid temporenc type: {0!r}".format(type))

    #
    # Value checking
    #

    if year is None:
        year = YEAR_EMPTY
    elif not YEAR_MIN <= year <= YEAR_MAX:
        raise ValueError("'year' not within supported range")

    if month is None:
        month = MONTH_EMPTY
    else:
        month -= 1
        if not MONTH_MIN <= month <= MONTH_MAX:
            raise ValueError("'month' not within supported range")

    if day is None:
        day = DAY_EMPTY
    else:
        day -= 1
        if not DAY_MIN <= day <= DAY_MAX:
            raise ValueError("'day' not within supported range")

    if hour is None:
        hour = HOUR_EMPTY
    elif not HOUR_MIN <= hour <= HOUR_MAX:
        raise ValueError("'hour' not within supported range")

    if minute is None:
        minute = MINUTE_EMPTY
    elif not MINUTE_MIN <= minute <= MINUTE_MAX:
        raise ValueError("'minute' not within supported range")

    if second is None:
        second = SECOND_EMPTY
    elif not SECOND_MIN <= second <= SECOND_MAX:
        raise ValueError("'second' not within supported range")

    if (millisecond is not None
            and not MILLISECOND_MIN <= millisecond <= MILLISECOND_MAX):
        raise ValueError("'millisecond' not within supported range")

    if (microsecond is not None
            and not MICROSECOND_MIN <= microsecond <= MICROSECOND_MAX):
        raise ValueError("'microsecond' not within supported range")

    if (nanosecond is not None
            and not NANOSECOND_MIN <= nanosecond <= NANOSECOND_MAX):
        raise ValueError("'nanosecond' not within supported range")

    #
    # Byte packing
    #

    d = year << 9 | month << 5 | day
    t = hour << 12 | minute << 6 | second

    if type == 'D':
        # 100DDDDD DDDDDDDD DDDDDDDD
        return pack_4(0x800000 | d)[-3:]

    elif type == 'T':
        # 1010000T TTTTTTTT TTTTTTTT
        return pack_4(0xa00000 | t)[-3:]

    elif type == 'DT':
        # 00DDDDDD DDDDDDDD DDDDDDDT TTTTTTTT
        # TTTTTTTT
        return pack_8(d << 17 | t)[-5:]

    elif type == 'DTS':
        if nanosecond is not None:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSSSSSS SSSSSSSS
            # SSSSSSSS
            return pack_2_8(
                0b0110 << 4 | d >> 17,
                (d & 0x1ffff) << 47 | t << 30 | nanosecond)[-9:]
        elif microsecond is not None:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSSSSSS SSSSSS00
            return pack_8(
                0b0101 << 60 | d << 39 | t << 22 | microsecond << 2)
        elif millisecond is not None:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSS0000
            return pack_8(
                0b0100 << 52 | d << 31 | t << 14 | millisecond << 4)[-7:]
        else:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TT000000
            return pack_8(0b0111 << 44 | d << 23 | t << 6)[-6:]

    raise NotImplementedError()

def unpackb(value):
    if not 3 <= len(value) <= 10:
        raise ValueError("value must be between 3 and 10 bytes")

    #
    # Unpack components
    #

    first = value[0]
    if PY2:
        first = ord(first)

    d = t = millisecond = microsecond = nanosecond = None

    if first <= 0b00111111:
        # Type DT, tag 00

        if not len(value) == 5:
            raise ValueError(
                "DT value must be 5 bytes; got {0:d}".format(len(value)))

        n = unpack_8(value)
        d = n >> 17 & D_MASK
        t = n & T_MASK

    elif first <= 0b01111111:
        # Type DTS, tag 01

        precision = first >> 4 & 0x03
        expected_length = 6 if precision == 3 else 7 + precision
        if not len(value) == expected_length:
            raise ValueError(
                "DTS value has incorrect length; expected, {0:d}, "
                "got {1:d}".format(expected_length, len(value)))

        # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
        # TTTTTTTT TT...... (first 6 bytes)
        n = unpack_8(value[:6]) >> 6
        d = n >> 23 & D_MASK
        t = n >> 6 & T_MASK

        # Extract S component from last 4 bytes
        n = unpack_4(value[-4:])
        if precision == 0:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSS0000
            millisecond = n >> 4 & MILLISECOND_MASK
        elif precision == 1:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSSSSSS SSSSSS00
            microsecond = n >> 2 & MICROSECOND_MASK
        elif precision == 2:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSSSSSS SSSSSSSS
            # SSSSSSSS
            nanosecond = n & NANOSECOND_MASK
        elif precision == 3:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TT000000
            pass

    elif first <= 0b10011111:
        # Type D, tag 100

        if not len(value) == 3:
            raise ValueError(
                "D value must be 3 bytes; got {0:d}".format(len(value)))

        d = unpack_4(value) & D_MASK

    elif first <= 0b10100001:
        # Type T, tag 1010000

        if not len(value) == 3:
            raise ValueError(
                "T value must be 3 bytes; got {0:d}".format(len(value)))

        t = unpack_4(value) & T_MASK

    elif first <= 0b10111111:
        raise ValueError("first byte does not contain a valid tag")

    elif first <= 0b11011111:  # tag 110
        raise NotImplementedError("DTZ")

    elif first <= 0b11111111:  # tag 111
        raise NotImplementedError("DTSZ")

    #
    # Split D and T components
    #

    if d is None:
        year = month = day = None
    else:
        year = d >> 9 & YEAR_MASK
        month = (d >> 5 & MONTH_MASK) + 1
        day = (d & DAY_MASK) + 1

    if t is None:
        hour = minute = second = None
    else:
        hour = t >> 12 & HOUR_MASK
        minute = t >> 6 & MINUTE_MASK
        second = t & SECOND_MASK

    #
    # Sub-second fields are either all None, or none are None.
    #

    if millisecond is not None:
        microsecond = millisecond * 1000
        nanosecond = microsecond * 1000
    elif microsecond is not None:
        millisecond = microsecond // 1000
        nanosecond = microsecond * 1000
    elif nanosecond is not None:
        microsecond = nanosecond // 1000
        millisecond = microsecond // 1000

    return Value(
        year, month, day,
        hour, minute, second,
        millisecond, microsecond, nanosecond,
        None, None, None,  # TODO: timezone
    )
<commit_msg>Add tz_offset arg to packb() and extend type detection<commit_after>
import collections
import struct
import sys

PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3

#
# Byte packing helpers
#

pack_4 = struct.Struct('>L').pack
pack_8 = struct.Struct('>Q').pack
pack_2_8 = struct.Struct('>HQ').pack

def unpack_4(value, _unpack=struct.Struct('>L').unpack):
    return _unpack(value.rjust(4, b'\x00'))[0]

def unpack_8(value, _unpack=struct.Struct('>Q').unpack):
    return _unpack(value.rjust(8, b'\x00'))[0]

#
# Components and types
#
# Composite components like date and time are split to make the
# implementation simpler. Each component is a tuple with these
# components:
#
#   (name, size, mask, min_value, max_value, empty)
#

YEAR_MIN, YEAR_MAX, YEAR_EMPTY, YEAR_MASK = 0, 4094, 4095, 0xfff
MONTH_MIN, MONTH_MAX, MONTH_EMPTY, MONTH_MASK = 0, 11, 15, 0xf
DAY_MIN, DAY_MAX, DAY_EMPTY, DAY_MASK = 0, 30, 31, 0x1f
HOUR_MIN, HOUR_MAX, HOUR_EMPTY, HOUR_MASK = 0, 23, 31, 0x1f
MINUTE_MIN, MINUTE_MAX, MINUTE_EMPTY, MINUTE_MASK = 0, 59, 63, 0x3f
SECOND_MIN, SECOND_MAX, SECOND_EMPTY, SECOND_MASK = 0, 60, 63, 0x3f
MILLISECOND_MIN, MILLISECOND_MAX, MILLISECOND_MASK = 0, 999, 0x3ff
MICROSECOND_MIN, MICROSECOND_MAX, MICROSECOND_MASK = 0, 999999, 0xfffff
NANOSECOND_MIN, NANOSECOND_MAX, NANOSECOND_MASK = 0, 999999999, 0x3fffffff

SUPPORTED_TYPES = set(['D', 'T', 'DT', 'DTZ', 'DTS', 'DTSZ'])

D_MASK = 0x1fffff
T_MASK = 0x1ffff

#
# Public API
#

Value = collections.namedtuple('Value', [
    'year', 'month', 'day',
    'hour', 'minute', 'second',
    'millisecond', 'microsecond', 'nanosecond',
    'tz_hour', 'tz_minute', 'tz_offset'])

def packb(
        type=None,
        year=None, month=None, day=None,
        hour=None, minute=None, second=None,
        millisecond=None, microsecond=None, nanosecond=None,
        tz_offset=None):
    """
    Pack date and time information into a byte string.

    :return: encoded temporenc value
    :rtype: bytes
    """

    #
    # Type detection
    #

    if type is None:
        has_d = not (year is None and month is None and day is None)
        has_t = not (hour is None and minute is None and second is None)
        has_s = not (millisecond is None and microsecond is None
                     and nanosecond is None)
        has_z = tz_offset is not None

        if has_z and has_s:
            type = 'DTSZ'
        elif has_z:
            type = 'DTZ'
        elif has_s:
            type = 'DTS'
        elif has_d and has_t:
            type = 'DT'
        elif has_d:
            type = 'D'
        elif has_t:
            type = 'T'
        else:
            # No information at all, just use the smallest type
            type = 'D'

    elif type not in SUPPORTED_TYPES:
        raise ValueError("invalid temporenc type: {0!r}".format(type))

    #
    # Value checking
    #

    if year is None:
        year = YEAR_EMPTY
    elif not YEAR_MIN <= year <= YEAR_MAX:
        raise ValueError("'year' not within supported range")

    if month is None:
        month = MONTH_EMPTY
    else:
        month -= 1
        if not MONTH_MIN <= month <= MONTH_MAX:
            raise ValueError("'month' not within supported range")

    if day is None:
        day = DAY_EMPTY
    else:
        day -= 1
        if not DAY_MIN <= day <= DAY_MAX:
            raise ValueError("'day' not within supported range")

    if hour is None:
        hour = HOUR_EMPTY
    elif not HOUR_MIN <= hour <= HOUR_MAX:
        raise ValueError("'hour' not within supported range")

    if minute is None:
        minute = MINUTE_EMPTY
    elif not MINUTE_MIN <= minute <= MINUTE_MAX:
        raise ValueError("'minute' not within supported range")

    if second is None:
        second = SECOND_EMPTY
    elif not SECOND_MIN <= second <= SECOND_MAX:
        raise ValueError("'second' not within supported range")

    if (millisecond is not None
            and not MILLISECOND_MIN <= millisecond <= MILLISECOND_MAX):
        raise ValueError("'millisecond' not within supported range")

    if (microsecond is not None
            and not MICROSECOND_MIN <= microsecond <= MICROSECOND_MAX):
        raise ValueError("'microsecond' not within supported range")

    if (nanosecond is not None
            and not NANOSECOND_MIN <= nanosecond <= NANOSECOND_MAX):
        raise ValueError("'nanosecond' not within supported range")

    #
    # Byte packing
    #

    d = year << 9 | month << 5 | day
    t = hour << 12 | minute << 6 | second

    if type == 'D':
        # 100DDDDD DDDDDDDD DDDDDDDD
        return pack_4(0x800000 | d)[-3:]

    elif type == 'T':
        # 1010000T TTTTTTTT TTTTTTTT
        return pack_4(0xa00000 | t)[-3:]

    elif type == 'DT':
        # 00DDDDDD DDDDDDDD DDDDDDDT TTTTTTTT
        # TTTTTTTT
        return pack_8(d << 17 | t)[-5:]

    elif type == 'DTS':
        if nanosecond is not None:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSSSSSS SSSSSSSS
            # SSSSSSSS
            return pack_2_8(
                0b0110 << 4 | d >> 17,
                (d & 0x1ffff) << 47 | t << 30 | nanosecond)[-9:]
        elif microsecond is not None:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSSSSSS SSSSSS00
            return pack_8(
                0b0101 << 60 | d << 39 | t << 22 | microsecond << 2)
        elif millisecond is not None:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSS0000
            return pack_8(
                0b0100 << 52 | d << 31 | t << 14 | millisecond << 4)[-7:]
        else:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TT000000
            return pack_8(0b0111 << 44 | d << 23 | t << 6)[-6:]

    raise NotImplementedError()

def unpackb(value):
    if not 3 <= len(value) <= 10:
        raise ValueError("value must be between 3 and 10 bytes")

    #
    # Unpack components
    #

    first = value[0]
    if PY2:
        first = ord(first)

    d = t = millisecond = microsecond = nanosecond = None

    if first <= 0b00111111:
        # Type DT, tag 00

        if not len(value) == 5:
            raise ValueError(
                "DT value must be 5 bytes; got {0:d}".format(len(value)))

        n = unpack_8(value)
        d = n >> 17 & D_MASK
        t = n & T_MASK

    elif first <= 0b01111111:
        # Type DTS, tag 01

        precision = first >> 4 & 0x03
        expected_length = 6 if precision == 3 else 7 + precision
        if not len(value) == expected_length:
            raise ValueError(
                "DTS value has incorrect length; expected, {0:d}, "
                "got {1:d}".format(expected_length, len(value)))

        # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
        # TTTTTTTT TT...... (first 6 bytes)
        n = unpack_8(value[:6]) >> 6
        d = n >> 23 & D_MASK
        t = n >> 6 & T_MASK

        # Extract S component from last 4 bytes
        n = unpack_4(value[-4:])
        if precision == 0:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSS0000
            millisecond = n >> 4 & MILLISECOND_MASK
        elif precision == 1:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSSSSSS SSSSSS00
            microsecond = n >> 2 & MICROSECOND_MASK
        elif precision == 2:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TTSSSSSS SSSSSSSS SSSSSSSS
            # SSSSSSSS
            nanosecond = n & NANOSECOND_MASK
        elif precision == 3:
            # 01PPDDDD DDDDDDDD DDDDDDDD DTTTTTTT
            # TTTTTTTT TT000000
            pass

    elif first <= 0b10011111:
        # Type D, tag 100

        if not len(value) == 3:
            raise ValueError(
                "D value must be 3 bytes; got {0:d}".format(len(value)))

        d = unpack_4(value) & D_MASK

    elif first <= 0b10100001:
        # Type T, tag 1010000

        if not len(value) == 3:
            raise ValueError(
                "T value must be 3 bytes; got {0:d}".format(len(value)))

        t = unpack_4(value) & T_MASK

    elif first <= 0b10111111:
        raise ValueError("first byte does not contain a valid tag")

    elif first <= 0b11011111:  # tag 110
        raise NotImplementedError("DTZ")

    elif first <= 0b11111111:  # tag 111
        raise NotImplementedError("DTSZ")

    #
    # Split D and T components
    #

    if d is None:
        year = month = day = None
    else:
        year = d >> 9 & YEAR_MASK
        month = (d >> 5 & MONTH_MASK) + 1
        day = (d & DAY_MASK) + 1

    if t is None:
        hour = minute = second = None
    else:
        hour = t >> 12 & HOUR_MASK
        minute = t >> 6 & MINUTE_MASK
        second = t & SECOND_MASK

    #
    # Sub-second fields are either all None, or none are None.
    #

    if millisecond is not None:
        microsecond = millisecond * 1000
        nanosecond = microsecond * 1000
    elif microsecond is not None:
        millisecond = microsecond // 1000
        nanosecond = microsecond * 1000
    elif nanosecond is not None:
        microsecond = nanosecond // 1000
        millisecond = microsecond // 1000

    return Value(
        year, month, day,
        hour, minute, second,
        millisecond, microsecond, nanosecond,
        None, None, None,  # TODO: timezone
    )
<|endoftext|>


<commit_before>import cv2
import numpy as np

def resize_tensor(tensor, new_size):
    """
    Resizes an input tensor with opencv. Resize is done for each channel indipendently,

    :param tensor: must have size (channels, h, w)
    :param new_size: tuple like (new_h, new_w)
    :return: the resized tensor having size (channels, new_h, new_w)
    """
    channels = tensor.shape[0]
    new_tensor = np.zeros(shape=(channels,)+new_size)
    for i in range(0, channels):
        new_tensor[i] = cv2.resize(tensor[i], dsize=new_size[::-1])

    return new_tensor
<commit_msg>Added function to crop tensors<commit_after>import cv2
import numpy as np

def resize_tensor(tensor, new_size):
    """
    Resizes a numeric input tensor with opencv. Resize is done for each channel indipendently.

    :param tensor: must have size (channels, h, w)
    :param new_size: tuple like (new_h, new_w)
    :return: the resized tensor having size (channels, new_h, new_w)
    """
    channels = tensor.shape[0]
    new_tensor = np.zeros(shape=(channels,)+new_size)
    for i in range(0, channels):
        new_tensor[i] = cv2.resize(tensor[i], dsize=new_size[::-1])

    return new_tensor

def crop_tensor(tensor, indexes):
    """
    Crops a numeric input tensor.

    :param tensor: must have size (channels, h, w)
    :param indexes: in the form (h1, h2, w1, w2)
    :return: the cropped tensor having size (channels, h2-h1, w2-w1)
    """
    h1, h2, w1, w2 = indexes
    new_tensor = tensor[:, h1:h2, w1:w2].copy()

    return new_tensor
<|endoftext|>


<commit_before>from abc import ABCMeta, abstractmethod
from collections.abc import Callable

class Singleton(metaclass=ABCMeta):
    __instance = None

    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = \
                super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls.__instance

class Context(dict, Callable):

    def __call__(self, key, default=None):
        if default is not None and key not in self:
            return default
        return self[key]

    def __getattr__(self, attr):
        return self[attr]

class Runner(Callable):

    def __init__(self, context):
        self._context = context
        self._initialize()

    def _initialize(self):
        pass

    def setup(self):
        pass

    def teardown(self):
        pass

    def __call__(self):
        with self as s:
            s.process()

    @abstractmethod
    def process(self):
        raise NotImplementedError()

    def __enter__(self):
        self.setup()

    def __exit__(self):
        self.teardown()
<commit_msg>add iterator.<commit_after>from abc import ABCMeta, abstractmethod
from collections.abc import Callable

class Singleton(metaclass=ABCMeta):
    __instance = None

    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = \
                super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls.__instance

class Iterable(metaclass=ABCMeta):

    @abstractmethod
    def __iter__(self):
        raise NotImplementedError

class Iterator(Iterable):

    def __iter__(self):
        return self

    @abstractmethod
    def __next__(self):
        raise NotImplementedError

class Context(dict, Callable):

    def __call__(self, key, default=None):
        if default is not None and key not in self:
            return default
        return self[key]

    def __getattr__(self, attr):
        return self[attr]

class Runner(Callable):

    def __init__(self, context):
        self._context = context
        self._initialize()

    def _initialize(self):
        pass

    def setup(self):
        pass

    def teardown(self):
        pass

    def __call__(self):
        with self as s:
            s.process()

    @abstractmethod
    def process(self):
        raise NotImplementedError()

    def __enter__(self):
        self.setup()

    def __exit__(self):
        self.teardown()
<|endoftext|>


<commit_before># GameController is class based on Flask.MethodView which provides the logic
# required to handle get and post requests. It could also handle put, patch, etc.,
# but currently only supports GET (get a new game) and POST (make a guess against
# an existing game

# Import standard packages
import json
import logging
import socket
import sys
from redis.exceptions import ConnectionError

# Import flask packages
from flask import request
from flask.views import MethodView
from flask_helpers.build_response import build_response
from flask_helpers.ErrorHandler import ErrorHandler
from werkzeug.exceptions import BadRequest

# Import the Game and GameObject.
from python_cowbull_game import GameController

# Import a persistence package
from Persistence.RedisPersist import RedisPersist as PersistenceEngine

# Import the Flask app object
from python_cowbull_server import app

class GameServerController(MethodView):
    """
    tbc
    """
    # Initialize class level variables
    handler = None

    def __init__(self):
        # Get an error handler that can be used to handle errors and log to
        # std i/o. The error handler logs the error and forms an HTML response
        # using Flask's Response class.
        self.handler = ErrorHandler(module="GameServerController", method="__init__")
        self.handler.log(message="Loading configuration from environment.", status=0)

        # Get key configuration information from Flask's configuration engine.
        # These should have all been set before (in python_cowbull_server/__init__.py)
        # and if they haven't (for whatever reason) an exception WILL be raised.
        self.game_version = app.config.get("GAME_VERSION")
        self.redis_host = app.config.get("REDIS_HOST")
        self.redis_port = app.config.get("REDIS_PORT")
        self.redis_db = app.config.get("REDIS_DB")
        self.redis_auth = app.config.get("REDIS_USEAUTH")

        # Log the configuration to the handler.
        self.handler.log(
            message="Redis configured for: {}:{}/{} -- auth? {}"\
                .format(
                    self.redis_host,
                    self.redis_port,
                    self.redis_db,
                    self.redis_auth
                ),
            status=0
        )

    def get(self):
        # Set the error handler to default the module and method so that logging
        # calls can be more precise and easier to read.
        self.handler = ErrorHandler(module="GameServerController", method="get")
        self.handler.log(message='Processing GET request', status=0)

        # Check if a game mode has been passed as a query parameter. If it has,
        # validate the mode and use it to create the game. If it hasn't, then
        # default to normal.
        game_mode = request.args.get('mode', 'normal')

        try:
            game_controller = GameController(mode=game_mode)
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception="Invalid game mode",
                message="{}: game mode {}!".format(str(ve), game_mode)
            )

#        print(game_controller.game_mode_names)
#        if game_mode not in game_controller.game_mode_names:
#            return self.handler.error(
#                status=400, exception="Invalid game mode", message="There is no game mode {}!".format(game_mode)
#            )

        # Get a persistence engine. Currently, this is set to be redis but can
        # easily be changed simply by changing the import statement above. Ideally
        # an abstract class would be created which concrete classes could inherit
        # from to ensure uniform consistency in persistence handling regardless
        # of the engine (Redis, Mongo, etc.) used.
        try:
            persister = PersistenceEngine\
                (host=self.redis_host, port=self.redis_port, db=self.redis_db)

            self.handler.log(message='Persister instantiated', status=0)
        except ConnectionError as ce:
            return self.handler.error(status=503, exception=str(ce), message="There is no redis service available!")
        except AttributeError as ae:
            return self.handler.error(status=503, exception=str(ae), message="An internal error occurred - attribute missing for redis - check GameServerController:__init__")

        # Instantiate a game object. This calls the cowbull game object and creates
        # an empty object.
        print("Game mode is {}".format(game_mode))
#        game_controller.new(mode=game_mode)
        self.handler.log(message='New game created with key {}'.format(game_controller.game.key), status=0)

        #
        # Save the newly created game
        #
        persister.save(game_controller.game.key, game_controller.save())
        self.handler.log(message='Game {} persisted.'.format(game_controller.game.key), status=0)

        #
        # Build the user response - key, no. of digits, and no. of guesses
        #
        _response = {
            "key": game_controller.game.key,
            "digits": game_controller.game.digits,
            "digit-type": game_controller.game.digit_type,
            "guesses": game_controller.game.guesses_allowed,
            "served-by": socket.gethostname()
        }

        self.handler.log(message='GET request fulfilled. Returned: {}'.format(_response), status=0)

        return build_response(html_status=200, response_data=_response, response_mimetype="application/json")

    def post(self):
        self.handler = ErrorHandler(module="GameServerController", method="post")
        self.handler.log(message='Processing POST request.', status=0)

        #
        # Get the JSON from the request
        #
        try:
            self.handler.log(message='Attempting to load JSON', status=0)
            json_dict = request.get_json()
            self.handler.log(message='Loaded JSON. Returned: {}'.format(json_dict), status=0)
        except BadRequest as e:
            return self.handler.error(
                status=400,
                exception=e.description,
                message="Bad request. There was no JSON present. ### LIKELY CALLER ERROR ###"
            )

        #
        # Get a persister
        #
        try:
            self.handler.log(message='Getting persister', status=0)
            persister = PersistenceEngine(host=self.redis_host, port=self.redis_port, db=self.redis_db)
        except ConnectionError as ce:
            return self.handler.error(
                status=503,
                exception=str(ce),
                message="There is no redis service available!"
            )

        #
        # Load the game based on the JSON. If the JSON data is invalid, return a
        # response to the user indicating an HTML status, the exception, and an
        # explanatory message.
        #
        try:
            _key = json_dict["key"]
            self.handler.log(message='Attempting to load game {}'.format(_key), status=0)
        except TypeError as te:
            return self.handler.error(
                status=500,
                exception=str(te),
                message="Bad request. For some reason the json_dict is None! Are you "
                        "sure the header is set to application/json?"
            )

        try:
            #_game = GameController(game=str(persister.load(_key)))
            _game = GameController()
            _game.load(game=str(persister.load(_key)))
            self.handler.log(message='Loaded game {}'.format(_key), status=0)
        except RuntimeError as ve:
            return self.handler.error(
                status=500,
                exception=str(ve),
                message="Bad request. For some reason the json_dict was None!"
            )
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain a valid game key."
            )
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="The game key provided was invalid."
            )

        #
        # Get the digits being guessed and add them to a list
        #
        try:
            self.handler.log(message='Getting digits from {}'.format(json_dict))
            _guesses = json_dict["digits"]
            self.handler.log(message='Guesses extracted from JSON: {}'.format(_guesses), status=0)
        except RuntimeError as ve:
            return self.handler.error(
                status=500,
                exception=str(ve),
                message="Bad request. For some reason the json_dict was None!"
            )
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception=str(ve),
                message="There was a problem with the value of the digits provided!"
            )
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain an array of digits called 'digits'"
            )
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="The game key provided was invalid."
            )

        #
        # Make a guess
        #
        try:
            self.handler.log(message="In guess")
            self.handler.log(message='Making a guess with digits: {}'.format(_guesses), status=0)
            _analysis = _game.guess(*_guesses)
            self.handler.log(message='Retrieved guess analysis', status=0)
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception=str(ve),
                message="There is a problem with the digits provided!"
            )

        #
        # Save the game
        #
        self.handler.log(message='Update game (save) after guess', status=0)
        save_game = _game.save()
        persister.save(key=_key, jsonstr=save_game)

        #
        # Return the analysis of the guess to the user.
        #
        _display_info = json.loads(save_game)
        del(_display_info["answer"])
        _return_response = \
            {
                "game": _display_info,
                "outcome": _analysis,
                "served-by": socket.gethostname()
            }

        self.handler.log(message='Returning analysis and game info to caller', status=0)
        return build_response(response_data=_return_response)
<commit_msg>Update GameServerController, modify GET for new GameObject model<commit_after># GameController is class based on Flask.MethodView which provides the logic
# required to handle get and post requests. It could also handle put, patch, etc.,
# but currently only supports GET (get a new game) and POST (make a guess against
# an existing game

# Import standard packages
import json
import logging
import socket
import sys
from redis.exceptions import ConnectionError

# Import flask packages
from flask import request
from flask.views import MethodView
from flask_helpers.build_response import build_response
from flask_helpers.ErrorHandler import ErrorHandler
from werkzeug.exceptions import BadRequest

# Import the Game and GameObject.
from python_cowbull_game import GameController

# Import a persistence package
from Persistence.RedisPersist import RedisPersist as PersistenceEngine

# Import the Flask app object
from python_cowbull_server import app

class GameServerController(MethodView):
    """
    tbc
    """
    # Initialize class level variables
    handler = None

    def __init__(self):
        # Get an error handler that can be used to handle errors and log to
        # std i/o. The error handler logs the error and forms an HTML response
        # using Flask's Response class.
        self.handler = ErrorHandler(module="GameServerController", method="__init__")
        self.handler.log(message="Loading configuration from environment.", status=0)

        # Get key configuration information from Flask's configuration engine.
        # These should have all been set before (in python_cowbull_server/__init__.py)
        # and if they haven't (for whatever reason) an exception WILL be raised.
        self.game_version = app.config.get("GAME_VERSION")
        self.redis_host = app.config.get("REDIS_HOST")
        self.redis_port = app.config.get("REDIS_PORT")
        self.redis_db = app.config.get("REDIS_DB")
        self.redis_auth = app.config.get("REDIS_USEAUTH")

        # Log the configuration to the handler.
        self.handler.log(
            message="Redis configured for: {}:{}/{} -- auth? {}"\
                .format(
                    self.redis_host,
                    self.redis_port,
                    self.redis_db,
                    self.redis_auth
                ),
            status=0
        )

    def get(self):
        # Set the error handler to default the module and method so that logging
        # calls can be more precise and easier to read.
        self.handler = ErrorHandler(module="GameServerController", method="get")
        self.handler.log(message='Processing GET request', status=0)

        # Check if a game mode has been passed as a query parameter. If it has,
        # validate the mode and use it to create the game. If it hasn't, then
        # default to normal.
        game_mode = request.args.get('mode', 'normal')

        try:
            game_controller = GameController(mode=game_mode)
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception="Invalid game mode",
                message="{}: game mode {}!".format(str(ve), game_mode)
            )

#        print(game_controller.game_mode_names)
#        if game_mode not in game_controller.game_mode_names:
#            return self.handler.error(
#                status=400, exception="Invalid game mode", message="There is no game mode {}!".format(game_mode)
#            )

        # Get a persistence engine. Currently, this is set to be redis but can
        # easily be changed simply by changing the import statement above. Ideally
        # an abstract class would be created which concrete classes could inherit
        # from to ensure uniform consistency in persistence handling regardless
        # of the engine (Redis, Mongo, etc.) used.
        try:
            persister = PersistenceEngine\
                (host=self.redis_host, port=self.redis_port, db=self.redis_db)

            self.handler.log(message='Persister instantiated', status=0)
        except ConnectionError as ce:
            return self.handler.error(status=503, exception=str(ce), message="There is no redis service available!")
        except AttributeError as ae:
            return self.handler.error(status=503, exception=str(ae), message="An internal error occurred - attribute missing for redis - check GameServerController:__init__")

        # Instantiate a game object. This calls the cowbull game object and creates
        # an empty object.
        print("Game mode is {}".format(game_mode))
#        game_controller.new(mode=game_mode)
        self.handler.log(message='New game created with key {}'.format(game_controller.game.key), status=0)

        #
        # Save the newly created game
        #
        persister.save(game_controller.game.key, game_controller.save())
        self.handler.log(message='Game {} persisted.'.format(game_controller.game.key), status=0)

        #
        # Build the user response - key, no. of digits, and no. of guesses
        #
        _response = {
            "key": game_controller.game.key,
            "mode": game_controller.game.mode.mode,
            "digits": game_controller.game.mode.digits,
            "digit-type": game_controller.game.mode.digit_type,
            "guesses": game_controller.game.mode.guesses_allowed,
            "served-by": socket.gethostname()
        }

        self.handler.log(message='GET request fulfilled. Returned: {}'.format(_response), status=0)

        return build_response(html_status=200, response_data=_response, response_mimetype="application/json")

    def post(self):
        self.handler = ErrorHandler(module="GameServerController", method="post")
        self.handler.log(message='Processing POST request.', status=0)

        #
        # Get the JSON from the request
        #
        try:
            self.handler.log(message='Attempting to load JSON', status=0)
            json_dict = request.get_json()
            self.handler.log(message='Loaded JSON. Returned: {}'.format(json_dict), status=0)
        except BadRequest as e:
            return self.handler.error(
                status=400,
                exception=e.description,
                message="Bad request. There was no JSON present. ### LIKELY CALLER ERROR ###"
            )

        #
        # Get a persister
        #
        try:
            self.handler.log(message='Getting persister', status=0)
            persister = PersistenceEngine(host=self.redis_host, port=self.redis_port, db=self.redis_db)
        except ConnectionError as ce:
            return self.handler.error(
                status=503,
                exception=str(ce),
                message="There is no redis service available!"
            )

        #
        # Load the game based on the JSON. If the JSON data is invalid, return a
        # response to the user indicating an HTML status, the exception, and an
        # explanatory message.
        #
        try:
            _key = json_dict["key"]
            self.handler.log(message='Attempting to load game {}'.format(_key), status=0)
        except TypeError as te:
            return self.handler.error(
                status=500,
                exception=str(te),
                message="Bad request. For some reason the json_dict is None! Are you "
                        "sure the header is set to application/json?"
            )

        try:
            #_game = GameController(game=str(persister.load(_key)))
            _game = GameController()
            _game.load(game=str(persister.load(_key)))
            self.handler.log(message='Loaded game {}'.format(_key), status=0)
        except RuntimeError as ve:
            return self.handler.error(
                status=500,
                exception=str(ve),
                message="Bad request. For some reason the json_dict was None!"
            )
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain a valid game key."
            )
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="The game key provided was invalid."
            )

        #
        # Get the digits being guessed and add them to a list
        #
        try:
            self.handler.log(message='Getting digits from {}'.format(json_dict))
            _guesses = json_dict["digits"]
            self.handler.log(message='Guesses extracted from JSON: {}'.format(_guesses), status=0)
        except RuntimeError as ve:
            return self.handler.error(
                status=500,
                exception=str(ve),
                message="Bad request. For some reason the json_dict was None!"
            )
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception=str(ve),
                message="There was a problem with the value of the digits provided!"
            )
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain an array of digits called 'digits'"
            )
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="The game key provided was invalid."
            )

        #
        # Make a guess
        #
        try:
            self.handler.log(message="In guess")
            self.handler.log(message='Making a guess with digits: {}'.format(_guesses), status=0)
            _analysis = _game.guess(*_guesses)
            self.handler.log(message='Retrieved guess analysis', status=0)
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception=str(ve),
                message="There is a problem with the digits provided!"
            )

        #
        # Save the game
        #
        self.handler.log(message='Update game (save) after guess', status=0)
        save_game = _game.save()
        persister.save(key=_key, jsonstr=save_game)

        #
        # Return the analysis of the guess to the user.
        #
        _display_info = json.loads(save_game)
        del(_display_info["answer"])
        _return_response = \
            {
                "game": _display_info,
                "outcome": _analysis,
                "served-by": socket.gethostname()
            }

        self.handler.log(message='Returning analysis and game info to caller', status=0)
        return build_response(response_data=_return_response)
<|endoftext|>


<commit_before># GameController is class based on Flask.MethodView which provides the logic
# required to handle get and post requests. It could also handle put, patch, etc.,
# but currently only supports GET (get a new game) and POST (make a guess against
# an existing game

# Import standard packages
import json
import socket
from redis.exceptions import ConnectionError

# Import flask packages
from flask import request
from flask.views import MethodView
from flask_helpers.build_response import build_response
from flask_helpers.ErrorHandler import ErrorHandler
from werkzeug.exceptions import BadRequest

# Import the Game Controller; NB: a subclassed GameController is imported
# from extensions.ExtGameController which allows specific actions (such as
# additional modes) to be added to the game by default.
#
from extensions.ExtGameController import ExtGameController as GameController

# Import a persistence package
from Persistence.RedisPersist import RedisPersist as PersistenceEngine

# Import the Flask app object
from python_cowbull_server import app

class GameServerController(MethodView):
    """
    tbc
    """

    def __init__(self):
        """
        TBC
        """

        #
        # Get an error handler that can be used to handle errors and log to
        # std i/o. The error handler logs the error and forms an HTML response
        # using Flask's Response class.
        #
        self.handler = ErrorHandler(module="GameServerController", method="__init__")
        self.handler.log(message="Loading configuration from environment.", status=0)

        #
        # Get key configuration information from Flask's configuration engine.
        # These should have all been set before (in python_cowbull_server/__init__.py)
        # and if they haven't (for whatever reason) an exception WILL be raised.
        #
        self.game_version = app.config.get("GAME_VERSION")
        self.redis_host = app.config.get("REDIS_HOST")
        self.redis_port = app.config.get("REDIS_PORT")
        self.redis_db = app.config.get("REDIS_DB")
        self.redis_auth = app.config.get("REDIS_USEAUTH")

        #
        # Log the configuration to the handler.
        #
        self.handler.log(
            message="Redis configured for: {}:{}/{} -- auth? {}"\
                .format(
                    self.redis_host,
                    self.redis_port,
                    self.redis_db,
                    self.redis_auth
                ),
            status=0
        )

    def get(self):
        """
        TBC
        :return:
        """

        #
        # Set the error handler to default the module and method so that logging
        # calls can be more precise and easier to read.
        #
        self.handler = ErrorHandler(module="GameServerController", method="get")
        self.handler.log(message='Processing GET request', status=0)

        #
        # Check if a game mode has been passed as a query parameter. If it has,
        # use it to create the game. If it hasn't, then let the game decide.
        #
        game_mode = request.args.get('mode', None)

        try:
            self.handler.log(message="Creating game with mode {} ({})".format(game_mode, type(game_mode)))
            game_controller = GameController(mode=str(game_mode))
            self.handler.log(message='New game created with key {}'.format(game_controller.game.key), status=0)
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception="Invalid game mode",
                message="{}: game mode {}!".format(str(ve), game_mode)
            )

        # Get a persistence engine. Currently, this is set to be redis but can
        # easily be changed simply by changing the import statement above. Ideally
        # an abstract class would be created which concrete classes could inherit
        # from to ensure uniform consistency in persistence handling regardless
        # of the engine (Redis, Mongo, etc.) used.
        try:
            persister = PersistenceEngine(
                host=self.redis_host,
                port=self.redis_port,
                db=self.redis_db
            )
            self.handler.log(message='Persister instantiated', status=0)
        except ConnectionError as ce:
            return self.handler.error(status=503, exception=str(ce), message="There is no redis service available!")
        except AttributeError as ae:
            return self.handler.error(status=503, exception=str(ae), message="An internal error occurred - attribute missing for redis - check GameServerController:__init__")

        #
        # Save the newly created game to the persistence engine
        #
        persister.save(game_controller.game.key, game_controller.save())
        self.handler.log(message='Game {} persisted.'.format(game_controller.game.key), status=0)

        #
        # Build the user response - key, no. of digits, and no. of guesses
        #
        _response = {
            "key": game_controller.game.key,
            "mode": game_controller.game.mode.mode,
            "digits": game_controller.game.mode.digits,
            "digit-type": game_controller.game.mode.digit_type,
            "guesses": game_controller.game.mode.guesses_allowed,
            "served-by": socket.gethostname(),
            "help-text": game_controller.game.mode.help_text,
            "instruction-text": game_controller.game.mode.instruction_text
        }

        self.handler.log(message='GET request fulfilled. Returned: {}'.format(_response), status=0)

        return build_response(
            html_status=200,
            response_data=_response,
            response_mimetype="application/json"
        )

    def post(self):
        """
        TBC
        :return:
        """

        self.handler = ErrorHandler(module="GameServerController", method="post")
        self.handler.log(message='Processing POST request.', status=0)

        #
        # Get the JSON from the POST request. If there is no JSON then an exception
        # is raised. IMPORTANT NOTE: When debugging ensuring the Content-type is
        # set to application/json - for example (using cURL):
        #
        # curl -H "Content-type: application/json" ...
        #
        try:
            self.handler.log(message='Attempting to load JSON', status=0)
            json_dict = request.get_json()
            self.handler.log(message='Loaded JSON. Returned: {}'.format(json_dict), status=0)
        except BadRequest as e:
            return self.handler.error(
                status=400,
                exception=e.description,
                message="Bad request. There was no JSON present. Are you sure the "
                        "header Content-type is set to application/json?"
            )

        #
        # Get a persister to enable the game to be loaded and then saved (updated).
        # See the GET method above for more information on the persister.
        #
        try:
            self.handler.log(message='Getting persister', status=0)
            persister = PersistenceEngine(host=self.redis_host, port=self.redis_port, db=self.redis_db)
        except ConnectionError as ce:
            return self.handler.error(
                status=503,
                exception=str(ce),
                message="There is no redis service available!"
            )

        #
        # Get the game key from the JSON provided above. If it does not exist,
        # return a 400 status (client) error
        #
        try:
            _key = json_dict["key"]
            self.handler.log(message='Attempting to load game {}'.format(_key), status=0)
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="Bad request. For some reason the json_dict is None! Are you "
                        "sure the header is set to application/json?"
            )

        #
        # Load the game based on the key contained in the JSON provided to
        # the POST request. If the JSON data is invalid, return a
        # response to the user indicating an HTML status, the exception, and an
        # explanatory message. If the data
        #
        try:
            _persisted_response = persister.load(key=_key)
            if not _persisted_response:
                raise KeyError(
                    "The game key ({}) was not found in the persistence engine!".format(_key)
                )
            self.handler.log(message="Persister response: {}".format(_persisted_response))

            _loaded_game = json.loads(_persisted_response)
            self.handler.log(message="Loaded game: {}".format(_loaded_game))
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain a valid game key."
            )

        #
        # Load the game based on the key contained in the JSON provided to
        # the POST request. If the JSON data is invalid, return a
        # response to the user indicating an HTML status, the exception, and an
        # explanatory message. If the data
        #
        try:
            self.handler.log(message="Loading game mode from: {}.".format(_loaded_game["mode"]))
            _mode = _loaded_game["mode"]
            self.handler.log(message="Loaded game mode {}.".format(str(_mode["mode"])))

            _game = GameController(
                game_json=json.dumps(_loaded_game),
                mode=str(_mode["mode"])
            )
            self.handler.log(message='Loaded game {}'.format(_key), status=0)
        except RuntimeError as ve:
            return self.handler.error(
                status=500,
                exception=str(ve),
                message="Bad request. For some reason the json_dict was None!"
            )
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain a valid game key."
            )
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="The game key provided was invalid."
            )
        except Exception as e:
            return self.handler.error(
                status=500,
                exception=repr(e),
                message="Exception occurred while loading game!"
            )

        #
        # Get the digits being guessed and add them to a list
        #
        try:
            self.handler.log(message='Getting digits from {}'.format(json_dict))
            _guesses = json_dict["digits"]
            self.handler.log(message='Guesses extracted from JSON: {}'.format(_guesses), status=0)
        except RuntimeError as ve:
            return self.handler.error(
                status=500,
                exception=str(ve),
                message="Bad request. For some reason the json_dict was None!"
            )
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception=str(ve),
                message="There was a problem with the value of the digits provided!"
            )
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain an array of digits called 'digits'"
            )
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="The game key provided was invalid."
            )

        #
        # Make a guess
        #
        try:
            self.handler.log(message="In guess")
            self.handler.log(message='Making a guess with digits: {}'.format(_guesses), status=0)
            _analysis = _game.guess(*_guesses)
            self.handler.log(message='Retrieved guess analysis', status=0)
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception=str(ve),
                message="There is a problem with the digits provided!"
            )

        #
        # Save the game
        #
        self.handler.log(message='Update game (save) after guess', status=0)
        save_game = _game.save()
        persister.save(key=_key, jsonstr=save_game)

        #
        # Return the analysis of the guess to the user.
        #
        _display_info = json.loads(save_game)
        del(_display_info["answer"])
        _return_response = \
            {
                "game": _display_info,
                "outcome": _analysis,
                "served-by": socket.gethostname()
            }

        self.handler.log(message='Returning analysis and game info to caller', status=0)
        return build_response(response_data=_return_response)
<commit_msg>Update to include instruction and help texts in GET response.<commit_after># GameController is class based on Flask.MethodView which provides the logic
# required to handle get and post requests. It could also handle put, patch, etc.,
# but currently only supports GET (get a new game) and POST (make a guess against
# an existing game

# Import standard packages
import json
import socket
from redis.exceptions import ConnectionError

# Import flask packages
from flask import request
from flask.views import MethodView
from flask_helpers.build_response import build_response
from flask_helpers.ErrorHandler import ErrorHandler
from werkzeug.exceptions import BadRequest

# Import the Game Controller; NB: a subclassed GameController is imported
# from extensions.ExtGameController which allows specific actions (such as
# additional modes) to be added to the game by default.
#
from extensions.ExtGameController import ExtGameController as GameController

# Import a persistence package
from Persistence.RedisPersist import RedisPersist as PersistenceEngine

# Import the Flask app object
from python_cowbull_server import app

class GameServerController(MethodView):
    """
    tbc
    """

    def __init__(self):
        """
        TBC
        """

        #
        # Get an error handler that can be used to handle errors and log to
        # std i/o. The error handler logs the error and forms an HTML response
        # using Flask's Response class.
        #
        self.handler = ErrorHandler(module="GameServerController", method="__init__")
        self.handler.log(message="Loading configuration from environment.", status=0)

        #
        # Get key configuration information from Flask's configuration engine.
        # These should have all been set before (in python_cowbull_server/__init__.py)
        # and if they haven't (for whatever reason) an exception WILL be raised.
        #
        self.game_version = app.config.get("GAME_VERSION")
        self.redis_host = app.config.get("REDIS_HOST")
        self.redis_port = app.config.get("REDIS_PORT")
        self.redis_db = app.config.get("REDIS_DB")
        self.redis_auth = app.config.get("REDIS_USEAUTH")

        #
        # Log the configuration to the handler.
        #
        self.handler.log(
            message="Redis configured for: {}:{}/{} -- auth? {}"\
                .format(
                    self.redis_host,
                    self.redis_port,
                    self.redis_db,
                    self.redis_auth
                ),
            status=0
        )

    def get(self):
        """
        TBC
        :return:
        """

        #
        # Set the error handler to default the module and method so that logging
        # calls can be more precise and easier to read.
        #
        self.handler = ErrorHandler(module="GameServerController", method="get")
        self.handler.log(message='Processing GET request', status=0)

        #
        # Check if a game mode has been passed as a query parameter. If it has,
        # use it to create the game. If it hasn't, then let the game decide.
        #
        game_mode = request.args.get('mode', None)

        try:
            self.handler.log(message="Creating game with mode {} ({})".format(game_mode, type(game_mode)))
            game_controller = GameController(mode=str(game_mode))
            self.handler.log(message='New game created with key {}'.format(game_controller.game.key), status=0)
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception="Invalid game mode",
                message="{}: game mode {}!".format(str(ve), game_mode)
            )

        # Get a persistence engine. Currently, this is set to be redis but can
        # easily be changed simply by changing the import statement above. Ideally
        # an abstract class would be created which concrete classes could inherit
        # from to ensure uniform consistency in persistence handling regardless
        # of the engine (Redis, Mongo, etc.) used.
        try:
            persister = PersistenceEngine(
                host=self.redis_host,
                port=self.redis_port,
                db=self.redis_db
            )
            self.handler.log(message='Persister instantiated', status=0)
        except ConnectionError as ce:
            return self.handler.error(status=503, exception=str(ce), message="There is no redis service available!")
        except AttributeError as ae:
            return self.handler.error(status=503, exception=str(ae), message="An internal error occurred - attribute missing for redis - check GameServerController:__init__")

        #
        # Save the newly created game to the persistence engine
        #
        persister.save(game_controller.game.key, game_controller.save())
        self.handler.log(message='Game {} persisted.'.format(game_controller.game.key), status=0)

        #
        # Build the user response - key, no. of digits, and no. of guesses
        #
        _response = {
            "key": game_controller.game.key,
            "mode": game_controller.game.mode.mode,
            "digits": game_controller.game.mode.digits,
            "digit-type": game_controller.game.mode.digit_type,
            "guesses": game_controller.game.mode.guesses_allowed,
            "served-by": socket.gethostname(),
            "help-text": game_controller.game.mode.help_text,
            "instruction-text": game_controller.game.mode.instruction_text
        }

        self.handler.log(message='GET request fulfilled. Returned: {}'.format(_response), status=0)

        return build_response(
            html_status=200,
            response_data=_response,
            response_mimetype="application/json"
        )

    def post(self):
        """
        TBC
        :return:
        """

        self.handler = ErrorHandler(module="GameServerController", method="post")
        self.handler.log(message='Processing POST request.', status=0)

        #
        # Get the JSON from the POST request. If there is no JSON then an exception
        # is raised. IMPORTANT NOTE: When debugging ensuring the Content-type is
        # set to application/json - for example (using cURL):
        #
        # curl -H "Content-type: application/json" ...
        #
        try:
            self.handler.log(message='Attempting to load JSON', status=0)
            json_dict = request.get_json()
            self.handler.log(message='Loaded JSON. Returned: {}'.format(json_dict), status=0)
        except BadRequest as e:
            return self.handler.error(
                status=400,
                exception=e.description,
                message="Bad request. There was no JSON present. Are you sure the "
                        "header Content-type is set to application/json?"
            )

        #
        # Get a persister to enable the game to be loaded and then saved (updated).
        # See the GET method above for more information on the persister.
        #
        try:
            self.handler.log(message='Getting persister', status=0)
            persister = PersistenceEngine(host=self.redis_host, port=self.redis_port, db=self.redis_db)
        except ConnectionError as ce:
            return self.handler.error(
                status=503,
                exception=str(ce),
                message="There is no redis service available!"
            )

        #
        # Get the game key from the JSON provided above. If it does not exist,
        # return a 400 status (client) error
        #
        try:
            _key = json_dict["key"]
            self.handler.log(message='Attempting to load game {}'.format(_key), status=0)
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="Bad request. For some reason the json_dict is None! Are you "
                        "sure the header is set to application/json?"
            )

        #
        # Load the game based on the key contained in the JSON provided to
        # the POST request. If the JSON data is invalid, return a
        # response to the user indicating an HTML status, the exception, and an
        # explanatory message. If the data
        #
        try:
            _persisted_response = persister.load(key=_key)
            if not _persisted_response:
                raise KeyError(
                    "The game key ({}) was not found in the persistence engine!".format(_key)
                )
            self.handler.log(message="Persister response: {}".format(_persisted_response))

            _loaded_game = json.loads(_persisted_response)
            self.handler.log(message="Loaded game: {}".format(_loaded_game))
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain a valid game key."
            )

        #
        # Load the game based on the key contained in the JSON provided to
        # the POST request. If the JSON data is invalid, return a
        # response to the user indicating an HTML status, the exception, and an
        # explanatory message. If the data
        #
        try:
            self.handler.log(message="Loading game mode from: {}.".format(_loaded_game["mode"]))
            _mode = _loaded_game["mode"]
            self.handler.log(message="Loaded game mode {}.".format(str(_mode["mode"])))

            _game = GameController(
                game_json=json.dumps(_loaded_game),
                mode="{}".format(_mode["mode"])
            )
            self.handler.log(message='Loaded game {}'.format(_key), status=0)
        except RuntimeError as ve:
            return self.handler.error(
                status=500,
                exception=str(ve),
                message="Bad request. For some reason the json_dict was None!"
            )
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain a valid game key."
            )
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="The game key provided was invalid."
            )
        except Exception as e:
            return self.handler.error(
                status=500,
                exception=repr(e),
                message="Exception occurred while loading game!"
            )

        #
        # Get the digits being guessed and add them to a list
        #
        try:
            self.handler.log(message='Getting digits from {}'.format(json_dict))
            _guesses = json_dict["digits"]
            self.handler.log(message='Guesses extracted from JSON: {}'.format(_guesses), status=0)
        except RuntimeError as ve:
            return self.handler.error(
                status=500,
                exception=str(ve),
                message="Bad request. For some reason the json_dict was None!"
            )
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception=str(ve),
                message="There was a problem with the value of the digits provided!"
            )
        except KeyError as ke:
            return self.handler.error(
                status=400,
                exception=str(ke),
                message="The request must contain an array of digits called 'digits'"
            )
        except TypeError as te:
            return self.handler.error(
                status=400,
                exception=str(te),
                message="The game key provided was invalid."
            )

        #
        # Make a guess
        #
        try:
            self.handler.log(message="In guess")
            self.handler.log(message='Making a guess with digits: {}'.format(_guesses), status=0)
            _analysis = _game.guess(*_guesses)
            self.handler.log(message='Retrieved guess analysis', status=0)
        except ValueError as ve:
            return self.handler.error(
                status=400,
                exception=str(ve),
                message="There is a problem with the digits provided!"
            )

        #
        # Save the game
        #
        self.handler.log(message='Update game (save) after guess', status=0)
        save_game = _game.save()
        persister.save(key=_key, jsonstr=save_game)

        #
        # Return the analysis of the guess to the user.
        #
        _display_info = json.loads(save_game)
        del(_display_info["answer"])
        _return_response = \
            {
                "game": _display_info,
                "outcome": _analysis,
                "served-by": socket.gethostname()
            }

        self.handler.log(message='Returning analysis and game info to caller', status=0)
        return build_response(response_data=_return_response)
<|endoftext|>


<commit_before># This module contains built-in introspector plugins for various common
# Django apps.

# These imports trigger the lower-down files
import south.introspection_plugins.geodjango
import south.introspection_plugins.django_tagging
import south.introspection_plugins.django_taggit
<commit_msg>Enable objectpermissions plugin<commit_after># This module contains built-in introspector plugins for various common
# Django apps.

# These imports trigger the lower-down files
import south.introspection_plugins.geodjango
import south.introspection_plugins.django_tagging
import south.introspection_plugins.django_taggit
import south.introspection_plugins.django_objectpermissions

<|endoftext|>


<commit_before># Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# File usage:
# python3 telemetry.py session project event version

# from google.cloud import pubsub_v1
from google.cloud import storage, exceptions
#from google.api_core import exceptions
from datetime import datetime
import sys
import json
import hashlib
import uuid
import click

def get_uuid():
    return uuid.uuid4()

# define the following helper functions
def get_datetime_str():
    # send current date and time for end of installation script
    now = datetime.utcnow()
    data = now.strftime("%m/%d/%Y %H:%M:%S")
    return data

def get_id_hash(project_id):
    m = hashlib.sha256()
    m.update(project_id.encode('utf-8'))
    hashed = m.hexdigest()
    return hashed

def get_telemetry_file(session, project_id, event, version):
    datetime=get_datetime_str()
    project=get_id_hash(project_id)

    # send in json format
    data = {
        "session": session,
        "project": project,
        "event": event,
        "datetime": datetime,
        "version": version
    }

    # file name should be descriptive but unique
    file_name = 'telemetry-' + project + "-" + str(get_uuid())
    with open(file_name, 'w') as file:
        json.dump(data, file)

    return file_name

@click.command()
@click.option('--session', help='Current session (unique across projects).')
@click.option('--bucket_name', help='Name of the bucket provisioned in user project for telemetry storage')
@click.option('--project_id', help='Project name in Google Cloud Platform.')
@click.option('--event', help='The  event that occurred.')
@click.option('--version', default="v0.2.5", help='Release version of Sandbox.')
def store_message(session, bucket_name, project_id, event, version):
    file_name = get_telemetry_file(session, project_id, event, version)

    # create bucket if none created yet
    storage_client = storage.Client()
    bucket = None
    try:
        bucket = storage_client.get_bucket(bucket_name)
    except exceptions.NotFound:
        bucket = storage_client.create_bucket(bucket_name)
        print("Bucket {} created.".format(bucket_name))

    blob = bucket.blob(file_name)
    print(blob)
    blob.upload_from_filename(file_name)

    print(
        "File {} uploaded to {}.".format(
            file_name, file_name
        )
    )

if __name__ == "__main__":
    store_message()
<commit_msg>Added logic to upload to bucket<commit_after># Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# File usage:
# python3 telemetry.py session project event version

# from google.cloud import pubsub_v1
from google.cloud import storage, exceptions
#from google.api_core import exceptions
from datetime import datetime
import sys
import json
import hashlib
import uuid
import click

def get_uuid():
    return uuid.uuid4()

# define the following helper functions
def get_datetime_str():
    # send current date and time for end of installation script
    now = datetime.utcnow()
    data = now.strftime("%m/%d/%Y %H:%M:%S")
    return data

def get_id_hash(project_id):
    m = hashlib.sha256()
    m.update(project_id.encode('utf-8'))
    hashed = m.hexdigest()
    return hashed

def get_telemetry_msg(session, project_id, event, version):
    datetime=get_datetime_str()
    project=get_id_hash(project_id)

    # send in json format
    data = {
        "session": session,
        "project": project,
        "event": event,
        "datetime": datetime,
        "version": version
    }
    msg = json.dumps(data)
    # file name should be descriptive but unique
    file_name = 'telemetry-' + project_id + "-" + str(get_uuid())
    return msg, file_name

@click.command()
@click.option('--session', help='Current session (unique across projects).')
@click.option('--bucket_name', help='Name of the bucket provisioned in user project for telemetry storage')
@click.option('--project_id', help='Project name in Google Cloud Platform.')
@click.option('--event', help='The  event that occurred.')
@click.option('--version', default="v0.2.5", help='Release version of Sandbox.')
def store_message(session, bucket_name, project_id, event, version):
    msg, file_name = get_telemetry_msg(session, project_id, event, version)

    # create bucket if none created yet
    storage_client = storage.Client()
    bucket = None
    try:
        bucket = storage_client.get_bucket(bucket_name)
    except exceptions.NotFound:
        bucket = storage_client.create_bucket(bucket_name)
        print("Bucket {} created.".format(bucket_name))

    blob = bucket.blob(file_name)
    print(blob)
    blob.upload_from_string(msg)

    print(
        "File {} uploaded to {}.".format(
            file_name, file_name
        )
    )

if __name__ == "__main__":
    store_message()
<|endoftext|>