<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2017, AGB & GC
# Full license can be found in License.md
# -----------------------------------------------------------------------------
"""Tests the boundary EABoundary class."""

import datetime
from io import StringIO
import logging
import numpy
from os import path
import unittest

import ocbpy
from . import test_boundary_ocboundary as test_ocb

class TestDualBoundaryLogFailure(unittest.TestCase):
    """Test the logging messages raised by the DualBoundary class."""

    def setUp(self):
        """Initialize the test class."""
        self.lwarn = ""
        self.lout = ""
        self.log_capture = StringIO()
        ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture))
        ocbpy.logger.setLevel(logging.WARNING)
        return

    def tearDown(self):
        """Tear down the test case."""
        del self.lwarn, self.lout, self.log_capture
        return

    def test_bad_instrument_name(self):
        """Test OCB initialization with bad instrument name."""
        self.lwarn = "OCB instrument must be a string"

        # Initialize the DualBoundary class with bad instrument names
        for bad_inst in [1, None, True]:
            for btype in ["eab", "ocb"]:
                # Define the kwarg input
                val = {"_".join([btype, "instrument"]): bad_inst}
                with self.subTest(val=val):
                    # Initalize the DualBoundary class
                    bound = ocbpy.DualBoundary(**val)
                    subclass = getattr(bound, btype)

                    # Test the values for the sub-class
                    self.assertIsNone(subclass.filename)
                    self.assertIsNone(subclass.instrument)

                    self.lout = self.log_capture.getvalue()

                    # Test logging error message for each bad initialization
                    self.assertRegex(self.lout, self.lwarn)
        return

    def test_bad_filename(self):
        """Test initialization with a bad default file/instrument pairing."""
        self.lwarn = "name provided is not a file\ncannot open OCB file [hi]"

        # Try to load data with a non-existant file name
        for btype in ["eab", "ocb"]:
            # Define the kwarg input
            val = {"_".join([btype, "filename"]): "hi"}
            with self.subTest(val=val):
                # Initalize the DualBoundary class
                bound = ocbpy.DualBoundary(**val)
                subclass = getattr(bound, btype)

                # Test the values for the sub-class
                self.assertIsNone(subclass.filename)

                self.lout = self.log_capture.getvalue()

                # Test logging error message for each bad initialization
                self.assertTrue(
                    self.lout.find(self.lwarn) >= 0,
                    msg="logging output {:} != expected output {:}".format(
                        repr(self.lout), repr(self.lwarn)))

        return

class TestDualBoundaryInstruments(test_ocb.TestOCBoundaryInstruments):
    """Test the DualBoundary handling of different instruments."""

    def setUp(self):
        """Initialize the instrument information."""
        self.test_class = ocbpy.DualBoundary
        self.test_dir = path.join(path.dirname(ocbpy.__file__), "tests",
                                  "test_data")
        self.inst_attrs = {"image": ["year", "soy", "num_sectors", "a",
                                     "r_err", "fom"],
                           "ampere": ["date", "time", "x", "y", "fom"],
                           "dmsp-ssj": ["date", "time", "sc", "x", "y", "fom",
                                        "x_1", "x_2", "y_1", "y_2"]}
        self.not_attrs = {"image": ["date", "time", "x", "y", "x_1", "x_2",
                                    "y_1", "y_2", "sc"],
                          "ampere": ["year", "soy", "x_1", "y_1", "x_2",
                                     "y_2", "sc", "num_sectors", "a",
                                     "r_err"],
                          "dmsp-ssj": ["year", "soy", "num_sectors", "a",
                                       "r_err"]}
        self.inst_init = [{"eab_instrument": "dmsp-ssj", "hemisphere": 1,
                           "eab_filename": path.join(self.test_dir,
                                                     "dmsp-ssj_north_out.eab"),
                           "ocb_instrument": "image",
                           "ocb_filename": path.join(self.test_dir,
                                                     "test_north_circle")},
                          {"eab_instrument": "dmsp-ssj", "hemisphere": 1,
                           "eab_filename": path.join(self.test_dir,
                                                     "dmsp-ssj_north_out.eab"),
                           "ocb_instrument": "dmsp-ssj",
                           "ocb_filename": path.join(self.test_dir,
                                                     "dmsp-ssj_north_out.ocb")},
                          {"eab_instrument": "dmsp-ssj", "hemisphere": -1,
                           "eab_filename": path.join(self.test_dir,
                                                     "dmsp-ssj_south_out.eab"),
                           "ocb_instrument": "ampere",
                           "ocb_filename": path.join(self.test_dir,
                                                     "test_south_circle")}]
        self.ocb = None
        return

    def tearDown(self):
        """Clean up the test environment."""
        del self.test_dir, self.inst_attrs, self.inst_init, self.ocb
        del self.test_class
        return

class TestDualBoundaryMethodsGeneral(test_ocb.TestOCBoundaryMethodsGeneral):
    """Test the DualBoundary general methods."""

    def setUp(self):
        """Initialize the test environment."""
        self.test_class = ocbpy.DualBoundary
        test_dir = path.join(path.dirname(ocbpy.__file__), "tests", "test_data")
        self.set_empty = {"ocb_filename": path.join(test_dir, "test_empty"),
                          "eab_filename": path.join(test_dir, "test_empty"),
                          "ocb_instrument": "image", "eab_instrument": "image",
                          "hemisphere": 1}
        self.set_default = {"ocb_filename":
                            path.join(test_dir, "dmsp-ssj_north_out.ocb"),
                            "eab_filename":
                            path.join(test_dir, "dmsp-ssj_north_out.eab"),
                            "ocb_instrument": "dmsp-ssj",
                            "eab_instrument": "dmsp-ssj", "hemisphere": 1,
                            "max_delta": 600}
        self.ocb = None
        return

    def tearDown(self):
        """Clean up the test environment."""
        del self.set_empty, self.set_default, self.ocb
        return

    def test_repr_string(self):
        """Test __repr__ method string."""
        # Initalize the class object
        self.ocb = self.test_class(**self.set_default)

        # Get the representation of the class object and split by subclasses
        rocb = repr(self.ocb).split("ocb=")

        # Test the name of the repr object
        self.assertRegex(rocb[0], self.test_class.__name__)

        # Test each set kwarg has the expected value
        for val in self.set_default.keys():
            with self.subTest(val=val):
                i = 0 if val.find("eab") == 0 else 1

                # Construct the expected string
                if i == 0 or val.find("ocb") == 0:
                    test_str = "=".join([val.split("_")[-1],
                                         repr(self.set_default[val])])
                else:
                    test_str = "=".join([val, repr(self.set_default[val])])

                # Test the correct part of the repr output
                self.assertRegex(rocb[i], test_str)
        return

    def test_short_str(self):
        """Test the default class print output."""
        self.ocb = self.test_class(**self.set_default)
        self.ocb.records = 1

        self.assertRegex(self.ocb.__str__(), "1 good boundary pairs from")
        return

    def test_bad_rfunc_inst(self):
        """Test failure setting default rfunc for unknown instrument."""

        for bound in ['eab', 'ocb']:
            with self.subTest(bound=bound):
                self.set_empty["_".join([bound, "instrument"])] = "bad"

                with self.assertRaisesRegex(ValueError, "unknown instrument"):
                    self.ocb = self.test_class(**self.set_empty)
        return

    def test_no_file_str(self):
        """Test the unset class print output."""

        for bound in ['eab', 'ocb', 'both']:
            out_str = []
            # Update the kwarg input
            if bound in ['eab', 'both']:
                self.set_default['eab_filename'] = None
                out_str.append("No {:s} file specified".format(
                    ocbpy.EABoundary.__name__))
            if bound in ['ocb', 'both']:
                self.set_default['ocb_filename'] = None
                out_str.append("No {:s} file specified".format(
                    ocbpy.OCBoundary.__name__))

            # Initalise the object
            self.ocb = self.test_class(**self.set_default)

            for val in out_str:
                with self.subTest(val=val):
                    # Test the output string
                    self.assertRegex(self.ocb.__str__(), val)
        return

    def test_nofile_init(self):
        """Ensure that the class can be initialised without loading a file."""
        self.ocb = self.test_class(eab_filename=None, ocb_filename=None)

        self.assertIsNone(self.ocb.eab.filename)
        self.assertIsNone(self.ocb.ocb.filename)
        self.assertIsNone(self.ocb.dtime)
        self.assertEqual(self.ocb.records, 0)
        return
<commit_msg>TST: windows allowance<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2017, AGB & GC
# Full license can be found in License.md
# -----------------------------------------------------------------------------
"""Tests the boundary EABoundary class."""

import datetime
from io import StringIO
import logging
import numpy
from os import path
import platform
import unittest

import ocbpy
from . import test_boundary_ocboundary as test_ocb

class TestDualBoundaryLogFailure(unittest.TestCase):
    """Test the logging messages raised by the DualBoundary class."""

    def setUp(self):
        """Initialize the test class."""
        self.lwarn = ""
        self.lout = ""
        self.log_capture = StringIO()
        ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture))
        ocbpy.logger.setLevel(logging.WARNING)
        return

    def tearDown(self):
        """Tear down the test case."""
        del self.lwarn, self.lout, self.log_capture
        return

    def test_bad_instrument_name(self):
        """Test OCB initialization with bad instrument name."""
        self.lwarn = "OCB instrument must be a string"

        # Initialize the DualBoundary class with bad instrument names
        for bad_inst in [1, None, True]:
            for btype in ["eab", "ocb"]:
                # Define the kwarg input
                val = {"_".join([btype, "instrument"]): bad_inst}
                with self.subTest(val=val):
                    # Initalize the DualBoundary class
                    bound = ocbpy.DualBoundary(**val)
                    subclass = getattr(bound, btype)

                    # Test the values for the sub-class
                    self.assertIsNone(subclass.filename)
                    self.assertIsNone(subclass.instrument)

                    self.lout = self.log_capture.getvalue()

                    # Test logging error message for each bad initialization
                    self.assertRegex(self.lout, self.lwarn)
        return

    def test_bad_filename(self):
        """Test initialization with a bad default file/instrument pairing."""
        self.lwarn = "name provided is not a file\ncannot open OCB file [hi]"

        # Try to load data with a non-existant file name
        for btype in ["eab", "ocb"]:
            # Define the kwarg input
            val = {"_".join([btype, "filename"]): "hi"}
            with self.subTest(val=val):
                # Initalize the DualBoundary class
                bound = ocbpy.DualBoundary(**val)
                subclass = getattr(bound, btype)

                # Test the values for the sub-class
                self.assertIsNone(subclass.filename)

                self.lout = self.log_capture.getvalue()

                # Test logging error message for each bad initialization
                self.assertTrue(
                    self.lout.find(self.lwarn) >= 0,
                    msg="logging output {:} != expected output {:}".format(
                        repr(self.lout), repr(self.lwarn)))

        return

class TestDualBoundaryInstruments(test_ocb.TestOCBoundaryInstruments):
    """Test the DualBoundary handling of different instruments."""

    def setUp(self):
        """Initialize the instrument information."""
        self.test_class = ocbpy.DualBoundary
        self.test_dir = path.join(path.dirname(ocbpy.__file__), "tests",
                                  "test_data")
        self.inst_attrs = {"image": ["year", "soy", "num_sectors", "a",
                                     "r_err", "fom"],
                           "ampere": ["date", "time", "x", "y", "fom"],
                           "dmsp-ssj": ["date", "time", "sc", "x", "y", "fom",
                                        "x_1", "x_2", "y_1", "y_2"]}
        self.not_attrs = {"image": ["date", "time", "x", "y", "x_1", "x_2",
                                    "y_1", "y_2", "sc"],
                          "ampere": ["year", "soy", "x_1", "y_1", "x_2",
                                     "y_2", "sc", "num_sectors", "a",
                                     "r_err"],
                          "dmsp-ssj": ["year", "soy", "num_sectors", "a",
                                       "r_err"]}
        self.inst_init = [{"eab_instrument": "dmsp-ssj", "hemisphere": 1,
                           "eab_filename": path.join(self.test_dir,
                                                     "dmsp-ssj_north_out.eab"),
                           "ocb_instrument": "image",
                           "ocb_filename": path.join(self.test_dir,
                                                     "test_north_circle")},
                          {"eab_instrument": "dmsp-ssj", "hemisphere": 1,
                           "eab_filename": path.join(self.test_dir,
                                                     "dmsp-ssj_north_out.eab"),
                           "ocb_instrument": "dmsp-ssj",
                           "ocb_filename": path.join(self.test_dir,
                                                     "dmsp-ssj_north_out.ocb")},
                          {"eab_instrument": "dmsp-ssj", "hemisphere": -1,
                           "eab_filename": path.join(self.test_dir,
                                                     "dmsp-ssj_south_out.eab"),
                           "ocb_instrument": "ampere",
                           "ocb_filename": path.join(self.test_dir,
                                                     "test_south_circle")}]
        self.ocb = None
        return

    def tearDown(self):
        """Clean up the test environment."""
        del self.test_dir, self.inst_attrs, self.inst_init, self.ocb
        del self.test_class
        return

class TestDualBoundaryMethodsGeneral(test_ocb.TestOCBoundaryMethodsGeneral):
    """Test the DualBoundary general methods."""

    def setUp(self):
        """Initialize the test environment."""
        self.test_class = ocbpy.DualBoundary
        test_dir = path.join(path.dirname(ocbpy.__file__), "tests", "test_data")
        self.set_empty = {"ocb_filename": path.join(test_dir, "test_empty"),
                          "eab_filename": path.join(test_dir, "test_empty"),
                          "ocb_instrument": "image", "eab_instrument": "image",
                          "hemisphere": 1}
        self.set_default = {"ocb_filename":
                            path.join(test_dir, "dmsp-ssj_north_out.ocb"),
                            "eab_filename":
                            path.join(test_dir, "dmsp-ssj_north_out.eab"),
                            "ocb_instrument": "dmsp-ssj",
                            "eab_instrument": "dmsp-ssj", "hemisphere": 1,
                            "max_delta": 600}
        self.ocb = None
        return

    def tearDown(self):
        """Clean up the test environment."""
        del self.set_empty, self.set_default, self.ocb
        return

    def test_repr_string(self):
        """Test __repr__ method string."""
        # Initalize the class object
        self.ocb = self.test_class(**self.set_default)

        # Get the representation of the class object and split by subclasses
        rocb = repr(self.ocb).split("ocb=")

        # Test the name of the repr object
        self.assertRegex(rocb[0], self.test_class.__name__)

        # Test each set kwarg has the expected value
        for val in self.set_default.keys():
            with self.subTest(val=val):
                i = 0 if val.find("eab") == 0 else 1

                # Construct the expected string
                if i == 0 or val.find("ocb") == 0:
                    test_str = "=".join([val.split("_")[-1],
                                         repr(self.set_default[val])])
                else:
                    test_str = "=".join([val, repr(self.set_default[val])])

                # Windows has trouble recognizing the filename in Regex
                if(test_str.find("filename")
                   and platform.system().lower() == "windows"):
                    test_str = "filename="

                # Test the correct part of the repr output.
                self.assertRegex(rocb[i], test_str)
        return

    def test_short_str(self):
        """Test the default class print output."""
        self.ocb = self.test_class(**self.set_default)
        self.ocb.records = 1

        self.assertRegex(self.ocb.__str__(), "1 good boundary pairs from")
        return

    def test_bad_rfunc_inst(self):
        """Test failure setting default rfunc for unknown instrument."""

        for bound in ['eab', 'ocb']:
            with self.subTest(bound=bound):
                self.set_empty["_".join([bound, "instrument"])] = "bad"

                with self.assertRaisesRegex(ValueError, "unknown instrument"):
                    self.ocb = self.test_class(**self.set_empty)
        return

    def test_no_file_str(self):
        """Test the unset class print output."""

        for bound in ['eab', 'ocb', 'both']:
            out_str = []
            # Update the kwarg input
            if bound in ['eab', 'both']:
                self.set_default['eab_filename'] = None
                out_str.append("No {:s} file specified".format(
                    ocbpy.EABoundary.__name__))
            if bound in ['ocb', 'both']:
                self.set_default['ocb_filename'] = None
                out_str.append("No {:s} file specified".format(
                    ocbpy.OCBoundary.__name__))

            # Initalise the object
            self.ocb = self.test_class(**self.set_default)

            for val in out_str:
                with self.subTest(val=val):
                    # Test the output string
                    self.assertRegex(self.ocb.__str__(), val)
        return

    def test_nofile_init(self):
        """Ensure that the class can be initialised without loading a file."""
        self.ocb = self.test_class(eab_filename=None, ocb_filename=None)

        self.assertIsNone(self.ocb.eab.filename)
        self.assertIsNone(self.ocb.ocb.filename)
        self.assertIsNone(self.ocb.dtime)
        self.assertEqual(self.ocb.records, 0)
        return
<|endoftext|>


<commit_before>"""Helpers that help with state related things."""
import json
import logging
from collections import defaultdict

import homeassistant.util.dt as dt_util
from homeassistant.components.media_player import (
    ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_SEEK_POSITION,
    ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, SERVICE_PLAY_MEDIA,
    SERVICE_SELECT_SOURCE, ATTR_INPUT_SOURCE)
from homeassistant.components.notify import (
    ATTR_MESSAGE, SERVICE_NOTIFY)
from homeassistant.components.sun import (
    STATE_ABOVE_HORIZON, STATE_BELOW_HORIZON)
from homeassistant.components.switch.mysensors import (
    ATTR_IR_CODE, SERVICE_SEND_IR_CODE)
from homeassistant.components.thermostat import (
    ATTR_AWAY_MODE, ATTR_FAN, SERVICE_SET_AWAY_MODE, SERVICE_SET_FAN_MODE,
    SERVICE_SET_TEMPERATURE)
from homeassistant.components.thermostat.ecobee import (
    ATTR_FAN_MIN_ON_TIME, SERVICE_SET_FAN_MIN_ON_TIME)
from homeassistant.components.hvac import (
    ATTR_HUMIDITY, ATTR_SWING_MODE, ATTR_OPERATION_MODE, ATTR_AUX_HEAT,
    SERVICE_SET_HUMIDITY, SERVICE_SET_SWING_MODE,
    SERVICE_SET_OPERATION_MODE, SERVICE_SET_AUX_HEAT)
from homeassistant.const import (
    ATTR_ENTITY_ID, ATTR_TEMPERATURE, SERVICE_ALARM_ARM_AWAY,
    SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_DISARM, SERVICE_ALARM_TRIGGER,
    SERVICE_CLOSE, SERVICE_LOCK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY,
    SERVICE_MEDIA_SEEK, SERVICE_MOVE_DOWN, SERVICE_MOVE_UP, SERVICE_OPEN,
    SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_UNLOCK, SERVICE_VOLUME_MUTE,
    SERVICE_VOLUME_SET, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME,
    STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_CLOSED, STATE_LOCKED,
    STATE_OFF, STATE_ON, STATE_OPEN, STATE_PAUSED, STATE_PLAYING,
    STATE_UNKNOWN, STATE_UNLOCKED)
from homeassistant.core import State

_LOGGER = logging.getLogger(__name__)

GROUP_DOMAIN = 'group'
HASS_DOMAIN = 'homeassistant'

# Update this dict of lists when new services are added to HA.
# Each item is a service with a list of required attributes.
SERVICE_ATTRIBUTES = {
    SERVICE_PLAY_MEDIA: [ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID],
    SERVICE_MEDIA_SEEK: [ATTR_MEDIA_SEEK_POSITION],
    SERVICE_VOLUME_MUTE: [ATTR_MEDIA_VOLUME_MUTED],
    SERVICE_VOLUME_SET: [ATTR_MEDIA_VOLUME_LEVEL],
    SERVICE_NOTIFY: [ATTR_MESSAGE],
    SERVICE_SET_AWAY_MODE: [ATTR_AWAY_MODE],
    SERVICE_SET_FAN_MODE: [ATTR_FAN],
    SERVICE_SET_FAN_MIN_ON_TIME: [ATTR_FAN_MIN_ON_TIME],
    SERVICE_SET_TEMPERATURE: [ATTR_TEMPERATURE],
    SERVICE_SET_HUMIDITY: [ATTR_HUMIDITY],
    SERVICE_SET_SWING_MODE: [ATTR_SWING_MODE],
    SERVICE_SET_OPERATION_MODE: [ATTR_OPERATION_MODE],
    SERVICE_SET_AUX_HEAT: [ATTR_AUX_HEAT],
    SERVICE_SELECT_SOURCE: [ATTR_INPUT_SOURCE],
    SERVICE_SEND_IR_CODE: [ATTR_IR_CODE]
}

# Update this dict when new services are added to HA.
# Each item is a service with a corresponding state.
SERVICE_TO_STATE = {
    SERVICE_TURN_ON: STATE_ON,
    SERVICE_TURN_OFF: STATE_OFF,
    SERVICE_MEDIA_PLAY: STATE_PLAYING,
    SERVICE_MEDIA_PAUSE: STATE_PAUSED,
    SERVICE_ALARM_ARM_AWAY: STATE_ALARM_ARMED_AWAY,
    SERVICE_ALARM_ARM_HOME: STATE_ALARM_ARMED_HOME,
    SERVICE_ALARM_DISARM: STATE_ALARM_DISARMED,
    SERVICE_ALARM_TRIGGER: STATE_ALARM_TRIGGERED,
    SERVICE_LOCK: STATE_LOCKED,
    SERVICE_UNLOCK: STATE_UNLOCKED,
    SERVICE_CLOSE: STATE_CLOSED,
    SERVICE_OPEN: STATE_OPEN,
    SERVICE_MOVE_UP: STATE_OPEN,
    SERVICE_MOVE_DOWN: STATE_CLOSED,
}

# pylint: disable=too-few-public-methods, attribute-defined-outside-init
class TrackStates(object):
    """
    Record the time when the with-block is entered.

    Add all states that have changed since the start time to the return list
    when with-block is exited.
    """

    def __init__(self, hass):
        """Initialize a TrackStates block."""
        self.hass = hass
        self.states = []

    def __enter__(self):
        """Record time from which to track changes."""
        self.now = dt_util.utcnow()
        return self.states

    def __exit__(self, exc_type, exc_value, traceback):
        """Add changes states to changes list."""
        self.states.extend(get_changed_since(self.hass.states.all(), self.now))

def get_changed_since(states, utc_point_in_time):
    """Return list of states that have been changed since utc_point_in_time."""
    return [state for state in states
            if state.last_updated >= utc_point_in_time]

def reproduce_state(hass, states, blocking=False):
    """Reproduce given state."""
    if isinstance(states, State):
        states = [states]

    to_call = defaultdict(list)

    for state in states:

        if hass.states.get(state.entity_id) is None:
            _LOGGER.warning('reproduce_state: Unable to find entity %s',
                            state.entity_id)
            continue

        if state.domain == GROUP_DOMAIN:
            service_domain = HASS_DOMAIN
        else:
            service_domain = state.domain

        domain_services = hass.services.services[service_domain]

        service = None
        for _service in domain_services.keys():
            if (_service in SERVICE_ATTRIBUTES and
                    all(attr in state.attributes
                        for attr in SERVICE_ATTRIBUTES[_service]) or
                    _service in SERVICE_TO_STATE and
                    SERVICE_TO_STATE[_service] == state.state):
                service = _service
            if (_service in SERVICE_TO_STATE and
                    SERVICE_TO_STATE[_service] == state.state):
                break

        if not service:
            _LOGGER.warning("reproduce_state: Unable to reproduce state %s",
                            state)
            continue

        # We group service calls for entities by service call
        # json used to create a hashable version of dict with maybe lists in it
        key = (service_domain, service,
               json.dumps(dict(state.attributes), sort_keys=True))
        to_call[key].append(state.entity_id)

    for (service_domain, service, service_data), entity_ids in to_call.items():
        data = json.loads(service_data)
        data[ATTR_ENTITY_ID] = entity_ids
        hass.services.call(service_domain, service, data, blocking)

def state_as_number(state):
    """
    Try to coerce our state to a number.

    Raises ValueError if this is not possible.
    """
    if state.state in (STATE_ON, STATE_LOCKED, STATE_ABOVE_HORIZON,
                       STATE_OPEN):
        return 1
    elif state.state in (STATE_OFF, STATE_UNLOCKED, STATE_UNKNOWN,
                         STATE_BELOW_HORIZON, STATE_CLOSED):
        return 0

    return float(state.state)
<commit_msg>Add open/closed state for open_cover and close_cover in SERVICE_TO_STATE (#3180)<commit_after>"""Helpers that help with state related things."""
import json
import logging
from collections import defaultdict

import homeassistant.util.dt as dt_util
from homeassistant.components.media_player import (
    ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_SEEK_POSITION,
    ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, SERVICE_PLAY_MEDIA,
    SERVICE_SELECT_SOURCE, ATTR_INPUT_SOURCE)
from homeassistant.components.notify import (
    ATTR_MESSAGE, SERVICE_NOTIFY)
from homeassistant.components.sun import (
    STATE_ABOVE_HORIZON, STATE_BELOW_HORIZON)
from homeassistant.components.switch.mysensors import (
    ATTR_IR_CODE, SERVICE_SEND_IR_CODE)
from homeassistant.components.thermostat import (
    ATTR_AWAY_MODE, ATTR_FAN, SERVICE_SET_AWAY_MODE, SERVICE_SET_FAN_MODE,
    SERVICE_SET_TEMPERATURE)
from homeassistant.components.thermostat.ecobee import (
    ATTR_FAN_MIN_ON_TIME, SERVICE_SET_FAN_MIN_ON_TIME)
from homeassistant.components.hvac import (
    ATTR_HUMIDITY, ATTR_SWING_MODE, ATTR_OPERATION_MODE, ATTR_AUX_HEAT,
    SERVICE_SET_HUMIDITY, SERVICE_SET_SWING_MODE,
    SERVICE_SET_OPERATION_MODE, SERVICE_SET_AUX_HEAT)
from homeassistant.const import (
    ATTR_ENTITY_ID, ATTR_TEMPERATURE, SERVICE_ALARM_ARM_AWAY,
    SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_DISARM, SERVICE_ALARM_TRIGGER,
    SERVICE_CLOSE, SERVICE_LOCK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY,
    SERVICE_MEDIA_SEEK, SERVICE_MOVE_DOWN, SERVICE_MOVE_UP, SERVICE_OPEN,
    SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_UNLOCK, SERVICE_VOLUME_MUTE,
    SERVICE_VOLUME_SET, SERVICE_OPEN_COVER, SERVICE_CLOSE_COVER,
    STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
    STATE_ALARM_TRIGGERED, STATE_CLOSED, STATE_LOCKED, STATE_OFF, STATE_ON,
    STATE_OPEN, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN, STATE_UNLOCKED)
from homeassistant.core import State

_LOGGER = logging.getLogger(__name__)

GROUP_DOMAIN = 'group'
HASS_DOMAIN = 'homeassistant'

# Update this dict of lists when new services are added to HA.
# Each item is a service with a list of required attributes.
SERVICE_ATTRIBUTES = {
    SERVICE_PLAY_MEDIA: [ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_CONTENT_ID],
    SERVICE_MEDIA_SEEK: [ATTR_MEDIA_SEEK_POSITION],
    SERVICE_VOLUME_MUTE: [ATTR_MEDIA_VOLUME_MUTED],
    SERVICE_VOLUME_SET: [ATTR_MEDIA_VOLUME_LEVEL],
    SERVICE_NOTIFY: [ATTR_MESSAGE],
    SERVICE_SET_AWAY_MODE: [ATTR_AWAY_MODE],
    SERVICE_SET_FAN_MODE: [ATTR_FAN],
    SERVICE_SET_FAN_MIN_ON_TIME: [ATTR_FAN_MIN_ON_TIME],
    SERVICE_SET_TEMPERATURE: [ATTR_TEMPERATURE],
    SERVICE_SET_HUMIDITY: [ATTR_HUMIDITY],
    SERVICE_SET_SWING_MODE: [ATTR_SWING_MODE],
    SERVICE_SET_OPERATION_MODE: [ATTR_OPERATION_MODE],
    SERVICE_SET_AUX_HEAT: [ATTR_AUX_HEAT],
    SERVICE_SELECT_SOURCE: [ATTR_INPUT_SOURCE],
    SERVICE_SEND_IR_CODE: [ATTR_IR_CODE]
}

# Update this dict when new services are added to HA.
# Each item is a service with a corresponding state.
SERVICE_TO_STATE = {
    SERVICE_TURN_ON: STATE_ON,
    SERVICE_TURN_OFF: STATE_OFF,
    SERVICE_MEDIA_PLAY: STATE_PLAYING,
    SERVICE_MEDIA_PAUSE: STATE_PAUSED,
    SERVICE_ALARM_ARM_AWAY: STATE_ALARM_ARMED_AWAY,
    SERVICE_ALARM_ARM_HOME: STATE_ALARM_ARMED_HOME,
    SERVICE_ALARM_DISARM: STATE_ALARM_DISARMED,
    SERVICE_ALARM_TRIGGER: STATE_ALARM_TRIGGERED,
    SERVICE_LOCK: STATE_LOCKED,
    SERVICE_UNLOCK: STATE_UNLOCKED,
    SERVICE_CLOSE: STATE_CLOSED,
    SERVICE_OPEN: STATE_OPEN,
    SERVICE_MOVE_UP: STATE_OPEN,
    SERVICE_MOVE_DOWN: STATE_CLOSED,
    SERVICE_OPEN_COVER: STATE_OPEN,
    SERVICE_CLOSE_COVER: STATE_CLOSED
}

# pylint: disable=too-few-public-methods, attribute-defined-outside-init
class TrackStates(object):
    """
    Record the time when the with-block is entered.

    Add all states that have changed since the start time to the return list
    when with-block is exited.
    """

    def __init__(self, hass):
        """Initialize a TrackStates block."""
        self.hass = hass
        self.states = []

    def __enter__(self):
        """Record time from which to track changes."""
        self.now = dt_util.utcnow()
        return self.states

    def __exit__(self, exc_type, exc_value, traceback):
        """Add changes states to changes list."""
        self.states.extend(get_changed_since(self.hass.states.all(), self.now))

def get_changed_since(states, utc_point_in_time):
    """Return list of states that have been changed since utc_point_in_time."""
    return [state for state in states
            if state.last_updated >= utc_point_in_time]

def reproduce_state(hass, states, blocking=False):
    """Reproduce given state."""
    if isinstance(states, State):
        states = [states]

    to_call = defaultdict(list)

    for state in states:

        if hass.states.get(state.entity_id) is None:
            _LOGGER.warning('reproduce_state: Unable to find entity %s',
                            state.entity_id)
            continue

        if state.domain == GROUP_DOMAIN:
            service_domain = HASS_DOMAIN
        else:
            service_domain = state.domain

        domain_services = hass.services.services[service_domain]

        service = None
        for _service in domain_services.keys():
            if (_service in SERVICE_ATTRIBUTES and
                    all(attr in state.attributes
                        for attr in SERVICE_ATTRIBUTES[_service]) or
                    _service in SERVICE_TO_STATE and
                    SERVICE_TO_STATE[_service] == state.state):
                service = _service
            if (_service in SERVICE_TO_STATE and
                    SERVICE_TO_STATE[_service] == state.state):
                break

        if not service:
            _LOGGER.warning("reproduce_state: Unable to reproduce state %s",
                            state)
            continue

        # We group service calls for entities by service call
        # json used to create a hashable version of dict with maybe lists in it
        key = (service_domain, service,
               json.dumps(dict(state.attributes), sort_keys=True))
        to_call[key].append(state.entity_id)

    for (service_domain, service, service_data), entity_ids in to_call.items():
        data = json.loads(service_data)
        data[ATTR_ENTITY_ID] = entity_ids
        hass.services.call(service_domain, service, data, blocking)

def state_as_number(state):
    """
    Try to coerce our state to a number.

    Raises ValueError if this is not possible.
    """
    if state.state in (STATE_ON, STATE_LOCKED, STATE_ABOVE_HORIZON,
                       STATE_OPEN):
        return 1
    elif state.state in (STATE_OFF, STATE_UNLOCKED, STATE_UNKNOWN,
                         STATE_BELOW_HORIZON, STATE_CLOSED):
        return 0

    return float(state.state)
<|endoftext|>


<commit_before>"""Django page CMS ``models``."""

from pages.utils import get_placeholders, normalize_url
from pages.managers import PageManager, ContentManager
from pages.managers import PageAliasManager
from pages import settings

from datetime import datetime
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.utils.safestring import mark_safe
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.contrib.sites.models import Site

import mptt

PAGE_CONTENT_DICT_KEY = ContentManager.PAGE_CONTENT_DICT_KEY

class Page(models.Model):
    """
    This model contain the status, dates, author, template.
    The real content of the page can be found in the
    :class:`Content <pages.models.Content>` model.

    .. attribute:: creation_date
       When the page has been created.

    .. attribute:: publication_date
       When the page should be visible.

    .. attribute:: publication_end_date
       When the publication of this page end.

    .. attribute:: last_modification_date
       Last time this page has been modified.

    .. attribute:: status
       The current status of the page. Could be DRAFT, PUBLISHED,
       EXPIRED or HIDDEN. You should the property :attr:`calculated_status` if
       you want that the dates are taken in account.

    .. attribute:: template
       A string containing the name of the template file for this page.
    """

    # some class constants to refer to, e.g. Page.DRAFT
    DRAFT = 0
    PUBLISHED = 1
    EXPIRED = 2
    HIDDEN = 3
    STATUSES = (
        (PUBLISHED, _('Published')),
        (HIDDEN, _('Hidden')),
        (DRAFT, _('Draft')),
    )

    PAGE_LANGUAGES_KEY = "page_%d_languages"
    PAGE_URL_KEY = "page_%d_language_%s_url"
    PAGE_BROKEN_LINK_KEY = "page_broken_link_%s"

    author = models.ForeignKey(User, verbose_name=_('author'))

    parent = models.ForeignKey('self', null=True, blank=True,
            related_name='children', verbose_name=_('parent'))
    creation_date = models.DateTimeField(_('creation date'), editable=False,
            default=datetime.now)
    publication_date = models.DateTimeField(_('publication date'),
            null=True, blank=True, help_text=_('''When the page should go
            live. Status must be "Published" for page to go live.'''))
    publication_end_date = models.DateTimeField(_('publication end date'),
            null=True, blank=True, help_text=_('''When to expire the page.
            Leave empty to never expire.'''))

    last_modification_date = models.DateTimeField(_('last modification date'))

    status = models.IntegerField(_('status'), choices=STATUSES, default=DRAFT)
    template = models.CharField(_('template'), max_length=100, null=True,
            blank=True)

    delegate_to = models.CharField(_('delegate to'), max_length=100, null=True,
            blank=True)

    freeze_date = models.DateTimeField(_('freeze date'),
            null=True, blank=True, help_text=_('''Don't publish any content
            after this date.'''))

    if settings.PAGE_USE_SITE_ID:
        sites = models.ManyToManyField(Site, default=[settings.SITE_ID],
                help_text=_('The site(s) the page is accessible at.'),
                verbose_name=_('sites'))

    redirect_to_url = models.CharField(max_length=200, null=True, blank=True)

    redirect_to = models.ForeignKey('self', null=True, blank=True,
            related_name='redirected_pages')

    # Managers
    objects = PageManager()

    if settings.PAGE_TAGGING:
        from tagging import fields
        tags = fields.TagField(null=True)

    # per instance cache
    _languages = None
    _complete_slug = None
    _content_dict = None

    class Meta:
        """Make sure the default page ordering is correct."""
        ordering = ['tree_id', 'lft']
        get_latest_by = "publication_date"
        verbose_name = _('page')
        verbose_name_plural = _('pages')
        permissions = settings.PAGE_EXTRA_PERMISSIONS

    def save(self, *args, **kwargs):
        """Override the default ``save`` method."""
        if not self.status:
            self.status = self.DRAFT
        # Published pages should always have a publication date
        if self.publication_date is None and self.status == self.PUBLISHED:
            self.publication_date = datetime.now()
        # Drafts should not, unless they have been set to the future
        if self.status == self.DRAFT:
            if settings.PAGE_SHOW_START_DATE:
                if (self.publication_date and
                        self.publication_date <= datetime.now()):
                    self.publication_date = None
            else:
                self.publication_date = None
        self.last_modification_date = datetime.now()
        # let's assume there is no more broken links after a save
        cache.delete(self.PAGE_BROKEN_LINK_KEY % self.id)
        super(Page, self).save(*args, **kwargs)
        # fix sites many-to-many link when the're hidden from the form
        if settings.PAGE_HIDE_SITES and self.sites.count() == 0:
            self.sites.add(Site.objects.get(pk=settings.SITE_ID))

    def _get_calculated_status(self):
        """Get the calculated status of the page based on
        :attr:`Page.publication_date`,
        :attr:`Page.publication_end_date`,
        and :attr:`Page.status`."""
        if settings.PAGE_SHOW_START_DATE and self.publication_date:
            if self.publication_date > datetime.now():
                return self.DRAFT

        if settings.PAGE_SHOW_END_DATE and self.publication_end_date:
            if self.publication_end_date < datetime.now():
                return self.EXPIRED

        return self.status
    calculated_status = property(_get_calculated_status)

    def get_children_for_frontend(self):
        """Return a :class:`QuerySet` of published children page"""
        return Page.objects.filter_published(self.get_children())

    def get_date_ordered_children_for_frontend(self):
        """Return a :class:`QuerySet` of published children page ordered
        by publication date."""
        return self.get_children_for_frontend().order_by('-publication_date')

    def invalidate(self):
        """Invalidate cached data for this page."""

        cache.delete(self.PAGE_LANGUAGES_KEY % (self.id))
        self._languages = None
        self._complete_slug = None
        self._content_dict = dict()

        p_names = [p.name for p in get_placeholders(self.get_template())]
        if 'slug' not in p_names:
            p_names.append('slug')
        if 'title' not in p_names:
            p_names.append('title')
        # delete content cache, frozen or not
        for name in p_names:
            # frozen
            cache.delete(PAGE_CONTENT_DICT_KEY %
                (self.id, name, 1))
            # not frozen
            cache.delete(PAGE_CONTENT_DICT_KEY %
                (self.id, name, 0))

        for lang in settings.PAGE_LANGUAGES:
            cache.delete(self.PAGE_URL_KEY % (self.id, lang[0]))
        cache.delete(self.PAGE_URL_KEY % (self.id, "None"))

    def get_languages(self):
        """
        Return a list of all used languages for this page.
        """
        if self._languages:
            return self._languages
        self._languages = cache.get(self.PAGE_LANGUAGES_KEY % (self.id))
        if self._languages:
            return self._languages

        languages = [c['language'] for
                            c in Content.objects.filter(page=self,
                            type="slug").values('language')]
        languages = list(set(languages)) # remove duplicates
        languages.sort()
        cache.set(self.PAGE_LANGUAGES_KEY % (self.id), languages)
        self._languages = languages
        return languages

    def is_first_root(self):
        """Return ``True`` if the page is the first root page."""
        if self.parent:
            return False
        return Page.objects.root()[0].id == self.id

    def get_url_path(self, language=None):
        """Return the URL's path component. Add the language prefix if
        ``PAGE_USE_LANGUAGE_PREFIX`` setting is set to ``True``.

        :param language: the wanted url language.
        """
        url = reverse('pages-root')
        if settings.PAGE_USE_LANGUAGE_PREFIX:
            url += str(language) + '/'
        return url + self.get_complete_slug(language)

    def get_absolute_url(self, language=None):
        """Alias for `get_url_path`.

        This method is only there for backward compatibility and will be
        removed in a near futur.

        :param language: the wanted url language.
        """
        return self.get_url_path(language=language)

    def get_complete_slug(self, language=None):
        """Return the complete slug of this page by concatenating
        all parent's slugs.

        :param language: the wanted slug language."""
        if self._complete_slug:
            return self._complete_slug
        self._complete_slug = cache.get(self.PAGE_URL_KEY %
            (self.id, language))
        if self._complete_slug:
            return self._complete_slug
        if settings.PAGE_HIDE_ROOT_SLUG and self.is_first_root():
            url = ''
        else:
            url = u'%s' % self.slug(language)
        for ancestor in self.get_ancestors(ascending=True):
            url = ancestor.slug(language) + u'/' + url

        cache.set(self.PAGE_URL_KEY % (self.id, language), url)
        self._complete_slug = url
        return url

    def get_url(self, language=None):
        """Alias for `get_complete_slug`.

        This method is only there for backward compatibility and will be
        removed in a near futur.

        :param language: the wanted url language.
        """
        return self.get_complete_slug(language=language)

    def slug(self, language=None, fallback=True):
        """
        Return the slug of the page depending on the given language.

        :param language: wanted language, if not defined default is used.
        :param fallback: if ``True``, the slug will also be searched in other \
        languages.
        """

        slug = self.get_content(language, 'slug', language_fallback=fallback)

        return slug

    def title(self, language=None, fallback=True):
        """
        Return the title of the page depending on the given language.

        :param language: wanted language, if not defined default is used.
        :param fallback: if ``True``, the slug will also be searched in \
        other languages.
        """
        if not language:
            language = settings.PAGE_DEFAULT_LANGUAGE

        return self.get_content(language, 'title', language_fallback=fallback)

    def get_content(self, language, ctype, language_fallback=False):
        """Shortcut method for retrieving a piece of page content

        :param language: wanted language, if not defined default is used.
        :param ctype: the type of content.
        :param fallback: if ``True``, the content will also be searched in \
        other languages.
        """
        return Content.objects.get_content(self, language, ctype,
            language_fallback)

    def expose_content(self):
        """Return all the current content of this page into a `string`.

        This is used by the haystack framework to build the search index."""
        placeholders = get_placeholders(self.get_template())
        exposed_content = []
        for lang in self.get_languages():
            for p_name in [p.name for p in placeholders]:
                content = self.get_content(lang, p_name, False)
                if content:
                    exposed_content.append(content)
        return u"\r\n".join(exposed_content)

    def get_template(self):
        """
        Get the :attr:`template <Page.template>` of this page if
        defined or the closer parent's one if defined
        or :attr:`pages.settings.PAGE_DEFAULT_TEMPLATE` otherwise.
        """
        if self.template:
            return self.template

        template = None
        for p in self.get_ancestors(ascending=True):
            if p.template:
                template = p.template
                break

        if not template:
            template = settings.PAGE_DEFAULT_TEMPLATE

        return template

    def get_template_name(self):
        """
        Get the template name of this page if defined or if a closer
        parent has a defined template or
        :data:`pages.settings.PAGE_DEFAULT_TEMPLATE otherwise.
        """
        template = self.get_template()
        page_templates = settings.get_page_templates()
        for t in page_templates:
            if t[0] == template:
                return t[1]
        return template

    def has_broken_link(self):
        """
        Return ``True`` if the page have broken links to other pages
        into the content.
        """
        return cache.get(self.PAGE_BROKEN_LINK_KEY % self.id)

    def valid_targets(self):
        """Return a :class:`QuerySet` of valid targets for moving a page
        into the tree.

        :param perms: the level of permission of the concerned user.
        """
        exclude_list = [self.id]
        for p in self.get_descendants():
            exclude_list.append(p.id)
        return Page.objects.exclude(id__in=exclude_list)

    def slug_with_level(self, language=None):
        """Display the slug of the page prepended with insecable
        spaces equal to the level of page in the hierarchy."""
        level = ''
        if self.level:
            for n in range(0, self.level):
                level += '   '
        return mark_safe(level + self.slug(language))

    def margin_level(self):
        """Used in the admin menu to create the left margin."""
        return self.level * 2

    def __unicode__(self):
        """Representation of the page, saved or not."""
        if self.id:
            slug = self.slug()
            if slug:
                return slug
            return "Page %d" % self.id
        return "Page without id"

# Don't register the Page model twice.
try:
    mptt.register(Page)
except mptt.AlreadyRegistered:
    pass

class Content(models.Model):
    """A block of content, tied to a :class:`Page <pages.models.Page>`,
    for a particular language"""

    # languages could have five characters : Brazilian Portuguese is pt-br
    language = models.CharField(_('language'), max_length=5, blank=False)
    body = models.TextField(_('body'))
    type = models.CharField(_('type'), max_length=100, blank=False,
        db_index=True)
    page = models.ForeignKey(Page, verbose_name=_('page'))

    creation_date = models.DateTimeField(_('creation date'), editable=False,
        default=datetime.now)
    objects = ContentManager()

    class Meta:
        get_latest_by = 'creation_date'
        verbose_name = _('content')
        verbose_name_plural = _('contents')

    def __unicode__(self):
        return "%s :: %s" % (self.page.slug(), self.body[0:15])

class PageAlias(models.Model):
    """URL alias for a :class:`Page <pages.models.Page>`"""
    page = models.ForeignKey(Page, null=True, blank=True,
        verbose_name=_('page'))
    url = models.CharField(max_length=255, unique=True)
    objects = PageAliasManager()
    class Meta:
        verbose_name_plural = _('Aliases')

    def save(self, *args, **kwargs):
        # normalize url
        self.url = normalize_url(self.url)
        super(PageAlias, self).save(*args, **kwargs)

    def __unicode__(self):
        return "%s => %s" % (self.url, self.page.get_complete_slug())

<commit_msg>Fix minor typo in doc<commit_after>"""Django page CMS ``models``."""

from pages.utils import get_placeholders, normalize_url
from pages.managers import PageManager, ContentManager
from pages.managers import PageAliasManager
from pages import settings

from datetime import datetime
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.utils.safestring import mark_safe
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.contrib.sites.models import Site

import mptt

PAGE_CONTENT_DICT_KEY = ContentManager.PAGE_CONTENT_DICT_KEY

class Page(models.Model):
    """
    This model contain the status, dates, author, template.
    The real content of the page can be found in the
    :class:`Content <pages.models.Content>` model.

    .. attribute:: creation_date
       When the page has been created.

    .. attribute:: publication_date
       When the page should be visible.

    .. attribute:: publication_end_date
       When the publication of this page end.

    .. attribute:: last_modification_date
       Last time this page has been modified.

    .. attribute:: status
       The current status of the page. Could be DRAFT, PUBLISHED,
       EXPIRED or HIDDEN. You should the property :attr:`calculated_status` if
       you want that the dates are taken in account.

    .. attribute:: template
       A string containing the name of the template file for this page.
    """

    # some class constants to refer to, e.g. Page.DRAFT
    DRAFT = 0
    PUBLISHED = 1
    EXPIRED = 2
    HIDDEN = 3
    STATUSES = (
        (PUBLISHED, _('Published')),
        (HIDDEN, _('Hidden')),
        (DRAFT, _('Draft')),
    )

    PAGE_LANGUAGES_KEY = "page_%d_languages"
    PAGE_URL_KEY = "page_%d_language_%s_url"
    PAGE_BROKEN_LINK_KEY = "page_broken_link_%s"

    author = models.ForeignKey(User, verbose_name=_('author'))

    parent = models.ForeignKey('self', null=True, blank=True,
            related_name='children', verbose_name=_('parent'))
    creation_date = models.DateTimeField(_('creation date'), editable=False,
            default=datetime.now)
    publication_date = models.DateTimeField(_('publication date'),
            null=True, blank=True, help_text=_('''When the page should go
            live. Status must be "Published" for page to go live.'''))
    publication_end_date = models.DateTimeField(_('publication end date'),
            null=True, blank=True, help_text=_('''When to expire the page.
            Leave empty to never expire.'''))

    last_modification_date = models.DateTimeField(_('last modification date'))

    status = models.IntegerField(_('status'), choices=STATUSES, default=DRAFT)
    template = models.CharField(_('template'), max_length=100, null=True,
            blank=True)

    delegate_to = models.CharField(_('delegate to'), max_length=100, null=True,
            blank=True)

    freeze_date = models.DateTimeField(_('freeze date'),
            null=True, blank=True, help_text=_('''Don't publish any content
            after this date.'''))

    if settings.PAGE_USE_SITE_ID:
        sites = models.ManyToManyField(Site, default=[settings.SITE_ID],
                help_text=_('The site(s) the page is accessible at.'),
                verbose_name=_('sites'))

    redirect_to_url = models.CharField(max_length=200, null=True, blank=True)

    redirect_to = models.ForeignKey('self', null=True, blank=True,
            related_name='redirected_pages')

    # Managers
    objects = PageManager()

    if settings.PAGE_TAGGING:
        from tagging import fields
        tags = fields.TagField(null=True)

    # per instance cache
    _languages = None
    _complete_slug = None
    _content_dict = None

    class Meta:
        """Make sure the default page ordering is correct."""
        ordering = ['tree_id', 'lft']
        get_latest_by = "publication_date"
        verbose_name = _('page')
        verbose_name_plural = _('pages')
        permissions = settings.PAGE_EXTRA_PERMISSIONS

    def save(self, *args, **kwargs):
        """Override the default ``save`` method."""
        if not self.status:
            self.status = self.DRAFT
        # Published pages should always have a publication date
        if self.publication_date is None and self.status == self.PUBLISHED:
            self.publication_date = datetime.now()
        # Drafts should not, unless they have been set to the future
        if self.status == self.DRAFT:
            if settings.PAGE_SHOW_START_DATE:
                if (self.publication_date and
                        self.publication_date <= datetime.now()):
                    self.publication_date = None
            else:
                self.publication_date = None
        self.last_modification_date = datetime.now()
        # let's assume there is no more broken links after a save
        cache.delete(self.PAGE_BROKEN_LINK_KEY % self.id)
        super(Page, self).save(*args, **kwargs)
        # fix sites many-to-many link when the're hidden from the form
        if settings.PAGE_HIDE_SITES and self.sites.count() == 0:
            self.sites.add(Site.objects.get(pk=settings.SITE_ID))

    def _get_calculated_status(self):
        """Get the calculated status of the page based on
        :attr:`Page.publication_date`,
        :attr:`Page.publication_end_date`,
        and :attr:`Page.status`."""
        if settings.PAGE_SHOW_START_DATE and self.publication_date:
            if self.publication_date > datetime.now():
                return self.DRAFT

        if settings.PAGE_SHOW_END_DATE and self.publication_end_date:
            if self.publication_end_date < datetime.now():
                return self.EXPIRED

        return self.status
    calculated_status = property(_get_calculated_status)

    def get_children_for_frontend(self):
        """Return a :class:`QuerySet` of published children page"""
        return Page.objects.filter_published(self.get_children())

    def get_date_ordered_children_for_frontend(self):
        """Return a :class:`QuerySet` of published children page ordered
        by publication date."""
        return self.get_children_for_frontend().order_by('-publication_date')

    def invalidate(self):
        """Invalidate cached data for this page."""

        cache.delete(self.PAGE_LANGUAGES_KEY % (self.id))
        self._languages = None
        self._complete_slug = None
        self._content_dict = dict()

        p_names = [p.name for p in get_placeholders(self.get_template())]
        if 'slug' not in p_names:
            p_names.append('slug')
        if 'title' not in p_names:
            p_names.append('title')
        # delete content cache, frozen or not
        for name in p_names:
            # frozen
            cache.delete(PAGE_CONTENT_DICT_KEY %
                (self.id, name, 1))
            # not frozen
            cache.delete(PAGE_CONTENT_DICT_KEY %
                (self.id, name, 0))

        for lang in settings.PAGE_LANGUAGES:
            cache.delete(self.PAGE_URL_KEY % (self.id, lang[0]))
        cache.delete(self.PAGE_URL_KEY % (self.id, "None"))

    def get_languages(self):
        """
        Return a list of all used languages for this page.
        """
        if self._languages:
            return self._languages
        self._languages = cache.get(self.PAGE_LANGUAGES_KEY % (self.id))
        if self._languages:
            return self._languages

        languages = [c['language'] for
                            c in Content.objects.filter(page=self,
                            type="slug").values('language')]
        languages = list(set(languages)) # remove duplicates
        languages.sort()
        cache.set(self.PAGE_LANGUAGES_KEY % (self.id), languages)
        self._languages = languages
        return languages

    def is_first_root(self):
        """Return ``True`` if the page is the first root page."""
        if self.parent:
            return False
        return Page.objects.root()[0].id == self.id

    def get_url_path(self, language=None):
        """Return the URL's path component. Add the language prefix if
        ``PAGE_USE_LANGUAGE_PREFIX`` setting is set to ``True``.

        :param language: the wanted url language.
        """
        url = reverse('pages-root')
        if settings.PAGE_USE_LANGUAGE_PREFIX:
            url += str(language) + '/'
        return url + self.get_complete_slug(language)

    def get_absolute_url(self, language=None):
        """Alias for `get_url_path`.

        This method is only there for backward compatibility and will be
        removed in a near futur.

        :param language: the wanted url language.
        """
        return self.get_url_path(language=language)

    def get_complete_slug(self, language=None):
        """Return the complete slug of this page by concatenating
        all parent's slugs.

        :param language: the wanted slug language."""
        if self._complete_slug:
            return self._complete_slug
        self._complete_slug = cache.get(self.PAGE_URL_KEY %
            (self.id, language))
        if self._complete_slug:
            return self._complete_slug
        if settings.PAGE_HIDE_ROOT_SLUG and self.is_first_root():
            url = ''
        else:
            url = u'%s' % self.slug(language)
        for ancestor in self.get_ancestors(ascending=True):
            url = ancestor.slug(language) + u'/' + url

        cache.set(self.PAGE_URL_KEY % (self.id, language), url)
        self._complete_slug = url
        return url

    def get_url(self, language=None):
        """Alias for `get_complete_slug`.

        This method is only there for backward compatibility and will be
        removed in a near futur.

        :param language: the wanted url language.
        """
        return self.get_complete_slug(language=language)

    def slug(self, language=None, fallback=True):
        """
        Return the slug of the page depending on the given language.

        :param language: wanted language, if not defined default is used.
        :param fallback: if ``True``, the slug will also be searched in other \
        languages.
        """

        slug = self.get_content(language, 'slug', language_fallback=fallback)

        return slug

    def title(self, language=None, fallback=True):
        """
        Return the title of the page depending on the given language.

        :param language: wanted language, if not defined default is used.
        :param fallback: if ``True``, the slug will also be searched in \
        other languages.
        """
        if not language:
            language = settings.PAGE_DEFAULT_LANGUAGE

        return self.get_content(language, 'title', language_fallback=fallback)

    def get_content(self, language, ctype, language_fallback=False):
        """Shortcut method for retrieving a piece of page content

        :param language: wanted language, if not defined default is used.
        :param ctype: the type of content.
        :param fallback: if ``True``, the content will also be searched in \
        other languages.
        """
        return Content.objects.get_content(self, language, ctype,
            language_fallback)

    def expose_content(self):
        """Return all the current content of this page into a `string`.

        This is used by the haystack framework to build the search index."""
        placeholders = get_placeholders(self.get_template())
        exposed_content = []
        for lang in self.get_languages():
            for p_name in [p.name for p in placeholders]:
                content = self.get_content(lang, p_name, False)
                if content:
                    exposed_content.append(content)
        return u"\r\n".join(exposed_content)

    def get_template(self):
        """
        Get the :attr:`template <Page.template>` of this page if
        defined or the closer parent's one if defined
        or :attr:`pages.settings.PAGE_DEFAULT_TEMPLATE` otherwise.
        """
        if self.template:
            return self.template

        template = None
        for p in self.get_ancestors(ascending=True):
            if p.template:
                template = p.template
                break

        if not template:
            template = settings.PAGE_DEFAULT_TEMPLATE

        return template

    def get_template_name(self):
        """
        Get the template name of this page if defined or if a closer
        parent has a defined template or
        :data:`pages.settings.PAGE_DEFAULT_TEMPLATE` otherwise.
        """
        template = self.get_template()
        page_templates = settings.get_page_templates()
        for t in page_templates:
            if t[0] == template:
                return t[1]
        return template

    def has_broken_link(self):
        """
        Return ``True`` if the page have broken links to other pages
        into the content.
        """
        return cache.get(self.PAGE_BROKEN_LINK_KEY % self.id)

    def valid_targets(self):
        """Return a :class:`QuerySet` of valid targets for moving a page
        into the tree.

        :param perms: the level of permission of the concerned user.
        """
        exclude_list = [self.id]
        for p in self.get_descendants():
            exclude_list.append(p.id)
        return Page.objects.exclude(id__in=exclude_list)

    def slug_with_level(self, language=None):
        """Display the slug of the page prepended with insecable
        spaces equal to the level of page in the hierarchy."""
        level = ''
        if self.level:
            for n in range(0, self.level):
                level += '   '
        return mark_safe(level + self.slug(language))

    def margin_level(self):
        """Used in the admin menu to create the left margin."""
        return self.level * 2

    def __unicode__(self):
        """Representation of the page, saved or not."""
        if self.id:
            slug = self.slug()
            if slug:
                return slug
            return "Page %d" % self.id
        return "Page without id"

# Don't register the Page model twice.
try:
    mptt.register(Page)
except mptt.AlreadyRegistered:
    pass

class Content(models.Model):
    """A block of content, tied to a :class:`Page <pages.models.Page>`,
    for a particular language"""

    # languages could have five characters : Brazilian Portuguese is pt-br
    language = models.CharField(_('language'), max_length=5, blank=False)
    body = models.TextField(_('body'))
    type = models.CharField(_('type'), max_length=100, blank=False,
        db_index=True)
    page = models.ForeignKey(Page, verbose_name=_('page'))

    creation_date = models.DateTimeField(_('creation date'), editable=False,
        default=datetime.now)
    objects = ContentManager()

    class Meta:
        get_latest_by = 'creation_date'
        verbose_name = _('content')
        verbose_name_plural = _('contents')

    def __unicode__(self):
        return "%s :: %s" % (self.page.slug(), self.body[0:15])

class PageAlias(models.Model):
    """URL alias for a :class:`Page <pages.models.Page>`"""
    page = models.ForeignKey(Page, null=True, blank=True,
        verbose_name=_('page'))
    url = models.CharField(max_length=255, unique=True)
    objects = PageAliasManager()
    class Meta:
        verbose_name_plural = _('Aliases')

    def save(self, *args, **kwargs):
        # normalize url
        self.url = normalize_url(self.url)
        super(PageAlias, self).save(*args, **kwargs)

    def __unicode__(self):
        return "%s => %s" % (self.url, self.page.get_complete_slug())

<|endoftext|>


<commit_before># ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
from __future__ import absolute_import
import copy
import xml.etree.ElementTree as ET
import os
import re
from datetime import datetime
from urllib.parse import urljoin

from svtplay_dl.utils.output import output, progress_stream, ETA, progressbar
from svtplay_dl.error import UIException, ServiceError
from svtplay_dl.fetcher import VideoRetriever

class DASHException(UIException):
    def __init__(self, url, message):
        self.url = url
        super(DASHException, self).__init__(message)

class LiveDASHException(DASHException):
    def __init__(self, url):
        super(LiveDASHException, self).__init__(
            url, "This is a live DASH stream, and they are not supported.")

def templateelemt(element, filename, idnumber, offset_sec, duration_sec):
    files = []
    timescale = 1
    duration = 1
    total = 1

    init = element.attrib["initialization"]
    media = element.attrib["media"]
    if "startNumber" in element.attrib:
        start = int(element.attrib["startNumber"])
    else:
        start = 1

    if "timescale" in element.attrib:
        timescale = float(element.attrib["timescale"])

    if "duration" in element.attrib:
        duration = float(element.attrib["duration"])

    if offset_sec is not None and duration_sec is None:
        start += int(offset_sec / (duration / timescale))

    if duration_sec is not None:
        total = int(duration_sec / (duration / timescale))

    selements = None
    rvalue = None
    timeline = element.find("{urn:mpeg:dash:schema:mpd:2011}SegmentTimeline")
    if timeline is not None:

        rvalue = timeline.findall(".//{urn:mpeg:dash:schema:mpd:2011}S[@r]")
        selements = timeline.findall(".//{urn:mpeg:dash:schema:mpd:2011}S")
        selements.pop()

        if rvalue:
            total = int(rvalue[0].attrib["r"]) + len(selements) + 1

    name = media.replace("$RepresentationID$", idnumber)
    files.append(urljoin(filename, init.replace("$RepresentationID$", idnumber)))

    if "$Time$" in media:
        time = [0]
        for n in selements:
            time.append(int(n.attrib["d"]))
        match = re.search("\$Time\$", name)
        if rvalue and match and len(selements) < 3:
            for n in range(start, start + total):
                new = name.replace("$Time$", str(n * int(rvalue[0].attrib["d"])))
                files.append(urljoin(filename, new))
        else:
            number = 0
            for n in time:
                number += n
                new = name.replace("$Time$", str(number))
                files.append(urljoin(filename, new))
    if "$Number" in name:
        if re.search("\$Number(\%\d+)d\$", name):
            vname = name.replace("$Number", "").replace("$", "")
            for n in range(start, start + total):
                files.append(urljoin(filename, vname % n))
        else:
            for n in range(start, start + total):
                newname = name.replace("$Number$", str(n))
                files.append(urljoin(filename, newname))
    return files

def adaptionset(element, url, baseurl=None, offset_sec=None, duration_sec=None):
    streams = {}

    dirname = os.path.dirname(url) + "/"
    if baseurl:
        dirname = urljoin(dirname, baseurl)

    template = element[0].find("{urn:mpeg:dash:schema:mpd:2011}SegmentTemplate")
    represtation = element[0].findall(".//{urn:mpeg:dash:schema:mpd:2011}Representation")

    for i in represtation:
        files = []
        segments = False
        filename = dirname
        bitrate = int(i.attrib["bandwidth"]) / 1000
        idnumber = i.attrib["id"]

        if i.find("{urn:mpeg:dash:schema:mpd:2011}BaseURL") is not None:
            filename = urljoin(filename, i.find("{urn:mpeg:dash:schema:mpd:2011}BaseURL").text)

        if i.find("{urn:mpeg:dash:schema:mpd:2011}SegmentBase") is not None:
            segments = True
            files.append(filename)
        if template is not None:
            segments = True
            files = templateelemt(template, filename, idnumber, offset_sec, duration_sec)
        elif i.find("{urn:mpeg:dash:schema:mpd:2011}SegmentTemplate") is not None:
            segments = True
            files = templateelemt(i.find("{urn:mpeg:dash:schema:mpd:2011}SegmentTemplate"), filename, idnumber, offset_sec, duration_sec)

        if files:
            streams[bitrate] = {"segments": segments, "files": files}

    return streams

def dashparse(config, res, url, output=None):
    streams = {}
    if not res:
        return streams

    if res.status_code >= 400:
        streams[0] = ServiceError("Can't read DASH playlist. {0}".format(res.status_code))
        return streams
    if len(res.text) < 1:
        streams[0] = ServiceError("Can't read DASH playlist. {0}, size: {1}".format(res.status_code, len(res.text)))
        return streams

    return _dashparse(config, res.text, url, output, res.cookies)

def _dashparse(config, text, url, output, cookies):
    streams = {}
    baseurl = None
    offset_sec = None
    duration_sec = None

    xml = ET.XML(text)

    if xml.find("./{urn:mpeg:dash:schema:mpd:2011}BaseURL") is not None:
        baseurl = xml.find("./{urn:mpeg:dash:schema:mpd:2011}BaseURL").text

    if "availabilityStartTime" in xml.attrib:
        availabilityStartTime = xml.attrib["availabilityStartTime"]
        publishTime = xml.attrib["publishTime"]

        datetime_start = parse_dates(availabilityStartTime)
        datetime_publish = parse_dates(publishTime)
        diff_publish = datetime_publish - datetime_start
        offset_sec = diff_publish.total_seconds()

        if "mediaPresentationDuration" in xml.attrib:
            mediaPresentationDuration = xml.attrib["mediaPresentationDuration"]
            duration_sec = (parse_dates(mediaPresentationDuration) - datetime(1900, 1, 1)).total_seconds()

    temp = xml.findall('.//{urn:mpeg:dash:schema:mpd:2011}AdaptationSet[@mimeType="audio/mp4"]')
    audiofiles = adaptionset(temp, url, baseurl, offset_sec, duration_sec)
    temp = xml.findall('.//{urn:mpeg:dash:schema:mpd:2011}AdaptationSet[@mimeType="video/mp4"]')
    videofiles = adaptionset(temp, url, baseurl, offset_sec, duration_sec)

    if not audiofiles or not videofiles:
        streams[0] = ServiceError("Found no Audiofiles or Videofiles to download.")
        return streams

    for i in videofiles.keys():
        bitrate = i + list(audiofiles.keys())[0]
        streams[bitrate] = DASH(copy.copy(config), url, bitrate, cookies=cookies,
                                audio=audiofiles[list(audiofiles.keys())[0]]["files"], files=videofiles[i]["files"],
                                output=output, segments=videofiles[i]["segments"])

    return streams

def parse_dates(date_str):
    date_patterns = ["%Y-%m-%dT%H:%M:%S.%fZ", "PT%HH%MM%S.%fS", "PT%HH%MM%SS", "PT%MM%S.%fS", "PT%MM%SS", "PT%HH%SS", "PT%HH%S.%fS"]
    dt = None
    for pattern in date_patterns:
        try:
            dt = datetime.strptime(date_str, pattern)
            break
        except Exception:
            pass
    if not dt:
        raise ValueError("Can't parse date format: {0}".format(date_str))

    return dt

class DASH(VideoRetriever):
    @property
    def name(self):
        return "dash"

    def download(self):
        self.output_extention = "mp4"
        if self.config.get("live") and not self.config.get("force"):
            raise LiveDASHException(self.url)

        if self.segments:
            if self.audio:
                self._download2(self.audio, audio=True)
            self._download2(self.files)
        else:
            if self.audio:
                self._download_url(self.audio, audio=True)
            self._download_url(self.url)

    def _download2(self, files, audio=False):
        cookies = self.kwargs["cookies"]

        if audio:
            file_d = output(copy.copy(self.output), self.config, extension="m4a")
        else:
            file_d = output(self.output, self.config, extension="mp4")
        if file_d is None:
            return
        eta = ETA(len(files))
        n = 1
        for i in files:
            if not self.config.get("silent"):
                eta.increment()
                progressbar(len(files), n, ''.join(['ETA: ', str(eta)]))
                n += 1
            data = self.http.request("get", i, cookies=cookies)

            if data.status_code == 404:
                break
            data = data.content
            file_d.write(data)

        file_d.close()
        if not self.config.get("silent"):
            progress_stream.write('\n')
        self.finished = True
<commit_msg>dash.parse_dates: add a new time of date format..<commit_after># ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
from __future__ import absolute_import
import copy
import xml.etree.ElementTree as ET
import os
import re
from datetime import datetime
from urllib.parse import urljoin

from svtplay_dl.utils.output import output, progress_stream, ETA, progressbar
from svtplay_dl.error import UIException, ServiceError
from svtplay_dl.fetcher import VideoRetriever

class DASHException(UIException):
    def __init__(self, url, message):
        self.url = url
        super(DASHException, self).__init__(message)

class LiveDASHException(DASHException):
    def __init__(self, url):
        super(LiveDASHException, self).__init__(
            url, "This is a live DASH stream, and they are not supported.")

def templateelemt(element, filename, idnumber, offset_sec, duration_sec):
    files = []
    timescale = 1
    duration = 1
    total = 1

    init = element.attrib["initialization"]
    media = element.attrib["media"]
    if "startNumber" in element.attrib:
        start = int(element.attrib["startNumber"])
    else:
        start = 1

    if "timescale" in element.attrib:
        timescale = float(element.attrib["timescale"])

    if "duration" in element.attrib:
        duration = float(element.attrib["duration"])

    if offset_sec is not None and duration_sec is None:
        start += int(offset_sec / (duration / timescale))

    if duration_sec is not None:
        total = int(duration_sec / (duration / timescale))

    selements = None
    rvalue = None
    timeline = element.find("{urn:mpeg:dash:schema:mpd:2011}SegmentTimeline")
    if timeline is not None:

        rvalue = timeline.findall(".//{urn:mpeg:dash:schema:mpd:2011}S[@r]")
        selements = timeline.findall(".//{urn:mpeg:dash:schema:mpd:2011}S")
        selements.pop()

        if rvalue:
            total = int(rvalue[0].attrib["r"]) + len(selements) + 1

    name = media.replace("$RepresentationID$", idnumber)
    files.append(urljoin(filename, init.replace("$RepresentationID$", idnumber)))

    if "$Time$" in media:
        time = [0]
        for n in selements:
            time.append(int(n.attrib["d"]))
        match = re.search("\$Time\$", name)
        if rvalue and match and len(selements) < 3:
            for n in range(start, start + total):
                new = name.replace("$Time$", str(n * int(rvalue[0].attrib["d"])))
                files.append(urljoin(filename, new))
        else:
            number = 0
            for n in time:
                number += n
                new = name.replace("$Time$", str(number))
                files.append(urljoin(filename, new))
    if "$Number" in name:
        if re.search("\$Number(\%\d+)d\$", name):
            vname = name.replace("$Number", "").replace("$", "")
            for n in range(start, start + total):
                files.append(urljoin(filename, vname % n))
        else:
            for n in range(start, start + total):
                newname = name.replace("$Number$", str(n))
                files.append(urljoin(filename, newname))
    return files

def adaptionset(element, url, baseurl=None, offset_sec=None, duration_sec=None):
    streams = {}

    dirname = os.path.dirname(url) + "/"
    if baseurl:
        dirname = urljoin(dirname, baseurl)

    template = element[0].find("{urn:mpeg:dash:schema:mpd:2011}SegmentTemplate")
    represtation = element[0].findall(".//{urn:mpeg:dash:schema:mpd:2011}Representation")

    for i in represtation:
        files = []
        segments = False
        filename = dirname
        bitrate = int(i.attrib["bandwidth"]) / 1000
        idnumber = i.attrib["id"]

        if i.find("{urn:mpeg:dash:schema:mpd:2011}BaseURL") is not None:
            filename = urljoin(filename, i.find("{urn:mpeg:dash:schema:mpd:2011}BaseURL").text)

        if i.find("{urn:mpeg:dash:schema:mpd:2011}SegmentBase") is not None:
            segments = True
            files.append(filename)
        if template is not None:
            segments = True
            files = templateelemt(template, filename, idnumber, offset_sec, duration_sec)
        elif i.find("{urn:mpeg:dash:schema:mpd:2011}SegmentTemplate") is not None:
            segments = True
            files = templateelemt(i.find("{urn:mpeg:dash:schema:mpd:2011}SegmentTemplate"), filename, idnumber, offset_sec, duration_sec)

        if files:
            streams[bitrate] = {"segments": segments, "files": files}

    return streams

def dashparse(config, res, url, output=None):
    streams = {}
    if not res:
        return streams

    if res.status_code >= 400:
        streams[0] = ServiceError("Can't read DASH playlist. {0}".format(res.status_code))
        return streams
    if len(res.text) < 1:
        streams[0] = ServiceError("Can't read DASH playlist. {0}, size: {1}".format(res.status_code, len(res.text)))
        return streams

    return _dashparse(config, res.text, url, output, res.cookies)

def _dashparse(config, text, url, output, cookies):
    streams = {}
    baseurl = None
    offset_sec = None
    duration_sec = None

    xml = ET.XML(text)

    if xml.find("./{urn:mpeg:dash:schema:mpd:2011}BaseURL") is not None:
        baseurl = xml.find("./{urn:mpeg:dash:schema:mpd:2011}BaseURL").text

    if "availabilityStartTime" in xml.attrib:
        availabilityStartTime = xml.attrib["availabilityStartTime"]
        publishTime = xml.attrib["publishTime"]

        datetime_start = parse_dates(availabilityStartTime)
        datetime_publish = parse_dates(publishTime)
        diff_publish = datetime_publish - datetime_start
        offset_sec = diff_publish.total_seconds()

        if "mediaPresentationDuration" in xml.attrib:
            mediaPresentationDuration = xml.attrib["mediaPresentationDuration"]
            duration_sec = (parse_dates(mediaPresentationDuration) - datetime(1900, 1, 1)).total_seconds()

    temp = xml.findall('.//{urn:mpeg:dash:schema:mpd:2011}AdaptationSet[@mimeType="audio/mp4"]')
    audiofiles = adaptionset(temp, url, baseurl, offset_sec, duration_sec)
    temp = xml.findall('.//{urn:mpeg:dash:schema:mpd:2011}AdaptationSet[@mimeType="video/mp4"]')
    videofiles = adaptionset(temp, url, baseurl, offset_sec, duration_sec)

    if not audiofiles or not videofiles:
        streams[0] = ServiceError("Found no Audiofiles or Videofiles to download.")
        return streams

    for i in videofiles.keys():
        bitrate = i + list(audiofiles.keys())[0]
        streams[bitrate] = DASH(copy.copy(config), url, bitrate, cookies=cookies,
                                audio=audiofiles[list(audiofiles.keys())[0]]["files"], files=videofiles[i]["files"],
                                output=output, segments=videofiles[i]["segments"])

    return streams

def parse_dates(date_str):
    date_patterns = ["%Y-%m-%dT%H:%M:%S.%fZ", "%Y-%m-%dT%H:%M:%S", "PT%HH%MM%S.%fS",
                     "PT%HH%MM%SS", "PT%MM%S.%fS", "PT%MM%SS", "PT%HH%SS", "PT%HH%S.%fS"]
    dt = None
    for pattern in date_patterns:
        try:
            dt = datetime.strptime(date_str, pattern)
            break
        except Exception:
            pass
    if not dt:
        raise ValueError("Can't parse date format: {0}".format(date_str))

    return dt

class DASH(VideoRetriever):
    @property
    def name(self):
        return "dash"

    def download(self):
        self.output_extention = "mp4"
        if self.config.get("live") and not self.config.get("force"):
            raise LiveDASHException(self.url)

        if self.segments:
            if self.audio:
                self._download2(self.audio, audio=True)
            self._download2(self.files)
        else:
            if self.audio:
                self._download_url(self.audio, audio=True)
            self._download_url(self.url)

    def _download2(self, files, audio=False):
        cookies = self.kwargs["cookies"]

        if audio:
            file_d = output(copy.copy(self.output), self.config, extension="m4a")
        else:
            file_d = output(self.output, self.config, extension="mp4")
        if file_d is None:
            return
        eta = ETA(len(files))
        n = 1
        for i in files:
            if not self.config.get("silent"):
                eta.increment()
                progressbar(len(files), n, ''.join(['ETA: ', str(eta)]))
                n += 1
            data = self.http.request("get", i, cookies=cookies)

            if data.status_code == 404:
                break
            data = data.content
            file_d.write(data)

        file_d.close()
        if not self.config.get("silent"):
            progress_stream.write('\n')
        self.finished = True
<|endoftext|>


<commit_before>

from django import forms as django_forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from horizon import forms, messages
from django.utils.encoding import smart_text

try:
    from crispy_forms.helper import FormHelper
    from crispy_forms.layout import Div, Layout, HTML
    from crispy_forms.bootstrap import Tab, TabHolder
    CRISPY = True
except Exception as e:
    CRISPY = False

class DateForm(forms.Form):

    """A simple form for selecting a range of time."""
    start = forms.DateField(input_formats=("%Y-%m-%d", "%d.%m.%Y"))
    end = forms.DateField(input_formats=("%Y-%m-%d", "%d.%m.%Y"))

    def __init__(self, *args, **kwargs):
        super(DateForm, self).__init__(*args, **kwargs)
        self.fields['start'].widget.attrs['data-date-format'] = "dd-mm-yyyy"
        self.fields['end'].widget.attrs['data-date-format'] = "dd-mm-yyyy"

class SelfHandlingMixin(object):

    """A base :class:`Form <django:django.forms.Form>` class which includes
    processing logic in its subclasses.

        tabs = {
            'tab1': {
                'name': 'Verbose Name'
                'fields': ('field_name1',)
            }
        }

        help_text = "Select user and role."

    """
    required_css_class = 'required'

    def __init__(self, request=None, *args, **kwargs):
        self.request = request
        if not hasattr(self, "handle"):
            raise NotImplementedError("%s does not define a handle method."
                                      % self.__class__.__name__)
        super(SelfHandlingMixin, self).__init__(*args, **kwargs)

        # crispy layout
        if CRISPY and not hasattr(self, 'helper'):
            self.helper = FormHelper(self)
            self.helper.field_class = ""
            self.helper.form_tag = False
            self.helper.label_class = "control-label"

            if hasattr(self, 'help_text'):
                # for helptext make two columns
                self.helper[0:len(self.helper.layout.fields)].wrap_together(
                    Div, css_class="col-lg-6 field-wrapper")
                self.helper.layout.append(
                    HTML("
%s
" % self.help_text))
            else:
                # classes added only if not hidden
                for item in self.helper.layout.fields:
                    if item in ["object_id", "id"]:
                        continue
                    try:
                        self.helper[item].wrap(
                            Div, css_class="col-lg-6 field-wrapper")
                    except Exception:
                        pass

    def init_layout(self):
        '''Call init for generic layout'''
        self.helper.layout = Layout(TabHolder())
        self.init_custom_tabs()

    def get_tabs(self):
        '''Merge form tabs with models tabs'''
        tabs = getattr(self._meta.model, 'tabs', {})
        tabs.update(getattr(self, 'tabs', {}))
        return tabs

    def init_custom_tabs(self):
        '''init custom tabs
        tabs = {
            'tab1': {
                'name': 'Verbose Name'
                'fields': ('field_name1',)
            }
        }
        '''
        for tab_name, tab in self.get_tabs().items():
            self.insert_tab(tab.get('name', tab_name), tab['fields'])

    def get_main_fields(self, fields):
        '''filter field which are included in custom tab'''
        _fields = []
        for field in fields:
            if field not in self._custom_fields():
                _fields.append(field)
        return _fields

    def _custom_fields(self):
        '''returns acumulated fields from ``tabs``'''
        fields = []
        if not hasattr(self, '__custom_fields'):
            for tab_name, tab in self.get_tabs().items():
                fields += list(tab['fields'])
            self.__custom_fields = fields
        return self.__custom_fields

    def insert_tab(self, name, fields, position=1):
        '''Push tab to specific position
        in the default state is after main widget tab
        '''
        self.helper.layout[0].insert(
            position,
            Tab(name,
                *fields
                )
        )

class SelfHandlingForm(SelfHandlingMixin, django_forms.Form):

    """modal form with default crispy layout
    """

    pass

class SelfHandlingModelForm(SelfHandlingMixin, django_forms.ModelForm):

    """form with implemented handle method
    """

    def handle(self, request, data):

        instance = self.save()

        messages.success(
            request,
            _(smart_text("Model %s was successfuly saved." % instance)))

        if hasattr(self, 'handle_related_models'):
            # handle related models
            self.handle_related_models(self.request, instance)

        return instance
<commit_msg>fix encoding<commit_after>

from django import forms as django_forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from horizon import forms, messages
from django.utils.encoding import smart_text

try:
    from crispy_forms.helper import FormHelper
    from crispy_forms.layout import Div, Layout, HTML
    from crispy_forms.bootstrap import Tab, TabHolder
    CRISPY = True
except Exception as e:
    CRISPY = False

class DateForm(forms.Form):

    """A simple form for selecting a range of time."""
    start = forms.DateField(input_formats=("%Y-%m-%d", "%d.%m.%Y"))
    end = forms.DateField(input_formats=("%Y-%m-%d", "%d.%m.%Y"))

    def __init__(self, *args, **kwargs):
        super(DateForm, self).__init__(*args, **kwargs)
        self.fields['start'].widget.attrs['data-date-format'] = "dd-mm-yyyy"
        self.fields['end'].widget.attrs['data-date-format'] = "dd-mm-yyyy"

class SelfHandlingMixin(object):

    """A base :class:`Form <django:django.forms.Form>` class which includes
    processing logic in its subclasses.

        tabs = {
            'tab1': {
                'name': 'Verbose Name'
                'fields': ('field_name1',)
            }
        }

        help_text = "Select user and role."

    """
    required_css_class = 'required'

    def __init__(self, request=None, *args, **kwargs):
        self.request = request
        if not hasattr(self, "handle"):
            raise NotImplementedError("%s does not define a handle method."
                                      % self.__class__.__name__)
        super(SelfHandlingMixin, self).__init__(*args, **kwargs)

        # crispy layout
        if CRISPY and not hasattr(self, 'helper'):
            self.helper = FormHelper(self)
            self.helper.field_class = ""
            self.helper.form_tag = False
            self.helper.label_class = "control-label"

            if hasattr(self, 'help_text'):
                # for helptext make two columns
                self.helper[0:len(self.helper.layout.fields)].wrap_together(
                    Div, css_class="col-lg-6 field-wrapper")
                self.helper.layout.append(
                    HTML(smart_text("
%s
" % self.help_text)))
            else:
                # classes added only if not hidden
                for item in self.helper.layout.fields:
                    if item in ["object_id", "id"]:
                        continue
                    try:
                        self.helper[item].wrap(
                            Div, css_class="col-lg-6 field-wrapper")
                    except Exception:
                        pass

    def init_layout(self):
        '''Call init for generic layout'''
        self.helper.layout = Layout(TabHolder())
        self.init_custom_tabs()

    def get_tabs(self):
        '''Merge form tabs with models tabs'''
        tabs = getattr(self._meta.model, 'tabs', {})
        tabs.update(getattr(self, 'tabs', {}))
        return tabs

    def init_custom_tabs(self):
        '''init custom tabs
        tabs = {
            'tab1': {
                'name': 'Verbose Name'
                'fields': ('field_name1',)
            }
        }
        '''
        for tab_name, tab in self.get_tabs().items():
            self.insert_tab(tab.get('name', tab_name), tab['fields'])

    def get_main_fields(self, fields):
        '''filter field which are included in custom tab'''
        _fields = []
        for field in fields:
            if field not in self._custom_fields():
                _fields.append(field)
        return _fields

    def _custom_fields(self):
        '''returns acumulated fields from ``tabs``'''
        fields = []
        if not hasattr(self, '__custom_fields'):
            for tab_name, tab in self.get_tabs().items():
                fields += list(tab['fields'])
            self.__custom_fields = fields
        return self.__custom_fields

    def insert_tab(self, name, fields, position=1):
        '''Push tab to specific position
        in the default state is after main widget tab
        '''
        self.helper.layout[0].insert(
            position,
            Tab(name,
                *fields
                )
        )

class SelfHandlingForm(SelfHandlingMixin, django_forms.Form):

    """modal form with default crispy layout
    """

    pass

class SelfHandlingModelForm(SelfHandlingMixin, django_forms.ModelForm):

    """form with implemented handle method
    """

    def handle(self, request, data):

        instance = self.save()

        messages.success(
            request,
            _(smart_text("Model %s was successfuly saved." % instance)))

        if hasattr(self, 'handle_related_models'):
            # handle related models
            self.handle_related_models(self.request, instance)

        return instance
<|endoftext|>