# Path: labella/scale.py
# class TimeScale(object):
#     def __init__(self, linear=None, methods=None, fmt=None):
#         self._linear = LinearScale() if linear is None else linear
#         self._methods = (
#             d3_time_scaleLocalMethods if methods is None else methods
#         )
#         # self._format = d3_time_scaleLocalFormat if fmt is None else fmt
#         self._format = mytimeformat if fmt is None else fmt
# 
#     def invert(self, x):
#         return milli2dt(self._linear.invert(x))
# 
#     def domain(self, x=None):
#         if x is None:
#             return list(map(milli2dt, self._linear.domain()))
#         num_domain = list(map(dt2milli, x))
#         self._linear.domain(num_domain)
#         return self
# 
#     def tickMethod(self, extent, count):
#         span = extent[1] - extent[0]
#         target = span / count
#         i = d3_bisect(d3_time_scaleSteps, target)
#         if i == len(d3_time_scaleSteps):
#             return [
#                 self._methods[-1][0],
#                 d3_scale_linearTickRange(
#                     list(map(lambda d: d / 31536e6, extent)), count
#                 )[2],
#             ]
#         if not i:
#             return [
#                 d3_time_scaleMilliseconds,
#                 d3_scale_linearTickRange(extent, count)[2],
#             ]
#         if target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target:
#             return self._methods[i - 1]
#         return self._methods[i]
# 
#     def nice(self, interval=None, skip=0):
#         domain = self.domain()
#         extent = d3_scaleExtent(domain)
#         extent = list(map(dt2milli, extent))
#         if interval is None:
#             method = self.tickMethod(extent, 10)
#         elif str(interval).isnumeric():
#             method = self.tickMethod(extent, interval)
#         else:
#             method = None
#         if method:
#             interval = method[0]
#             skip = method[1]
# 
#         def skipped(date):
#             return (not date is None) and (
#                 not len(
#                     interval.range(date, milli2dt(dt2milli(date) + 1), skip)
#                 )
#             )
# 
#         if skip > 1:
#             return self.domain(
#                 d3_scale_nice(
#                     domain,
#                     {
#                         "floor": lambda x: time_nice_floor(
#                             x, skipped, interval
#                         ),
#                         "ceil": lambda x: time_nice_ceil(x, skipped, interval),
#                     },
#                 )
#             )
#         else:
#             return self.domain(d3_scale_nice(domain, interval))
# 
#     def ticks(self, interval=None, skip=None):
#         extent = d3_scaleExtent(self.domain())
#         extent = list(map(lambda x: x.timestamp() * 1000, extent))
#         method = (
#             self.tickMethod(extent, 10)
#             if interval is None
#             else self.tickMethod(extent, interval)
#         )
#         if method:
#             interval = method[0]
#             skip = method[1]
# 
#         if skip < 1:
#             return interval.range(
#                 milli2dt(extent[0]), milli2dt(extent[1] + 1), 1
#             )
#         else:
#             return interval.range(
#                 milli2dt(extent[0]), milli2dt(extent[1] + 1), skip
#             )
# 
#     def tickFormat(self):
#         return self._format
# 
#     def range(self, x=None):
#         if x is None:
#             return self._linear.range()
#         self._linear.range(x)
#         return self
# 
#     def rangeRound(self, x):
#         pass
# 
#     def clamp(self, x=None):
#         if x is None:
#             return self._linear.clamp()
#         self._linear.clamp(x)
#         return self
# 
#     def interpolate(self, x=None):
#         if x is None:
#             return self._linear.interpolate()
#         self._linear.interpolate(x)
#         return self
# 
#     def copy(self):
#         return TimeScale(self._linear.copy(), self._methods, self._format)
# 
#     def __call__(self, x):
#         return self._linear(dt2milli(x))
# 
# Path: labella/d3_time.py
# class d3_time_interval:
#     def __init__(self, local, step, number):
#     def round(self, date):
#     def floor(self, date):
#     def ceil(self, date):
#     def offset(self, date, k):
#     def range(self, t0, t1, dt):
#     def __call__(self, date):
# def d3_time_hour_local(date):
# def d3_time_day_offset(date, offset):
# def day_of_year(date):
# def d3_time_week_local(date):
# def d3_time_week_number(date):
# def d3_time_month_local(date):
# def d3_time_month_offset(date, offset):
# def d3_time_year_local(date):


import unittest
from datetime import datetime
from labella.scale import TimeScale
from labella.d3_time import d3_time

"""
NOTE: To make writing the tests easier and in accordance with the original d3
tests they are copied from, the local function converts months from 0 based
indexing to Python's 1 based indexing.

"""




def local(year, month, day, hours=0, minutes=0, seconds=0, milliseconds=0):
    # helper function copied from d3/test/time/time.js
    date = datetime(year, month+1, day, hours, minutes, seconds, milliseconds
            * 1000)
    return date

class TimeScaleTestCase(unittest.TestCase):

    def test_nice_1c(self):
        # rounds using the specified time interval
        x = TimeScale().domain([local(2009, 0, 1, 0, 12), local(2009, 0, 1,
            23, 48)])
        self.assertEqual(x.nice(d3_time['day']).domain(), [local(2009, 0, 1),
            local(2009, 0, 2)])
        self.assertEqual(x.nice(d3_time['week']).domain(), [local(2008, 11,
            28), local(2009, 0, 4)])

# Path: labella/scale.py
# class TimeScale(object):
#     def __init__(self, linear=None, methods=None, fmt=None):
#         self._linear = LinearScale() if linear is None else linear
#         self._methods = (
#             d3_time_scaleLocalMethods if methods is None else methods
#         )
#         # self._format = d3_time_scaleLocalFormat if fmt is None else fmt
#         self._format = mytimeformat if fmt is None else fmt
# 
#     def invert(self, x):
#         return milli2dt(self._linear.invert(x))
# 
#     def domain(self, x=None):
#         if x is None:
#             return list(map(milli2dt, self._linear.domain()))
#         num_domain = list(map(dt2milli, x))
#         self._linear.domain(num_domain)
#         return self
# 
#     def tickMethod(self, extent, count):
#         span = extent[1] - extent[0]
#         target = span / count
#         i = d3_bisect(d3_time_scaleSteps, target)
#         if i == len(d3_time_scaleSteps):
#             return [
#                 self._methods[-1][0],
#                 d3_scale_linearTickRange(
#                     list(map(lambda d: d / 31536e6, extent)), count
#                 )[2],
#             ]
#         if not i:
#             return [
#                 d3_time_scaleMilliseconds,
#                 d3_scale_linearTickRange(extent, count)[2],
#             ]
#         if target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target:
#             return self._methods[i - 1]
#         return self._methods[i]
# 
#     def nice(self, interval=None, skip=0):
#         domain = self.domain()
#         extent = d3_scaleExtent(domain)
#         extent = list(map(dt2milli, extent))
#         if interval is None:
#             method = self.tickMethod(extent, 10)
#         elif str(interval).isnumeric():
#             method = self.tickMethod(extent, interval)
#         else:
#             method = None
#         if method:
#             interval = method[0]
#             skip = method[1]
# 
#         def skipped(date):
#             return (not date is None) and (
#                 not len(
#                     interval.range(date, milli2dt(dt2milli(date) + 1), skip)
#                 )
#             )
# 
#         if skip > 1:
#             return self.domain(
#                 d3_scale_nice(
#                     domain,
#                     {
#                         "floor": lambda x: time_nice_floor(
#                             x, skipped, interval
#                         ),
#                         "ceil": lambda x: time_nice_ceil(x, skipped, interval),
#                     },
#                 )
#             )
#         else:
#             return self.domain(d3_scale_nice(domain, interval))
# 
#     def ticks(self, interval=None, skip=None):
#         extent = d3_scaleExtent(self.domain())
#         extent = list(map(lambda x: x.timestamp() * 1000, extent))
#         method = (
#             self.tickMethod(extent, 10)
#             if interval is None
#             else self.tickMethod(extent, interval)
#         )
#         if method:
#             interval = method[0]
#             skip = method[1]
# 
#         if skip < 1:
#             return interval.range(
#                 milli2dt(extent[0]), milli2dt(extent[1] + 1), 1
#             )
#         else:
#             return interval.range(
#                 milli2dt(extent[0]), milli2dt(extent[1] + 1), skip
#             )
# 
#     def tickFormat(self):
#         return self._format
# 
#     def range(self, x=None):
#         if x is None:
#             return self._linear.range()
#         self._linear.range(x)
#         return self
# 
#     def rangeRound(self, x):
#         pass
# 
#     def clamp(self, x=None):
#         if x is None:
#             return self._linear.clamp()
#         self._linear.clamp(x)
#         return self
# 
#     def interpolate(self, x=None):
#         if x is None:
#             return self._linear.interpolate()
#         self._linear.interpolate(x)
#         return self
# 
#     def copy(self):
#         return TimeScale(self._linear.copy(), self._methods, self._format)
# 
#     def __call__(self, x):
#         return self._linear(dt2milli(x))
# 
# Path: labella/d3_time.py
# class d3_time_interval:
#     def __init__(self, local, step, number):
#     def round(self, date):
#     def floor(self, date):
#     def ceil(self, date):
#     def offset(self, date, k):
#     def range(self, t0, t1, dt):
#     def __call__(self, date):
# def d3_time_hour_local(date):
# def d3_time_day_offset(date, offset):
# def day_of_year(date):
# def d3_time_week_local(date):
# def d3_time_week_number(date):
# def d3_time_month_local(date):
# def d3_time_month_offset(date, offset):
# def d3_time_year_local(date):


# Path: tests/test_scale_time.py
import unittest
from datetime import datetime
from labella.scale import TimeScale
from labella.d3_time import d3_time


"""
NOTE: To make writing the tests easier and in accordance with the original d3
tests they are copied from, the local function converts months from 0 based
indexing to Python's 1 based indexing.

"""




def local(year, month, day, hours=0, minutes=0, seconds=0, milliseconds=0):
    # helper function copied from d3/test/time/time.js
    date = datetime(year, month+1, day, hours, minutes, seconds, milliseconds
            * 1000)
    return date

class TimeScaleTestCase(unittest.TestCase):

    def test_nice_1c(self):
        # rounds using the specified time interval
        x = TimeScale().domain([local(2009, 0, 1, 0, 12), local(2009, 0, 1,
            23, 48)])
        self.assertEqual(x.nice(d3_time['day']).domain(), [local(2009, 0, 1),
            local(2009, 0, 2)])
        self.assertEqual(x.nice(d3_time['week']).domain(), [local(2008, 11,
            28), local(2009, 0, 4)])

        self.assertEqual(x.nice(d3_time['month']).domain(), [local(2008, 11,