<commit_before>from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import time
import uuid

from functools import wraps
from memoized import memoized
import itertools

from dimagi.utils.logging import notify_exception


class NestableTimer(object):
    """Timer object that can be nested. Used by ``TimingContext``.
    """
    def __init__(self, name, is_root=True):
        self.name = name
        self.beginning = None
        self.end = None
        self.subs = []
        self.root = self if is_root else None
        self.parent = None
        self.uuid = uuid.uuid4()

    def init(self, root, parent):
        self.root = root
        self.parent = parent

    def start(self):
        self.beginning = time.time()

    def stop(self):
        self.end = time.time()

    def append(self, timer):
        timer.init(self.root, self)
        self.subs.append(timer)

    @property
    def duration(self):
        """Get timer duration in seconds"""
        if self.beginning and self.end:
            return self.end - self.beginning
        elif self.beginning:
            return time.time() - self.beginning

    @property
    def percent_of_total(self):
        if self.duration and self.root and self.root.duration:
            return self.duration / self.root.duration * 100
        else:
            return None

    @property
    def percent_of_parent(self):
        if self.duration and self.parent and self.parent.duration:
            return self.duration / self.parent.duration * 100
        else:
            return None

    def to_dict(self):
        return {
            'name': self.name,
            'duration': self.duration,
            'percent_total': self.percent_of_total,
            'percent_parent': self.percent_of_parent,
            'subs': [sub.to_dict() for sub in self.subs]
        }

    def to_list(self, exclude_root=False):
        root = [] if exclude_root else [self]
        return root + list(itertools.chain(*[sub.to_list() for sub in self.subs]))

    @property
    def is_leaf_node(self):
        return not self.subs

    @property
    def is_root_node(self):
        return not self.parent

    @property
    @memoized
    def full_name(self):<commit_msg>Add beginning and end to NestableTimer dict with no duration<commit_after>from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import time
import uuid

from functools import wraps
from memoized import memoized
import itertools

from dimagi.utils.logging import notify_exception


class NestableTimer(object):
    """Timer object that can be nested. Used by ``TimingContext``.
    """
    def __init__(self, name, is_root=True):
        self.name = name
        self.beginning = None
        self.end = None
        self.subs = []
        self.root = self if is_root else None
        self.parent = None
        self.uuid = uuid.uuid4()

    def init(self, root, parent):
        self.root = root
        self.parent = parent

    def start(self):
        self.beginning = time.time()

    def stop(self):
        self.end = time.time()

    def append(self, timer):
        timer.init(self.root, self)
        self.subs.append(timer)

    @property
    def duration(self):
        """Get timer duration in seconds"""
        if self.beginning and self.end:
            return self.end - self.beginning
        elif self.beginning:
            return time.time() - self.beginning

    @property
    def percent_of_total(self):
        if self.duration and self.root and self.root.duration:
            return self.duration / self.root.duration * 100
        else:
            return None

    @property
    def percent_of_parent(self):
        if self.duration and self.parent and self.parent.duration:
            return self.duration / self.parent.duration * 100
        else:
            return None

    def to_dict(self):
        timer_dict = {
            'name': self.name,
            'duration': self.duration,
            'percent_total': self.percent_of_total,
            'percent_parent': self.percent_of_parent,
            'subs': [sub.to_dict() for sub in self.subs]
        }
        if not self.duration:
            timer_dict.update({
                'beginning': self.beginning,
                'end': self.end
            })
        return timer_dict

    def to_list(self, exclude_root=False):
        root = [] if exclude_root else [self]
        return root + list(itertools.chain(*[sub.to_list() for sub in self.subs]))

    @property
    def is_leaf_node(self):
        return not self.subs

    @property
    def is_root_node(self):
        return not self.parent

    @property
    @memoized
    def full_name(self):
{'fraction_non_alphanumeric': 0.09075665478935234, 'fraction_numerical': 0.002934395304967512, 'mean_word_length': 2.1477572559366753, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2316518', 'n_tokens_mistral': 1396, 'n_tokens_neox': 1354, 'n_words': 386}