# Path: src/formpack/schema/datadef.py
# class FormDataDef:
#     """
#     Any object composing a form. It's only used with a subclass.
#     """
# 
#     def __init__(self, name, labels=None, has_stats=False, *args, **kwargs):
#         self.name = name
#         self.labels = labels or {}
#         self.has_stats = has_stats
# 
#     def __repr__(self):
#         return "<%s name='%s'>" % (self.__class__.__name__, self.name)
# 
#     def get_value_names(self):
#         return [self.name]
# 
#     @classmethod
#     def from_json_definition(cls, definition, translations=None):
#         labels = cls._extract_json_labels(definition, translations)
#         return cls(definition['name'], labels)
# 
#     @classmethod
#     def _extract_json_labels(cls, definition, translations):
#         """
#         Extract translation labels from the JSON data definition
#         """
#         label = definition.get('label')
#         if label:
#             labels = OrderedDict(zip(translations, label))
#         else:
#             labels = {}
#         return labels
# 
# class FormChoice(FormDataDef):
#     def __init__(self, name, *args, **kwargs):
#         super().__init__(name, *args, **kwargs)
#         self.name = name
#         self.options = OrderedDict()
# 
#     @classmethod
#     def all_from_json_definition(cls, definition, translation_list):
#         all_choices = {}
#         for choice_definition in definition:
#             choice_name = choice_definition.get('name')
#             choice_key = choice_definition.get('list_name')
#             if not choice_name or not choice_key:
#                 continue
# 
#             if choice_key not in all_choices:
#                 all_choices[choice_key] = FormChoice(choice_key)
#             choices = all_choices[choice_key]
# 
#             option = choices.options[choice_name] = {}
# 
#             # apparently choices dont need a label if they have an image
#             if 'label' in choice_definition:
#                 _label = choice_definition['label']
#             else:
#                 _label = choice_definition.get('image')
#             if isinstance(_label, str):
#                 _label = [_label]
#             elif _label is None:
#                 _label = []
#             option['labels'] = OrderedDict(zip(translation_list, _label))
#             option['name'] = choice_name
#         return all_choices
# 
#     @property
#     def translations(self):
#         for option in self.options.values():
#             for translation in option['labels'].keys():
#                 yield translation
# 
# Path: src/formpack/constants.py
# UNSPECIFIED_TRANSLATION = False
# 
# Path: src/formpack/utils/statistics.py
# def singlemode(data):
#     try:
#         # New in Python 3.8
#         modes = statistics.multimode(data)
#     except AttributeError:
#         return statistics.mode(data)
#     else:
#         if len(modes) > 1:
#             raise statistics.StatisticsError('no unique mode')
#         else:
#             return modes[0]
# 
# Path: src/formpack/utils/ordered_collection.py
# class OrderedDefaultdict(OrderedDict):
#     """
#     defaultdict that keeps insertion order compatible with Python 2.7+
# 
#     Source: https://stackoverflow.com/a/4127426
#     """
# 
#     def __init__(self, default_factory=None, *args, **kwargs):
#         if not (
#             default_factory is None or isinstance(default_factory, Callable)
#         ):
#             raise TypeError('first argument must be callable or None')
#         super().__init__(*args, **kwargs)
#         self.default_factory = default_factory  # called by __missing__()
# 
#     def __missing__(self, key):
#         if self.default_factory is None:
#             raise KeyError(key)
#         self[key] = value = self.default_factory()
#         return value
# 
#     def __reduce__(self):  # optional, for pickle support
#         args = (self.default_factory,) if self.default_factory else tuple()
#         return self.__class__, args, None, None, iter(self.items())
# 
#     def __repr__(self):  # optional
#         return '%s(%r, %r)' % (
#             self.__class__.__name__,
#             self.default_factory,
#             list(iter(self.items())),
#         )


import math
import statistics
from collections import defaultdict, OrderedDict
from dateutil import parser
from functools import partial
from operator import itemgetter
from .datadef import FormDataDef, FormChoice
from ..constants import UNSPECIFIED_TRANSLATION
from ..utils import singlemode
from ..utils.ordered_collection import OrderedDefaultdict
        substats = OrderedDict()

        # transpose the metrics data structure to look like
        # {splitter1: [x, y, z], splitter2...}}
        inversed_metrics = OrderedDefaultdict(list)

        for val, counter in metrics.items():
            if val is None:
                continue
            for splitter, count in counter.items():
                inversed_metrics[splitter].extend([val] * count)

        for splitter, values in inversed_metrics.items():

            val_stats = substats[splitter] = {
                'median': '*',
                'mean': '*',
                'mode': '*',
                'stdev': '*',
            }

            try:
                # require a non empty dataset
                val_stats['mean'] = statistics.mean(values)
                val_stats['median'] = statistics.median(values)
                # requires at least 2 values in the dataset
                val_stats['stdev'] = statistics.stdev(
                    values, xbar=val_stats['mean']
                )
                # requires a non empty dataset and a unique mode

# Path: src/formpack/schema/datadef.py
# class FormDataDef:
#     """
#     Any object composing a form. It's only used with a subclass.
#     """
# 
#     def __init__(self, name, labels=None, has_stats=False, *args, **kwargs):
#         self.name = name
#         self.labels = labels or {}
#         self.has_stats = has_stats
# 
#     def __repr__(self):
#         return "<%s name='%s'>" % (self.__class__.__name__, self.name)
# 
#     def get_value_names(self):
#         return [self.name]
# 
#     @classmethod
#     def from_json_definition(cls, definition, translations=None):
#         labels = cls._extract_json_labels(definition, translations)
#         return cls(definition['name'], labels)
# 
#     @classmethod
#     def _extract_json_labels(cls, definition, translations):
#         """
#         Extract translation labels from the JSON data definition
#         """
#         label = definition.get('label')
#         if label:
#             labels = OrderedDict(zip(translations, label))
#         else:
#             labels = {}
#         return labels
# 
# class FormChoice(FormDataDef):
#     def __init__(self, name, *args, **kwargs):
#         super().__init__(name, *args, **kwargs)
#         self.name = name
#         self.options = OrderedDict()
# 
#     @classmethod
#     def all_from_json_definition(cls, definition, translation_list):
#         all_choices = {}
#         for choice_definition in definition:
#             choice_name = choice_definition.get('name')
#             choice_key = choice_definition.get('list_name')
#             if not choice_name or not choice_key:
#                 continue
# 
#             if choice_key not in all_choices:
#                 all_choices[choice_key] = FormChoice(choice_key)
#             choices = all_choices[choice_key]
# 
#             option = choices.options[choice_name] = {}
# 
#             # apparently choices dont need a label if they have an image
#             if 'label' in choice_definition:
#                 _label = choice_definition['label']
#             else:
#                 _label = choice_definition.get('image')
#             if isinstance(_label, str):
#                 _label = [_label]
#             elif _label is None:
#                 _label = []
#             option['labels'] = OrderedDict(zip(translation_list, _label))
#             option['name'] = choice_name
#         return all_choices
# 
#     @property
#     def translations(self):
#         for option in self.options.values():
#             for translation in option['labels'].keys():
#                 yield translation
# 
# Path: src/formpack/constants.py
# UNSPECIFIED_TRANSLATION = False
# 
# Path: src/formpack/utils/statistics.py
# def singlemode(data):
#     try:
#         # New in Python 3.8
#         modes = statistics.multimode(data)
#     except AttributeError:
#         return statistics.mode(data)
#     else:
#         if len(modes) > 1:
#             raise statistics.StatisticsError('no unique mode')
#         else:
#             return modes[0]
# 
# Path: src/formpack/utils/ordered_collection.py
# class OrderedDefaultdict(OrderedDict):
#     """
#     defaultdict that keeps insertion order compatible with Python 2.7+
# 
#     Source: https://stackoverflow.com/a/4127426
#     """
# 
#     def __init__(self, default_factory=None, *args, **kwargs):
#         if not (
#             default_factory is None or isinstance(default_factory, Callable)
#         ):
#             raise TypeError('first argument must be callable or None')
#         super().__init__(*args, **kwargs)
#         self.default_factory = default_factory  # called by __missing__()
# 
#     def __missing__(self, key):
#         if self.default_factory is None:
#             raise KeyError(key)
#         self[key] = value = self.default_factory()
#         return value
# 
#     def __reduce__(self):  # optional, for pickle support
#         args = (self.default_factory,) if self.default_factory else tuple()
#         return self.__class__, args, None, None, iter(self.items())
# 
#     def __repr__(self):  # optional
#         return '%s(%r, %r)' % (
#             self.__class__.__name__,
#             self.default_factory,
#             list(iter(self.items())),
#         )


# Path: src/formpack/schema/fields.py
import math
import statistics
from collections import defaultdict, OrderedDict
from dateutil import parser
from functools import partial
from operator import itemgetter
from .datadef import FormDataDef, FormChoice
from ..constants import UNSPECIFIED_TRANSLATION
from ..utils import singlemode
from ..utils.ordered_collection import OrderedDefaultdict

        substats = OrderedDict()

        # transpose the metrics data structure to look like
        # {splitter1: [x, y, z], splitter2...}}
        inversed_metrics = OrderedDefaultdict(list)

        for val, counter in metrics.items():
            if val is None:
                continue
            for splitter, count in counter.items():
                inversed_metrics[splitter].extend([val] * count)

        for splitter, values in inversed_metrics.items():

            val_stats = substats[splitter] = {
                'median': '*',
                'mean': '*',
                'mode': '*',
                'stdev': '*',
            }

            try:
                # require a non empty dataset
                val_stats['mean'] = statistics.mean(values)
                val_stats['median'] = statistics.median(values)
                # requires at least 2 values in the dataset
                val_stats['stdev'] = statistics.stdev(
                    values, xbar=val_stats['mean']
                )
                # requires a non empty dataset and a unique mode

                val_stats['mode'] = singlemode(values)