text: """Schedule Type Limit Schema"""
  from pydantic import Field, validator, root_validator, constr, conlist
  from typing import List, Union
  from enum import Enum
  import datetime
  
  from ._base import IDdEnergyBaseModel, DatedBaseModel
  from ..altnumber import NoLimit
  
  
  class ScheduleNumericType (str, Enum):
      """Designates how the range values are validated."""
      continuous = 'Continuous'
      discrete = 'Discrete'
  
  
  class ScheduleUnitType (str, Enum):
      dimensionless = 'Dimensionless'
      temperature = 'Temperature'
      delta_temperature = 'DeltaTemperature'
      precipitation_rate = 'PrecipitationRate'
      angle = 'Angle'
      convection_coefficient = 'ConvectionCoefficient'
      activity_level = 'ActivityLevel'
      velocity = 'Velocity'
      capacity = 'Capacity'
      power = 'Power'
      availability = 'Availability'
      percent = 'Percent'
      control = 'Control'
      mode = 'Mode'
  
  
  class ScheduleTypeLimit(IDdEnergyBaseModel):
      """Specifies the data types and limits for values contained in schedules."""
  
      type: constr(regex='^ScheduleTypeLimit$') = 'ScheduleTypeLimit'
  
      lower_limit: Union[NoLimit, float] = Field(
          default=NoLimit(),
          description='Lower limit for the schedule type or NoLimit.'
      )
  
      upper_limit: Union[NoLimit, float] = Field(
          default=NoLimit(),
          description='Upper limit for the schedule type or NoLimit.'
      )
  
      numeric_type: ScheduleNumericType = ScheduleNumericType.continuous
  
      unit_type: ScheduleUnitType = ScheduleUnitType.dimensionless
  
      class Config:
          @staticmethod
          def schema_extra(schema, model):
              schema['properties']['lower_limit']['anyOf'] = [
                      {"$ref": "#/components/schemas/NoLimit"},
                      {"type": "number"}
                  ]
              schema['properties']['upper_limit']['anyOf'] = [
                      {"$ref": "#/components/schemas/NoLimit"},
                      {"type": "number"}
                  ]
  
  
  class ScheduleDay(IDdEnergyBaseModel):
      """Used to describe the daily schedule for a single simulation day."""
  
      type: constr(regex='^ScheduleDay$') = 'ScheduleDay'
  
      values: List[float] = Field(
          ...,
          description='A list of floats or integers for the values of the schedule. '
              'The length of this list must match the length of the times list.'
      )
  
      times: List[conlist(int, min_items=2, max_items=2)] = Field(
          [0, 0],
          description='A list of lists with each sub-list possesing 2 values for '
              '[hour, minute]. The length of the master list must match the length '
              'of the values list. Each time in the master list represents the time '
              'of day that the corresponding value begins to take effect. For example '
              '[(0,0), (9,0), (17,0)] in combination with the values [0, 1, 0] denotes a '
              'schedule value of 0 from 0:00 to 9:00, a value of 1 from 9:00 to 17:00 '
              'and 0 from 17:00 to the end of the day. Note that this representation '
              'of times as the "time of beginning" is a different convention than '
              'EnergyPlus, which uses "time until".'
      )
  
      @root_validator
      def check_times_values_match(cls, values):
          "Ensure the length of the times matches the length of the values."
          times, vals = values.get('times'), values.get('values')
          assert len(times) == len(vals), 'Length of schedule times must match ' \
              'the schedule values. {} != {}.'.format(len(v), len(values['values']))
          return values
  
      interpolate: bool = Field(
          False,
          description='Boolean to note whether values in between times should be '
              'linearly interpolated or whether successive values should take effect '
              'immediately upon the beginning time corrsponding to them.'
      )
  
  
  class ScheduleRuleAbridged(DatedBaseModel):
      """Schedule rule including a ScheduleDay and when it should be applied.."""
  
      type: constr(regex='^ScheduleRuleAbridged$') = 'ScheduleRuleAbridged'
  
      schedule_day: str = Field(
          ...,
          min_length=1,
          max_length=100,
          description='The identifier of a ScheduleDay object associated with this rule.'
      )
  
      apply_sunday: bool = Field(
          False,
          description='Boolean noting whether to apply schedule_day on Sundays.'
      )
  
      apply_monday: bool = Field(
          False,
          description='Boolean noting whether to apply schedule_day on Mondays.'
      )
  
      apply_tuesday: bool = Field(
          False,
          description='Boolean noting whether to apply schedule_day on Tuesdays.'
      )
  
      apply_wednesday: bool = Field(
          False,
          description='Boolean noting whether to apply schedule_day on Wednesdays.'
      )
  
      apply_thursday: bool = Field(
          False,
          description='Boolean noting whether to apply schedule_day on Thursdays.'
      )
  
      apply_friday: bool = Field(
          False,
          description='Boolean noting whether to apply schedule_day on Fridays.'
      )
  
      apply_saturday: bool = Field(
          False,
          description='Boolean noting whether to apply schedule_day on Saturdays.'
      )
  
      start_date: List[int] = Field(
          [1, 1],
          min_items=2,
          max_items=3,
          description='A list of two integers for [month, day], representing the start '
              'date of the period over which the schedule_day will be applied.'
              'A third integer may be added to denote whether the date should be '
              're-serialized for a leap year (it should be a 1 in this case).'
      )
  
      @validator('start_date')
      def check_start_date(cls, v):
          return cls.check_date(v)
  
      end_date: List[int] = Field(
          [12, 31],
          min_items=2,
          max_items=3,
          description='A list of two integers for [month, day], representing the end '
              'date of the period over which the schedule_day will be applied.'
              'A third integer may be added to denote whether the date should be '
              're-serialized for a leap year (it should be a 1 in this case).'
      )
  
      @validator('end_date')
      def check_end_date(cls, v):
          return cls.check_date(v)
  
  
  class ScheduleRulesetAbridged(IDdEnergyBaseModel):
      """Used to define a schedule for a default day, further described by ScheduleRule."""
  
      type: constr(regex='^ScheduleRulesetAbridged$') = 'ScheduleRulesetAbridged'
  
      day_schedules: List[ScheduleDay] = Field(
          ...,
          description='A list of ScheduleDays that are referenced in the other keys of '
              'this ScheduleRulesetAbridged.'
      )
  
      default_day_schedule: str = Field(
          ...,
          min_length=1,
          max_length=100,
          description='An identifier for the ScheduleDay that will be used for '
              'all days when no ScheduleRule is applied. This ScheduleDay must be '
              'in the day_schedules.'
      )
  
      schedule_rules: List[ScheduleRuleAbridged] = Field(
          default=None,
          description='A list of ScheduleRuleAbridged that note exceptions to the '
              'default_day_schedule. These rules should be ordered from highest to '
              'lowest priority.'
      )
  
      holiday_schedule: str = Field(
          default=None,
          min_length=1,
          max_length=100,
          description='An identifier for the ScheduleDay that will be used for holidays. '
              'This ScheduleDay must be in the day_schedules.'
      )
  
      summer_designday_schedule: str = Field(
          default=None,
          min_length=1,
          max_length=100,
          description='An identifier for the ScheduleDay that will be used for '
              'the summer design day. This ScheduleDay must be in the day_schedules.'
      )
  
      winter_designday_schedule: str = Field(
          default=None,
          min_length=1,
          max_length=100,
          description='An identifier for the ScheduleDay that will be used for the '
              'winter design day. This ScheduleDay must be in the day_schedules.'
      )
  
      schedule_type_limit: str = Field(
          default=None,
          min_length=1,
          max_length=100,
          description='Identifier of a ScheduleTypeLimit that will be used to validate '
              'schedule values against upper/lower limits and assign units to the '
              'schedule values. If None, no validation will occur.'
      )
  
  
  class ScheduleRuleset(ScheduleRulesetAbridged):
      """Used to define a schedule for a default day, further described by ScheduleRule."""
  
      type: constr(regex='^ScheduleRuleset$') = 'ScheduleRuleset'
  
      schedule_type_limit: ScheduleTypeLimit = Field(
          default=None,
          description='ScheduleTypeLimit object that will be used to validate '
              'schedule values against upper/lower limits and assign units to the '
              'schedule values. If None, no validation will occur.',
      )
  
  
  class ScheduleFixedIntervalAbridged(IDdEnergyBaseModel):
      """Used to specify a start date and a list of values for a period of analysis."""
  
      type: constr(regex='^ScheduleFixedIntervalAbridged$') = \
          'ScheduleFixedIntervalAbridged'
  
      values: List[float] = Field(
          ...,
          min_items=24,
          max_items=527040,
          description='A list of timeseries values occuring at each timestep over '
              'the course of the simulation.'
      )
  
      schedule_type_limit: str = Field(
          default=None,
          min_length=1,
          max_length=100,
          description='Identifier of a ScheduleTypeLimit that will be used to validate '
              'schedule values against upper/lower limits and assign units to the '
              'schedule values. If None, no validation will occur.'
      )
  
      timestep: int = Field(
          1,
          description='An integer for the number of steps per hour that the input '
              'values correspond to.  For example, if each value represents 30 '
              'minutes, the timestep is 2. For 15 minutes, it is 4.'
      )
  
      @validator('timestep')
      def check_timestep(cls, v):
          "Ensure the timestep is acceptable by EnergyPlus."
          valid_timesteps = (1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60)
          assert v in valid_timesteps, '"{}" is not a valid timestep. ' \
              'Choose from {}'.format(v, valid_timesteps)
          return v
  
      start_date: List[int] = Field(
          [1, 1],
          min_items=2,
          max_items=3,
          description='A list of two integers for [month, day], representing the start '
              'date when the schedule values begin to take effect.'
              'A third integer may be added to denote whether the date should be '
              're-serialized for a leap year (it should be a 1 in this case).'
      )
  
      @validator('start_date')
      def check_start_date(cls, v):
          if len(v) == 3 and v[2]:
              try:
                  datetime.date(2016, v[0] , v[1])
              except ValueError:
                  raise ValueError('{}/{} is not a valid date.'.format(v[0], v[1]))
          else:
              try:
                  datetime.date(2017, v[0] , v[1])
              except ValueError:
                  raise ValueError('{}/{} is not a valid date.'.format(v[0], v[1]))
          return v
  
      placeholder_value: float = Field(
          0,
          description=' A value that will be used for all times not covered by the '
              'input values. Typically, your simulation should not need to use this '
              'value if the input values completely cover the simulation period.'
      )
  
      interpolate: bool = Field(
          False,
          description='Boolean to note whether values in between intervals should be '
              'linearly interpolated or whether successive values should take effect '
              'immediately upon the beginning time corrsponding to them.'
      )
  
      @root_validator
      def check_number_of_values(cls, values):
          "Ensure an acceptable number of schedule values."
          vals = values.get('values')
          timestep = values.get('timestep')
          start_date = values.get('start_date')
          if len(vals) < 24 * timestep:
               raise ValueError('Number of schedule values must be for at least one day, '
                                'with a length greater than 24 * timestep.')
          max_l = timestep * 8760 if len(start_date) == 3 and start_date[2] \
              else timestep * 8784
          if len(vals) > max_l:
               raise ValueError('Number of schedule values must not exceed a full year, '
                                'with a length greater than 8760 * timestep.')
          if len(vals) % (24 * timestep) != 0:
              raise ValueError(
                  'Number of schedule values must be for a whole number of days.')
          return values
  
  
  class ScheduleFixedInterval(ScheduleFixedIntervalAbridged):
      """Used to specify a start date and a list of values for a period of analysis."""
  
      type: constr(regex='^ScheduleFixedInterval$') = 'ScheduleFixedInterval'
  
      schedule_type_limit: ScheduleTypeLimit = Field(
          default=None,
          description='ScheduleTypeLimit object that will be used to validate '
              'schedule values against upper/lower limits and assign units to the '
              'schedule values. If None, no validation will occur.',
      )
  
  
  if __name__ == '__main__':
      print(ScheduleFixedIntervalAbridged.schema_json(indent=2))
      print(ScheduleRulesetAbridged.schema_json(indent=2))
      print(ScheduleFixedInterval.schema_json(indent=2))
      print(ScheduleRuleset.schema_json(indent=2))
  