cengal.time_management.relative_time.approximate_representation.versions.v_1.approximate_representation
1#!/usr/bin/env python 2# coding=utf-8 3 4# Copyright © 2012-2024 ButenkoMS. All rights reserved. Contacts: <gtalk@butenkoms.space> 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17 18from typing import Union, Set, Tuple, Dict 19from math import modf 20from enum import Enum 21from copy import copy 22from cengal.data_manipulation.conversion.mapping import inverse_mapping 23from cengal.text_processing.optional_formatter.versions.v_2 import OptionalFormatter, IT, AK 24from ....constants import * 25 26""" 27Module Docstring 28Docstrings: http://www.python.org/dev/peps/pep-0257/ 29""" 30 31__author__ = "ButenkoMS <gtalk@butenkoms.space>" 32__copyright__ = "Copyright © 2012-2024 ButenkoMS. All rights reserved. Contacts: <gtalk@butenkoms.space>" 33__credits__ = ["ButenkoMS <gtalk@butenkoms.space>", ] 34__license__ = "Apache License, Version 2.0" 35__version__ = "4.4.1" 36__maintainer__ = "ButenkoMS <gtalk@butenkoms.space>" 37__email__ = "gtalk@butenkoms.space" 38# __status__ = "Prototype" 39__status__ = "Development" 40# __status__ = "Production" 41 42 43SECONDS_PER_YEAR = SECONDS_PER_DAY * 365 44SECONDS_PER_MONTH = round(SECONDS_PER_YEAR / 12) 45SECONDS_PER_DECADE = SECONDS_PER_YEAR * 10 46SECONDS_PER_CENTURY = SECONDS_PER_YEAR * 100 47SECONDS_PER_MILLENIA = SECONDS_PER_YEAR * 1000 48 49 50class TimeAttributes(Enum): 51 microseconds = 0 52 milliseconds = 1 53 seconds = 2 54 minutes = 3 55 hours = 4 56 days = 5 57 weeks = 6 58 months = 7 59 years = 8 60 decades = 9 61 centuries = 10 62 millennia = 11 63 64 65ATTRIBUTES_ASCENDING = sorted(TimeAttributes, key=lambda item: item.value, reverse=False) 66ATTRIBUTES_DESCENDING = sorted(TimeAttributes, key=lambda item: item.value, reverse=True) 67 68 69DIVIDER_PER_ATTRIBUTE = { 70 TimeAttributes.millennia: SECONDS_PER_MILLENIA, 71 TimeAttributes.centuries: SECONDS_PER_CENTURY, 72 TimeAttributes.decades: SECONDS_PER_DECADE, 73 TimeAttributes.years: SECONDS_PER_YEAR, 74 TimeAttributes.months: SECONDS_PER_MONTH, 75 TimeAttributes.weeks: SECONDS_PER_WEEK, 76 TimeAttributes.days: SECONDS_PER_DAY, 77 TimeAttributes.hours: SECONDS_PER_HOUR, 78 TimeAttributes.minutes: SECONDS_PER_MINUTE 79} 80 81ATTRIBUTE_PER_DIVIDER = inverse_mapping(DIVIDER_PER_ATTRIBUTE) 82 83DIV_ATTRIBUTES = set(DIVIDER_PER_ATTRIBUTE) 84DIV_ATTRIBUTES_ASCENDING = sorted(DIV_ATTRIBUTES, key=lambda item: item.value, reverse=False) 85DIV_ATTRIBUTES_DESCENDING = sorted(DIV_ATTRIBUTES, key=lambda item: item.value, reverse=True) 86 87 88MULTIPLIER_PER_ATTRIBUTE = { 89 TimeAttributes.milliseconds: 1 / SECONDS_PER_MILLISECOND, 90 TimeAttributes.microseconds: 1 / SECONDS_PER_MICROSECOND 91} 92 93ATTRIBUTE_PER_MULTIPLIER = inverse_mapping(MULTIPLIER_PER_ATTRIBUTE) 94 95MUL_ATTRIBUTES = set(MULTIPLIER_PER_ATTRIBUTE) 96MUL_ATTRIBUTES_ASCENDING = sorted(MUL_ATTRIBUTES, key=lambda item: item.value, reverse=False) 97MUL_ATTRIBUTES_DESCENDING = sorted(MUL_ATTRIBUTES, key=lambda item: item.value, reverse=True) 98 99 100DEFAULT_ATTRIBUTES = { 101 TimeAttributes.years 102 , TimeAttributes.months 103 , TimeAttributes.days 104 , TimeAttributes.hours 105 , TimeAttributes.minutes 106 , TimeAttributes.seconds 107 , TimeAttributes.microseconds 108} 109 110DEFAULT_ATTRIBUTES_ASCENDING = sorted(DEFAULT_ATTRIBUTES, key=lambda item: item.value, reverse=False) 111DEFAULT_ATTRIBUTES_DESCENDING = sorted(DEFAULT_ATTRIBUTES, key=lambda item: item.value, reverse=True) 112 113 114DEFAULT_FORMATTER = OptionalFormatter(tuple(DEFAULT_ATTRIBUTES_DESCENDING), { 115 TimeAttributes.years: ('', 'Y', '{}', '', '-'), 116 TimeAttributes.months: ('', 'M', '{}', '', '-'), 117 TimeAttributes.days: ('', 'D', '{}', '', ' '), 118 TimeAttributes.hours: ('', 'h', '{}', '', ''), 119 TimeAttributes.minutes: (':', 'm', '{}', '', ''), 120 TimeAttributes.seconds: (':', 's', '{}', '', ''), 121 TimeAttributes.microseconds: ('.', 'μs', '{0:0>6}', '', '') 122}) 123 124FULL_FORMATTER = OptionalFormatter(tuple(ATTRIBUTES_DESCENDING), { 125 TimeAttributes.millennia: ('', '', 'Millennia: {}', '', ', '), 126 TimeAttributes.centuries: ('', '', 'Centuries: {}', '', ', '), 127 TimeAttributes.decades: ('', '', 'Decades: {}', '', ', '), 128 TimeAttributes.years: ('', '', 'Years: {}', '', ', '), 129 TimeAttributes.months: ('', '', 'Months: {}', '', ', '), 130 TimeAttributes.weeks: ('', '', 'Weeks: {}', '', ', '), 131 TimeAttributes.days: ('', '', 'Days: {}', '', ', '), 132 TimeAttributes.hours: ('', '', 'Hours: {}', '', ', '), 133 TimeAttributes.minutes: ('', '', 'Minutes: {}', '', ', '), 134 TimeAttributes.seconds: ('', '', 'Seconds: {}', '', ', '), 135 TimeAttributes.milliseconds: ('', '', 'Milliseconds: {0:0>3}', '', ', '), 136 TimeAttributes.microseconds: ('', '', 'Microseconds: {0:0>6}', '', ', '), 137}) 138 139 140class ApproximateTimeRepresentation: 141 def __init__(self, seconds: Union[int, float], attributes: Union[Set[TimeAttributes], None]=None, crop: bool=False): 142 self._initial__seconds = seconds 143 self._initial__attributes = attributes or DEFAULT_ATTRIBUTES 144 self._initial__crop = crop 145 146 self.values = dict() 147 148 rest = self._initial__seconds 149 for attribute in DIV_ATTRIBUTES_DESCENDING: 150 self.values[attribute], rest = self.compute_attribute(rest, attribute) 151 152 rest, seconds = modf(rest) 153 seconds = int(seconds) 154 self.values[TimeAttributes.seconds] = seconds 155 156 for attribute in MUL_ATTRIBUTES_DESCENDING: 157 self.values[attribute], rest = self.compute_fractional_attribute(rest, attribute) 158 159 self.cropped_values = copy(self.values) 160 self._normalize() 161 if self._initial__crop: 162 self._crop() 163 164 def compute_attribute(self, seconds: float, 165 attribute: TimeAttributes) -> Tuple[float, float]: 166 if attribute in self._initial__attributes: 167 result, rest = divmod(seconds, DIVIDER_PER_ATTRIBUTE[attribute]) 168 result = int(result) 169 return result, rest 170 else: 171 return 0, seconds 172 173 def compute_fractional_attribute(self, seconds: float, 174 attribute: TimeAttributes) -> Tuple[float, float]: 175 if attribute in self._initial__attributes: 176 multiplier = MULTIPLIER_PER_ATTRIBUTE[attribute] 177 rest, integer_value = modf(seconds * multiplier) 178 integer_value = int(integer_value) 179 rest /= multiplier 180 return integer_value, rest 181 else: 182 return 0, seconds 183 184 def _normalize(self): 185 for attribute in TimeAttributes: 186 if attribute not in self._initial__attributes: 187 if attribute in self.cropped_values: 188 del self.cropped_values[attribute] 189 190 def _crop(self): 191 descending_stopped = False 192 ascending_stopped = False 193 194 for attribute in ATTRIBUTES_DESCENDING: 195 descending_stopped = self._crop__check_attribute(self.values[attribute], attribute, descending_stopped) 196 197 for attribute in ATTRIBUTES_ASCENDING: 198 ascending_stopped = self._crop__check_attribute(self.values[attribute], attribute, ascending_stopped) 199 200 def _crop__check_attribute(self, value: Union[int, float], attribute: TimeAttributes, stopped: bool) -> bool: 201 if stopped or (0 != value): 202 return True 203 else: 204 if attribute in self.cropped_values: 205 del self.cropped_values[attribute] 206 return False 207 208 def format(self, formatter: Union[OptionalFormatter, None]=None) -> str: 209 formatter = formatter or DEFAULT_FORMATTER 210 args = tuple() 211 return formatter(self._construct_formatter_dict(self.values)) 212 213 def format_cropped(self, formatter: Union[OptionalFormatter, None]=None) -> str: 214 formatter = formatter or DEFAULT_FORMATTER 215 return formatter(self._construct_formatter_dict(self.cropped_values)) 216 217 def __str__(self): 218 formatter = FULL_FORMATTER 219 return '{class_name}{{{data}}}'.format(class_name=self.__class__.__name__, 220 data=formatter(self._construct_formatter_dict(self.values))) 221 222 @staticmethod 223 def _construct_formatter_dict(values: Dict[TimeAttributes, Union[int, float]]): 224 result = dict() 225 for item, value in values.items(): 226 result[item] = value 227 return result
SECONDS_PER_YEAR =
525600
SECONDS_PER_MONTH =
43800
SECONDS_PER_DECADE =
5256000
SECONDS_PER_CENTURY =
52560000
SECONDS_PER_MILLENIA =
525600000
class
TimeAttributes(enum.Enum):
51class TimeAttributes(Enum): 52 microseconds = 0 53 milliseconds = 1 54 seconds = 2 55 minutes = 3 56 hours = 4 57 days = 5 58 weeks = 6 59 months = 7 60 years = 8 61 decades = 9 62 centuries = 10 63 millennia = 11
An enumeration.
microseconds =
<TimeAttributes.microseconds: 0>
milliseconds =
<TimeAttributes.milliseconds: 1>
seconds =
<TimeAttributes.seconds: 2>
minutes =
<TimeAttributes.minutes: 3>
hours =
<TimeAttributes.hours: 4>
days =
<TimeAttributes.days: 5>
weeks =
<TimeAttributes.weeks: 6>
months =
<TimeAttributes.months: 7>
years =
<TimeAttributes.years: 8>
decades =
<TimeAttributes.decades: 9>
centuries =
<TimeAttributes.centuries: 10>
millennia =
<TimeAttributes.millennia: 11>
Inherited Members
- enum.Enum
- name
- value
ATTRIBUTES_ASCENDING =
[<TimeAttributes.microseconds: 0>, <TimeAttributes.milliseconds: 1>, <TimeAttributes.seconds: 2>, <TimeAttributes.minutes: 3>, <TimeAttributes.hours: 4>, <TimeAttributes.days: 5>, <TimeAttributes.weeks: 6>, <TimeAttributes.months: 7>, <TimeAttributes.years: 8>, <TimeAttributes.decades: 9>, <TimeAttributes.centuries: 10>, <TimeAttributes.millennia: 11>]
ATTRIBUTES_DESCENDING =
[<TimeAttributes.millennia: 11>, <TimeAttributes.centuries: 10>, <TimeAttributes.decades: 9>, <TimeAttributes.years: 8>, <TimeAttributes.months: 7>, <TimeAttributes.weeks: 6>, <TimeAttributes.days: 5>, <TimeAttributes.hours: 4>, <TimeAttributes.minutes: 3>, <TimeAttributes.seconds: 2>, <TimeAttributes.milliseconds: 1>, <TimeAttributes.microseconds: 0>]
DIVIDER_PER_ATTRIBUTE =
{<TimeAttributes.millennia: 11>: 525600000, <TimeAttributes.centuries: 10>: 52560000, <TimeAttributes.decades: 9>: 5256000, <TimeAttributes.years: 8>: 525600, <TimeAttributes.months: 7>: 43800, <TimeAttributes.weeks: 6>: 10080, <TimeAttributes.days: 5>: 1440, <TimeAttributes.hours: 4>: 3600, <TimeAttributes.minutes: 3>: 60}
ATTRIBUTE_PER_DIVIDER =
{525600000: <TimeAttributes.millennia: 11>, 52560000: <TimeAttributes.centuries: 10>, 5256000: <TimeAttributes.decades: 9>, 525600: <TimeAttributes.years: 8>, 43800: <TimeAttributes.months: 7>, 10080: <TimeAttributes.weeks: 6>, 1440: <TimeAttributes.days: 5>, 3600: <TimeAttributes.hours: 4>, 60: <TimeAttributes.minutes: 3>}
DIV_ATTRIBUTES =
{<TimeAttributes.decades: 9>, <TimeAttributes.days: 5>, <TimeAttributes.months: 7>, <TimeAttributes.minutes: 3>, <TimeAttributes.centuries: 10>, <TimeAttributes.years: 8>, <TimeAttributes.hours: 4>, <TimeAttributes.weeks: 6>, <TimeAttributes.millennia: 11>}
DIV_ATTRIBUTES_ASCENDING =
[<TimeAttributes.minutes: 3>, <TimeAttributes.hours: 4>, <TimeAttributes.days: 5>, <TimeAttributes.weeks: 6>, <TimeAttributes.months: 7>, <TimeAttributes.years: 8>, <TimeAttributes.decades: 9>, <TimeAttributes.centuries: 10>, <TimeAttributes.millennia: 11>]
DIV_ATTRIBUTES_DESCENDING =
[<TimeAttributes.millennia: 11>, <TimeAttributes.centuries: 10>, <TimeAttributes.decades: 9>, <TimeAttributes.years: 8>, <TimeAttributes.months: 7>, <TimeAttributes.weeks: 6>, <TimeAttributes.days: 5>, <TimeAttributes.hours: 4>, <TimeAttributes.minutes: 3>]
MULTIPLIER_PER_ATTRIBUTE =
{<TimeAttributes.milliseconds: 1>: 1000.0, <TimeAttributes.microseconds: 0>: 1000000.0}
ATTRIBUTE_PER_MULTIPLIER =
{1000.0: <TimeAttributes.milliseconds: 1>, 1000000.0: <TimeAttributes.microseconds: 0>}
MUL_ATTRIBUTES =
{<TimeAttributes.microseconds: 0>, <TimeAttributes.milliseconds: 1>}
MUL_ATTRIBUTES_ASCENDING =
[<TimeAttributes.microseconds: 0>, <TimeAttributes.milliseconds: 1>]
MUL_ATTRIBUTES_DESCENDING =
[<TimeAttributes.milliseconds: 1>, <TimeAttributes.microseconds: 0>]
DEFAULT_ATTRIBUTES =
{<TimeAttributes.microseconds: 0>, <TimeAttributes.days: 5>, <TimeAttributes.months: 7>, <TimeAttributes.seconds: 2>, <TimeAttributes.minutes: 3>, <TimeAttributes.hours: 4>, <TimeAttributes.years: 8>}
DEFAULT_ATTRIBUTES_ASCENDING =
[<TimeAttributes.microseconds: 0>, <TimeAttributes.seconds: 2>, <TimeAttributes.minutes: 3>, <TimeAttributes.hours: 4>, <TimeAttributes.days: 5>, <TimeAttributes.months: 7>, <TimeAttributes.years: 8>]
DEFAULT_ATTRIBUTES_DESCENDING =
[<TimeAttributes.years: 8>, <TimeAttributes.months: 7>, <TimeAttributes.days: 5>, <TimeAttributes.hours: 4>, <TimeAttributes.minutes: 3>, <TimeAttributes.seconds: 2>, <TimeAttributes.microseconds: 0>]
DEFAULT_FORMATTER =
<cengal.text_processing.optional_formatter.versions.v_2.optional_formatter.OptionalFormatter object>
FULL_FORMATTER =
<cengal.text_processing.optional_formatter.versions.v_2.optional_formatter.OptionalFormatter object>
class
ApproximateTimeRepresentation:
141class ApproximateTimeRepresentation: 142 def __init__(self, seconds: Union[int, float], attributes: Union[Set[TimeAttributes], None]=None, crop: bool=False): 143 self._initial__seconds = seconds 144 self._initial__attributes = attributes or DEFAULT_ATTRIBUTES 145 self._initial__crop = crop 146 147 self.values = dict() 148 149 rest = self._initial__seconds 150 for attribute in DIV_ATTRIBUTES_DESCENDING: 151 self.values[attribute], rest = self.compute_attribute(rest, attribute) 152 153 rest, seconds = modf(rest) 154 seconds = int(seconds) 155 self.values[TimeAttributes.seconds] = seconds 156 157 for attribute in MUL_ATTRIBUTES_DESCENDING: 158 self.values[attribute], rest = self.compute_fractional_attribute(rest, attribute) 159 160 self.cropped_values = copy(self.values) 161 self._normalize() 162 if self._initial__crop: 163 self._crop() 164 165 def compute_attribute(self, seconds: float, 166 attribute: TimeAttributes) -> Tuple[float, float]: 167 if attribute in self._initial__attributes: 168 result, rest = divmod(seconds, DIVIDER_PER_ATTRIBUTE[attribute]) 169 result = int(result) 170 return result, rest 171 else: 172 return 0, seconds 173 174 def compute_fractional_attribute(self, seconds: float, 175 attribute: TimeAttributes) -> Tuple[float, float]: 176 if attribute in self._initial__attributes: 177 multiplier = MULTIPLIER_PER_ATTRIBUTE[attribute] 178 rest, integer_value = modf(seconds * multiplier) 179 integer_value = int(integer_value) 180 rest /= multiplier 181 return integer_value, rest 182 else: 183 return 0, seconds 184 185 def _normalize(self): 186 for attribute in TimeAttributes: 187 if attribute not in self._initial__attributes: 188 if attribute in self.cropped_values: 189 del self.cropped_values[attribute] 190 191 def _crop(self): 192 descending_stopped = False 193 ascending_stopped = False 194 195 for attribute in ATTRIBUTES_DESCENDING: 196 descending_stopped = self._crop__check_attribute(self.values[attribute], attribute, descending_stopped) 197 198 for attribute in ATTRIBUTES_ASCENDING: 199 ascending_stopped = self._crop__check_attribute(self.values[attribute], attribute, ascending_stopped) 200 201 def _crop__check_attribute(self, value: Union[int, float], attribute: TimeAttributes, stopped: bool) -> bool: 202 if stopped or (0 != value): 203 return True 204 else: 205 if attribute in self.cropped_values: 206 del self.cropped_values[attribute] 207 return False 208 209 def format(self, formatter: Union[OptionalFormatter, None]=None) -> str: 210 formatter = formatter or DEFAULT_FORMATTER 211 args = tuple() 212 return formatter(self._construct_formatter_dict(self.values)) 213 214 def format_cropped(self, formatter: Union[OptionalFormatter, None]=None) -> str: 215 formatter = formatter or DEFAULT_FORMATTER 216 return formatter(self._construct_formatter_dict(self.cropped_values)) 217 218 def __str__(self): 219 formatter = FULL_FORMATTER 220 return '{class_name}{{{data}}}'.format(class_name=self.__class__.__name__, 221 data=formatter(self._construct_formatter_dict(self.values))) 222 223 @staticmethod 224 def _construct_formatter_dict(values: Dict[TimeAttributes, Union[int, float]]): 225 result = dict() 226 for item, value in values.items(): 227 result[item] = value 228 return result
ApproximateTimeRepresentation( seconds: Union[int, float], attributes: Union[Set[TimeAttributes], NoneType] = None, crop: bool = False)
142 def __init__(self, seconds: Union[int, float], attributes: Union[Set[TimeAttributes], None]=None, crop: bool=False): 143 self._initial__seconds = seconds 144 self._initial__attributes = attributes or DEFAULT_ATTRIBUTES 145 self._initial__crop = crop 146 147 self.values = dict() 148 149 rest = self._initial__seconds 150 for attribute in DIV_ATTRIBUTES_DESCENDING: 151 self.values[attribute], rest = self.compute_attribute(rest, attribute) 152 153 rest, seconds = modf(rest) 154 seconds = int(seconds) 155 self.values[TimeAttributes.seconds] = seconds 156 157 for attribute in MUL_ATTRIBUTES_DESCENDING: 158 self.values[attribute], rest = self.compute_fractional_attribute(rest, attribute) 159 160 self.cropped_values = copy(self.values) 161 self._normalize() 162 if self._initial__crop: 163 self._crop()
def
compute_fractional_attribute( self, seconds: float, attribute: TimeAttributes) -> Tuple[float, float]:
174 def compute_fractional_attribute(self, seconds: float, 175 attribute: TimeAttributes) -> Tuple[float, float]: 176 if attribute in self._initial__attributes: 177 multiplier = MULTIPLIER_PER_ATTRIBUTE[attribute] 178 rest, integer_value = modf(seconds * multiplier) 179 integer_value = int(integer_value) 180 rest /= multiplier 181 return integer_value, rest 182 else: 183 return 0, seconds
def
format( self, formatter: Union[cengal.text_processing.optional_formatter.versions.v_2.optional_formatter.OptionalFormatter, NoneType] = None) -> str: