<commit_before># SPDX-FileCopyrightText: 2022 James R. Barlow, 2020 Matthias Erll

# SPDX-License-Identifier: MPL-2.0

from __future__ import annotations

from enum import Enum
from itertools import chain
from typing import Iterable, List, cast

from pikepdf import Array, Dictionary, Name, Object, Page, Pdf, String

class PageLocation(Enum):
    XYZ = 1
    Fit = 2
    FitH = 3
    FitV = 4
    FitR = 5
    FitB = 6
    FitBH = 7
    FitBV = 8

PAGE_LOCATION_ARGS = {
    PageLocation.XYZ: ('left', 'top', 'zoom'),
    PageLocation.FitH: ('top',),
    PageLocation.FitV: ('left',),
    PageLocation.FitR: ('left', 'bottom', 'right', 'top'),
    PageLocation.FitBH: ('top',),
    PageLocation.FitBV: ('left',),
}
ALL_PAGE_LOCATION_KWARGS = set(chain.from_iterable(PAGE_LOCATION_ARGS.values()))

def make_page_destination(
    pdf: Pdf,
    page_num: int,
    page_location: PageLocation | str | None = None,
    *,
    left: float | None = None,
    top: float | None = None,
    right: float | None = None,
    bottom: float | None = None,
    zoom: float | None = None,
) -> Array:
    """
    Creates a destination ``Array`` with reference to a Pdf document's page number.

    Arguments:
        pdf: PDF document object.
        page_num: Page number (zero-based).
        page_location: Optional page location, as a string or :enum:`PageLocation`.
        left, top, right, bottom, zoom: Optional keyword arguments for specifying
            a position on the page, used in conjunction with the page fit style
            specified by *page_location*.
    """
    return _make_page_destination(
        pdf,
        page_num,
        page_location=page_location,
        left=left,
        top=top,
        right=right,
        bottom=bottom,
        zoom=zoom,
    )

def _make_page_destination(
    pdf: Pdf,
    page_num: int,
    page_location: PageLocation | str | None = None,
    **kwargs,
) -> Array:
    kwargs = {k: v for k, v in kwargs.items() if v is not None}

    res: list[Dictionary | Name] = [pdf.pages[page_num].obj]
    if page_location:
        if isinstance(page_location, PageLocation):
            loc_key = page_location
            loc_str = loc_key.name
        else:
            loc_str = page_location
            try:
                loc_key = PageLocation[loc_str]
            except KeyError:
                raise ValueError(
                    f"Invalid or unsupported page location type {loc_str}"
                ) from None
        res.append(Name(f'/{loc_str}'))
        dest_arg_names = PAGE_LOCATION_ARGS.get(loc_key)
        if dest_arg_names:
            res.extend(kwargs.get(k, 0) for k in dest_arg_names)
    else:
        res.append(Name.Fit)
    return Array(res)

class OutlineStructureError(Exception):
    pass

class OutlineItem:
    """Manages a single item in a PDF document outlines structure, including
    nested items.

    Arguments:
        title: Title of the outlines item.
        destination: Page number, destination name, or any other PDF object
            to be used as a reference when clicking on the outlines entry. Note
            this should be ``None`` if an action is used instead. If set to a
            page number, it will be resolved to a reference at the time of
            writing the outlines back to the document.
        page_location: Supplemental page location for a page number
            in ``destination``, e.g. ``PageLocation.Fit``. May also be
            a simple string such as ``'FitH'``.
        action: Action to perform when clicking on this item. Will be ignored
           during writing if ``destination`` is also set.
        obj: ``Dictionary`` object representing this outlines item in a ``Pdf``.
            May be ``None`` for creating a new object. If present, an existing
            object is modified in-place during writing and original attributes
            are retained.
        left, top, bottom, right, zoom: Describes the viewport position associated
            with a destination.

    This object does not contain any information about higher-level or
    neighboring elements.

    Valid destination arrays:
        [page /XYZ left top zoom]
        generally
        [page, PageLocationEntry, 0 to 4 ints]
    """

    def __init__(
        self,
        title: str,
        destination: Array | String | Name | int | None = None,
        page_location: PageLocation | str | None = None,
        action: Dictionary | None = None,
        obj: Dictionary | None = None,
        *,
        left: float | None = None,
        top: float | None = None,
        right: float | None = None,
        bottom: float | None = None,
        zoom: float | None = None,
    ):
        self.title = title
        self.destination = destination
        self.page_location = page_location
        self.page_location_kwargs = {}
        self.action = action
        if self.destination is not None and self.action is not None:
            raise ValueError("Only one of destination and action may be set")
        self.obj = obj
        kwargs = dict(left=left, top=top, right=right, bottom=bottom, zoom=zoom)
        self.page_location_kwargs = {k: v for k, v in kwargs.items() if v is not None}
        self.is_closed = False
        self.children: list[OutlineItem] = []

    def __str__(self):
        if self.children:
            if self.is_closed:
                oc_indicator = '[+]'
            else:
                oc_indicator = '[-]'
        else:
            oc_indicator = '[ ]'
        if self.destination is not None:
            if isinstance(self.destination, Array):
                # 12.3.2.2 Explicit destination
                # [raw_page, /PageLocation.SomeThing, integer parameters for viewport]
                raw_page = self.destination[0]
                page = Page(raw_page)
                dest = page.label
            elif isinstance(self.destination, String):
                # 12.3.2.2 Named destination, byte string reference to Names
                dest = f'<Named Destination in document .Root.Names dictionary: {self.destination}>'
            elif isinstance(self.destination, Name):
                # 12.3.2.2 Named desintation, name object (PDF 1.1)
                dest = f'<Named Destination in document .Root.Dests dictionary: {self.destination}>'
            elif isinstance(self.destination, int):
                # Page number
                dest = f'<Page {self.destination}>'
        else:
            dest = '<Action>'
        return f'{oc_indicator} {self.title} -> {dest}'

    def __repr__(self):
        return f'<pikepdf.{self.__class__.__name__}: "{self.title}">'

    @classmethod
    def from_dictionary_object(cls, obj: Dictionary):
        """Creates a ``OutlineItem`` from a PDF document's ``Dictionary``
        object. Does not process nested items.

        Arguments:
            obj: ``Dictionary`` object representing a single outline node.
        """
        title = str(obj.Title)
        destination = obj.get(Name.Dest)
        if destination is not None and not isinstance(
            destination, (Array, String, Name)
        ):
            # 12.3.3: /Dest may be a name, byte string or array
            raise OutlineStructureError(
                f"Unexpected object type in Outline's /Dest: {destination!r}"
            )
        action = obj.get(Name.A)
        if action is not None and not isinstance(action, Dictionary):
            raise OutlineStructureError(
                f"Unexpected object type in Outline's /A: {action!r}"
            )
        return cls(title, destination=destination, action=action, obj=obj)

    def to_dictionary_object(self, pdf: Pdf, create_new: bool = False) -> Dictionary:
        """Creates a ``Dictionary`` object from this outline node's data,
        or updates the existing object.
        Page numbers are resolved to a page reference on the input
        ``Pdf`` object.

        Arguments:
            pdf: PDF document object.
            create_new: If set to ``True``, creates a new object instead of
                modifying an existing one in-place.
        """
        if create_new or self.obj is None:
            self.obj = obj = pdf.make_indirect(Dictionary())
        else:
            obj = self.obj
        obj.Title = self.title
        if self.destination is not None:
            if isinstance(self.destination, int):
                self.destination = make_page_destination(
                    pdf,
                    self.destination,
                    self.page_location,
                    **self.page_location_kwargs,
                )
            obj.Dest = self.destination
            if Name.A in obj:
                del obj.A
        elif self.action is not None:
            obj.A = self.action
            if Name.Dest in obj:
                del obj.Dest
        return obj

class Outline:
    """Maintains a intuitive interface for creating and editing PDF document outlines,
    according to the |pdfrm| section 12.3.

    Arguments:
        pdf: PDF document object.
        max_depth: Maximum recursion depth to consider when reading the outline.
        strict: If set to ``False`` (default) silently ignores structural errors.
            Setting it to ``True`` raises a
            :class:`pikepdf.OutlineStructureError`
            if any object references re-occur while the outline is being read or
            written.

    See Also:
        :meth:`pikepdf.Pdf.open_outline`
    """

    def __init__(self, pdf: Pdf, max_depth: int = 15, strict: bool = False):
        self._root: list[OutlineItem] | None = None
        self._pdf = pdf
        self._max_depth = max_depth
        self._strict = strict
        self._updating = False

    def __str__(self):
        return str(self.root)

    def __repr__(self):
        return f'<pikepdf.{self.__class__.__name__}: {len(self.root)} items>'

    def __enter__(self):
        self._updating = True
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            if exc_type is not None:
                return
            self._save()
        finally:
            self._updating = False

    def _save_level_outline(
        self,
        parent: Dictionary,
        outline_items: Iterable[OutlineItem],
        level: int,
        visited_objs: set[tuple[int, int]],
    ):
        count = 0
        prev: Dictionary | None = None
        first: Dictionary | None = None
        for item in outline_items:
            out_obj = item.to_dictionary_object(self._pdf)
            objgen = out_obj.objgen
            if objgen in visited_objs:
                if self._strict:
                    raise OutlineStructureError(
                        f"Outline object {objgen} reoccurred in structure"
                    )
                out_obj = item.to_dictionary_object(self._pdf, create_new=True)
            else:
                visited_objs.add(objgen)

            out_obj.Parent = parent
            count += 1
            if prev is not None:
                prev.Next = out_obj
                out_obj.Prev = prev
            else:
                first = out_obj
                if Name.Prev in out_obj:
                    del out_obj.Prev
            prev = out_obj
            if level < self._max_depth:
                sub_items: Iterable[OutlineItem] = item.children
            else:
                sub_items = ()
            self._save_level_outline(out_obj, sub_items, level + 1, visited_objs)
            if item.is_closed:
                out_obj.Count = -cast(int, out_obj.Count)
            else:
                count += cast(int, out_obj.Count)
        if count:
            assert prev is not None and first is not None
            if Name.Next in prev:
                del prev.Next
            parent.First = first
            parent.Last = prev
        else:
            if Name.First in parent:
                del parent.First
            if Name.Last in parent:
                del parent.Last
        parent.Count = count

    def _load_level_outline(
        self,
        first_obj: Dictionary,
        outline_items: list[Object],
        level: int,
        visited_objs: set[tuple[int, int]],
    ):
        current_obj: Dictionary | None = first_obj
        while current_obj:
            objgen = current_obj.objgen
            if objgen in visited_objs:
                if self._strict:
                    raise OutlineStructureError(
                        f"Outline object {objgen} reoccurred in structure"
                    )
                return
            visited_objs.add(objgen)

            item = OutlineItem.from_dictionary_object(current_obj)
            first_child = current_obj.get(Name.First)
            if isinstance(first_child, Dictionary) and level < self._max_depth:
                self._load_level_outline(
                    first_child, item.children, level + 1, visited_objs
                )
                count = current_obj.get(Name.Count)
                if isinstance(count, int) and count < 0:
                    item.is_closed = True
            outline_items.append(item)
            next_obj = current_obj.get(Name.Next)
            if next_obj is None or isinstance(next_obj, Dictionary):
                current_obj = next_obj
            else:
                raise OutlineStructureError(
                    f"Outline object {objgen} points to non-dictionary"
                )

    def _save(self):
        if self._root is None:
            return
        if Name.Outlines in self._pdf.Root:
            outlines = self._pdf.Root.Outlines
        else:
            self._pdf.Root.Outlines = outlines = self._pdf.make_indirect(
                Dictionary(Type=Name.Outlines)
            )
        self._save_level_outline(outlines, self._root, 0, set())

    def _load(self):
        self._root = root = []
        if Name.Outlines not in self._pdf.Root:
            return
        outlines = self._pdf.Root.Outlines or {}
        first_obj = outlines.get(Name.First)
        if first_obj:
            self._load_level_outline(first_obj, root, 0, set())

    @property
    def root(self) -> list[OutlineItem]:
        if self._root is None:
            self._load()
        return cast(List[OutlineItem], self._root)
<commit_msg>Fix typo in src/pikepdf/models/outlines.py<commit_after># SPDX-FileCopyrightText: 2022 James R. Barlow, 2020 Matthias Erll

# SPDX-License-Identifier: MPL-2.0

from __future__ import annotations

from enum import Enum
from itertools import chain
from typing import Iterable, List, cast

from pikepdf import Array, Dictionary, Name, Object, Page, Pdf, String

class PageLocation(Enum):
    XYZ = 1
    Fit = 2
    FitH = 3
    FitV = 4
    FitR = 5
    FitB = 6
    FitBH = 7
    FitBV = 8

PAGE_LOCATION_ARGS = {
    PageLocation.XYZ: ('left', 'top', 'zoom'),
    PageLocation.FitH: ('top',),
    PageLocation.FitV: ('left',),
    PageLocation.FitR: ('left', 'bottom', 'right', 'top'),
    PageLocation.FitBH: ('top',),
    PageLocation.FitBV: ('left',),
}
ALL_PAGE_LOCATION_KWARGS = set(chain.from_iterable(PAGE_LOCATION_ARGS.values()))

def make_page_destination(
    pdf: Pdf,
    page_num: int,
    page_location: PageLocation | str | None = None,
    *,
    left: float | None = None,
    top: float | None = None,
    right: float | None = None,
    bottom: float | None = None,
    zoom: float | None = None,
) -> Array:
    """
    Creates a destination ``Array`` with reference to a Pdf document's page number.

    Arguments:
        pdf: PDF document object.
        page_num: Page number (zero-based).
        page_location: Optional page location, as a string or :enum:`PageLocation`.
        left, top, right, bottom, zoom: Optional keyword arguments for specifying
            a position on the page, used in conjunction with the page fit style
            specified by *page_location*.
    """
    return _make_page_destination(
        pdf,
        page_num,
        page_location=page_location,
        left=left,
        top=top,
        right=right,
        bottom=bottom,
        zoom=zoom,
    )

def _make_page_destination(
    pdf: Pdf,
    page_num: int,
    page_location: PageLocation | str | None = None,
    **kwargs,
) -> Array:
    kwargs = {k: v for k, v in kwargs.items() if v is not None}

    res: list[Dictionary | Name] = [pdf.pages[page_num].obj]
    if page_location:
        if isinstance(page_location, PageLocation):
            loc_key = page_location
            loc_str = loc_key.name
        else:
            loc_str = page_location
            try:
                loc_key = PageLocation[loc_str]
            except KeyError:
                raise ValueError(
                    f"Invalid or unsupported page location type {loc_str}"
                ) from None
        res.append(Name(f'/{loc_str}'))
        dest_arg_names = PAGE_LOCATION_ARGS.get(loc_key)
        if dest_arg_names:
            res.extend(kwargs.get(k, 0) for k in dest_arg_names)
    else:
        res.append(Name.Fit)
    return Array(res)

class OutlineStructureError(Exception):
    pass

class OutlineItem:
    """Manages a single item in a PDF document outlines structure, including
    nested items.

    Arguments:
        title: Title of the outlines item.
        destination: Page number, destination name, or any other PDF object
            to be used as a reference when clicking on the outlines entry. Note
            this should be ``None`` if an action is used instead. If set to a
            page number, it will be resolved to a reference at the time of
            writing the outlines back to the document.
        page_location: Supplemental page location for a page number
            in ``destination``, e.g. ``PageLocation.Fit``. May also be
            a simple string such as ``'FitH'``.
        action: Action to perform when clicking on this item. Will be ignored
           during writing if ``destination`` is also set.
        obj: ``Dictionary`` object representing this outlines item in a ``Pdf``.
            May be ``None`` for creating a new object. If present, an existing
            object is modified in-place during writing and original attributes
            are retained.
        left, top, bottom, right, zoom: Describes the viewport position associated
            with a destination.

    This object does not contain any information about higher-level or
    neighboring elements.

    Valid destination arrays:
        [page /XYZ left top zoom]
        generally
        [page, PageLocationEntry, 0 to 4 ints]
    """

    def __init__(
        self,
        title: str,
        destination: Array | String | Name | int | None = None,
        page_location: PageLocation | str | None = None,
        action: Dictionary | None = None,
        obj: Dictionary | None = None,
        *,
        left: float | None = None,
        top: float | None = None,
        right: float | None = None,
        bottom: float | None = None,
        zoom: float | None = None,
    ):
        self.title = title
        self.destination = destination
        self.page_location = page_location
        self.page_location_kwargs = {}
        self.action = action
        if self.destination is not None and self.action is not None:
            raise ValueError("Only one of destination and action may be set")
        self.obj = obj
        kwargs = dict(left=left, top=top, right=right, bottom=bottom, zoom=zoom)
        self.page_location_kwargs = {k: v for k, v in kwargs.items() if v is not None}
        self.is_closed = False
        self.children: list[OutlineItem] = []

    def __str__(self):
        if self.children:
            if self.is_closed:
                oc_indicator = '[+]'
            else:
                oc_indicator = '[-]'
        else:
            oc_indicator = '[ ]'
        if self.destination is not None:
            if isinstance(self.destination, Array):
                # 12.3.2.2 Explicit destination
                # [raw_page, /PageLocation.SomeThing, integer parameters for viewport]
                raw_page = self.destination[0]
                page = Page(raw_page)
                dest = page.label
            elif isinstance(self.destination, String):
                # 12.3.2.2 Named destination, byte string reference to Names
                dest = f'<Named Destination in document .Root.Names dictionary: {self.destination}>'
            elif isinstance(self.destination, Name):
                # 12.3.2.2 Named destination, name object (PDF 1.1)
                dest = f'<Named Destination in document .Root.Dests dictionary: {self.destination}>'
            elif isinstance(self.destination, int):
                # Page number
                dest = f'<Page {self.destination}>'
        else:
            dest = '<Action>'
        return f'{oc_indicator} {self.title} -> {dest}'

    def __repr__(self):
        return f'<pikepdf.{self.__class__.__name__}: "{self.title}">'

    @classmethod
    def from_dictionary_object(cls, obj: Dictionary):
        """Creates a ``OutlineItem`` from a PDF document's ``Dictionary``
        object. Does not process nested items.

        Arguments:
            obj: ``Dictionary`` object representing a single outline node.
        """
        title = str(obj.Title)
        destination = obj.get(Name.Dest)
        if destination is not None and not isinstance(
            destination, (Array, String, Name)
        ):
            # 12.3.3: /Dest may be a name, byte string or array
            raise OutlineStructureError(
                f"Unexpected object type in Outline's /Dest: {destination!r}"
            )
        action = obj.get(Name.A)
        if action is not None and not isinstance(action, Dictionary):
            raise OutlineStructureError(
                f"Unexpected object type in Outline's /A: {action!r}"
            )
        return cls(title, destination=destination, action=action, obj=obj)

    def to_dictionary_object(self, pdf: Pdf, create_new: bool = False) -> Dictionary:
        """Creates a ``Dictionary`` object from this outline node's data,
        or updates the existing object.
        Page numbers are resolved to a page reference on the input
        ``Pdf`` object.

        Arguments:
            pdf: PDF document object.
            create_new: If set to ``True``, creates a new object instead of
                modifying an existing one in-place.
        """
        if create_new or self.obj is None:
            self.obj = obj = pdf.make_indirect(Dictionary())
        else:
            obj = self.obj
        obj.Title = self.title
        if self.destination is not None:
            if isinstance(self.destination, int):
                self.destination = make_page_destination(
                    pdf,
                    self.destination,
                    self.page_location,
                    **self.page_location_kwargs,
                )
            obj.Dest = self.destination
            if Name.A in obj:
                del obj.A
        elif self.action is not None:
            obj.A = self.action
            if Name.Dest in obj:
                del obj.Dest
        return obj

class Outline:
    """Maintains a intuitive interface for creating and editing PDF document outlines,
    according to the |pdfrm| section 12.3.

    Arguments:
        pdf: PDF document object.
        max_depth: Maximum recursion depth to consider when reading the outline.
        strict: If set to ``False`` (default) silently ignores structural errors.
            Setting it to ``True`` raises a
            :class:`pikepdf.OutlineStructureError`
            if any object references re-occur while the outline is being read or
            written.

    See Also:
        :meth:`pikepdf.Pdf.open_outline`
    """

    def __init__(self, pdf: Pdf, max_depth: int = 15, strict: bool = False):
        self._root: list[OutlineItem] | None = None
        self._pdf = pdf
        self._max_depth = max_depth
        self._strict = strict
        self._updating = False

    def __str__(self):
        return str(self.root)

    def __repr__(self):
        return f'<pikepdf.{self.__class__.__name__}: {len(self.root)} items>'

    def __enter__(self):
        self._updating = True
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            if exc_type is not None:
                return
            self._save()
        finally:
            self._updating = False

    def _save_level_outline(
        self,
        parent: Dictionary,
        outline_items: Iterable[OutlineItem],
        level: int,
        visited_objs: set[tuple[int, int]],
    ):
        count = 0
        prev: Dictionary | None = None
        first: Dictionary | None = None
        for item in outline_items:
            out_obj = item.to_dictionary_object(self._pdf)
            objgen = out_obj.objgen
            if objgen in visited_objs:
                if self._strict:
                    raise OutlineStructureError(
                        f"Outline object {objgen} reoccurred in structure"
                    )
                out_obj = item.to_dictionary_object(self._pdf, create_new=True)
            else:
                visited_objs.add(objgen)

            out_obj.Parent = parent
            count += 1
            if prev is not None:
                prev.Next = out_obj
                out_obj.Prev = prev
            else:
                first = out_obj
                if Name.Prev in out_obj:
                    del out_obj.Prev
            prev = out_obj
            if level < self._max_depth:
                sub_items: Iterable[OutlineItem] = item.children
            else:
                sub_items = ()
            self._save_level_outline(out_obj, sub_items, level + 1, visited_objs)
            if item.is_closed:
                out_obj.Count = -cast(int, out_obj.Count)
            else:
                count += cast(int, out_obj.Count)
        if count:
            assert prev is not None and first is not None
            if Name.Next in prev:
                del prev.Next
            parent.First = first
            parent.Last = prev
        else:
            if Name.First in parent:
                del parent.First
            if Name.Last in parent:
                del parent.Last
        parent.Count = count

    def _load_level_outline(
        self,
        first_obj: Dictionary,
        outline_items: list[Object],
        level: int,
        visited_objs: set[tuple[int, int]],
    ):
        current_obj: Dictionary | None = first_obj
        while current_obj:
            objgen = current_obj.objgen
            if objgen in visited_objs:
                if self._strict:
                    raise OutlineStructureError(
                        f"Outline object {objgen} reoccurred in structure"
                    )
                return
            visited_objs.add(objgen)

            item = OutlineItem.from_dictionary_object(current_obj)
            first_child = current_obj.get(Name.First)
            if isinstance(first_child, Dictionary) and level < self._max_depth:
                self._load_level_outline(
                    first_child, item.children, level + 1, visited_objs
                )
                count = current_obj.get(Name.Count)
                if isinstance(count, int) and count < 0:
                    item.is_closed = True
            outline_items.append(item)
            next_obj = current_obj.get(Name.Next)
            if next_obj is None or isinstance(next_obj, Dictionary):
                current_obj = next_obj
            else:
                raise OutlineStructureError(
                    f"Outline object {objgen} points to non-dictionary"
                )

    def _save(self):
        if self._root is None:
            return
        if Name.Outlines in self._pdf.Root:
            outlines = self._pdf.Root.Outlines
        else:
            self._pdf.Root.Outlines = outlines = self._pdf.make_indirect(
                Dictionary(Type=Name.Outlines)
            )
        self._save_level_outline(outlines, self._root, 0, set())

    def _load(self):
        self._root = root = []
        if Name.Outlines not in self._pdf.Root:
            return
        outlines = self._pdf.Root.Outlines or {}
        first_obj = outlines.get(Name.First)
        if first_obj:
            self._load_level_outline(first_obj, root, 0, set())

    @property
    def root(self) -> list[OutlineItem]:
        if self._root is None:
            self._load()
        return cast(List[OutlineItem], self._root)
<|endoftext|>


<commit_before>import os
import stat
import tempfile

__all__ = [ 'tempfile' ]

TEMP = '/tmp/kaa-%s' % os.getuid()

if os.path.isdir(TEMP):
    # temp dir is already there, check permissions
    if os.path.islink(TEMP):
        raise IOError('Security Error: %s is a link, aborted')
    if stat.S_IMODE(os.stat(TEMP)[stat.ST_MODE]) != 0700:
        raise IOError('Security Error: %s has wrong permissions, aborted')
    if os.stat(TEMP)[stat.ST_UID] != os.getuid():
        raise IOError('Security Error: %s does not belong to you, aborted')
else:
    os.mkdir(TEMP, 0700)

def tempfile(name, unique=False):
    """
    Return a filename in the secure kaa tmp directory with the given name.
    Name can also be a relative path in the temp directory, directories will
    be created if missing. If unique is set, it will return a unique name based
    on the given name.
    """
    name = os.path.join(TEMP, name)
    if not os.path.isdir(os.path.dirname(name)):
        os.mkdir(os.path.dirname(name))
    if not unique:
        return name
    return tempfile.mktemp(os.path.basename(name), dir=os.path.dirname(name))
<commit_msg>Respect TMPDIR env and don't care about extra flags in the mode like the sticky bit as long as group and others have no read, write and execute rights.<commit_after>import os
import stat
import tempfile

__all__ = [ 'tempfile' ]

TEMP = '/tmp/kaa-%s' % os.getuid()
if os.environ.get('TMPDIR'):
    TEMP = os.path.join(os.environ['TMPDIR'], 'kaa-%s' % os.getuid())

if os.path.isdir(TEMP):
    # temp dir is already there, check permissions
    if os.path.islink(TEMP):
        raise IOError('Security Error: %s is a link, aborted' % TEMP)
    if stat.S_IMODE(os.stat(TEMP)[stat.ST_MODE]) % 01000 != 0700:
        raise IOError('Security Error: %s has wrong permissions, aborted' % TEMP)
    if os.stat(TEMP)[stat.ST_UID] != os.getuid():
        raise IOError('Security Error: %s does not belong to you, aborted' % TEMP)
else:
    os.mkdir(TEMP, 0700)

def tempfile(name, unique=False):
    """
    Return a filename in the secure kaa tmp directory with the given name.
    Name can also be a relative path in the temp directory, directories will
    be created if missing. If unique is set, it will return a unique name based
    on the given name.
    """
    name = os.path.join(TEMP, name)
    if not os.path.isdir(os.path.dirname(name)):
        os.mkdir(os.path.dirname(name))
    if not unique:
        return name
    return tempfile.mktemp(os.path.basename(name), dir=os.path.dirname(name))
<|endoftext|>


<commit_before># The GeoTag-X command line toolkit.
# This module contains the toolkit's entry point.
#
# Copyright (c) 2016 UNITAR/UNOSAT
#
# The MIT License (MIT)
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.
def _init_argparser():
	"""Initializes the toolkit's command line argument parser."""
	from __init__ import __version__
	import argparse

	parser = argparse.ArgumentParser(
		prog="geotagx",
		description="A set of tools that help manage a GeoTag-X server.",
		add_help=False
	)
	options = parser.add_argument_group("OPTIONS")
	options.add_argument("-h", "--help", action="help", help="Display this help and exit.")
	options.add_argument("-v", "--version", action="version", help="Display version information and exit.", version=_version())

	return parser

def _version():
	"""Returns the project's version string."""
	from __init__ import __version__
	return "GeoTag-X Command Line Toolkit v%s, Copyright (C) 2016 UNITAR/UNOSAT." % __version__

def main(argv=None):
	import sys
	import geotagx_builder.core as builder
	import geotagx_sanitizer.sanitizer as sanitizer

	parser = _init_argparser()

	# Initialize each tool's argument parser.
	subparsers = parser.add_subparsers(
		title="COMMANDS",
		metavar="", # Remove the default metavar by setting it to an empty string. Setting it to 'None' uses a default value.
		description="The following is a set of commands you can use to select a specific tool.",
		prog="geotagx"
	)
	parents = [parser]
	initializers = [
		builder._init_argparser,
		sanitizer._init_argparser,
	]
	for initializer in initializers:
		initializer(subparsers, parents)

	arguments = parser.parse_args(sys.argv[1:] if argv is None else argv)

	# Make sure the 'handler' argument is callable (a function or method).
	# Note: the handler is a reference set to a callable object by the tool during
	# the _init_argparser call.
	run = arguments.handler
	if not hasattr(run, "__call__"):
		parser.error("The 'handler' argument is not callable.")

	run(arguments)
	return 0
<commit_msg>Fix imports<commit_after># The GeoTag-X command line toolkit.
# This module contains the toolkit's entry point.
#
# Copyright (c) 2016 UNITAR/UNOSAT
#
# The MIT License (MIT)
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.
def _init_argparser():
	"""Initializes the toolkit's command line argument parser."""
	from __init__ import __version__
	import argparse

	parser = argparse.ArgumentParser(
		prog="geotagx",
		description="A set of tools that help manage a GeoTag-X server.",
		add_help=False
	)
	options = parser.add_argument_group("OPTIONS")
	options.add_argument("-h", "--help", action="help", help="Display this help and exit.")
	options.add_argument("-v", "--version", action="version", help="Display version information and exit.", version=_version())

	return parser

def _version():
	"""Returns the project's version string."""
	from __init__ import __version__
	return "GeoTag-X Command Line Toolkit v%s, Copyright (C) 2016 UNITAR/UNOSAT." % __version__

def main(argv=None):
	import sys
	import geotagx_builder.builder as builder
	import geotagx_sanitizer.sanitizer as sanitizer

	parser = _init_argparser()

	# Initialize each tool's argument parser.
	subparsers = parser.add_subparsers(
		title="COMMANDS",
		metavar="", # Remove the default metavar by setting it to an empty string. Setting it to 'None' uses a default value.
		description="The following is a set of commands you can use to select a specific tool.",
		prog="geotagx"
	)
	parents = [parser]
	initializers = [
		builder._init_argparser,
		sanitizer._init_argparser,
	]
	for initializer in initializers:
		initializer(subparsers, parents)

	arguments = parser.parse_args(sys.argv[1:] if argv is None else argv)

	# Make sure the 'handler' argument is callable (a function or method).
	# Note: the handler is a reference set to a callable object by the tool during
	# the _init_argparser call.
	run = arguments.handler
	if not hasattr(run, "__call__"):
		parser.error("The 'handler' argument is not callable.")

	run(arguments)
	return 0
<|endoftext|>


<commit_before># util.py
# Copyright (C) 2006, 2007, 2008 Michael Bayer mike_mp@zzzcomputing.com
#
# This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

try:
    Set = set
except:
    import sets
    Set = sets.Set

try:
    from cStringIO import StringIO
except:
   from StringIO import StringIO

import weakref, os, time

try:
   import threading
   import thread
except ImportError:
   import dummy_threading as threading
   import dummy_thread as thread

def verify_directory(dir):
   """create and/or verify a filesystem directory."""
   tries = 0
   while not os.access(dir, os.F_OK):
       try:
           tries += 1
           os.makedirs(dir, 0750)
       except:
           if tries > 5:
               raise

class SetLikeDict(dict):
    """a dictionary that has some setlike methods on it"""
    def union(self, other):
        """produce a 'union' of this dict and another (at the key level).

        values in the second dict take precedence over that of the first"""
        x = SetLikeDict(**self)
        x.update(other)
        return x

class FastEncodingBuffer(object):
    """a very rudimentary buffer that is faster than StringIO, but doesnt crash on unicode data like cStringIO."""
    def __init__(self, encoding=None, errors='strict'):
        self.data = []
        self.encoding = encoding
        self.errors = errors
    def write(self, text):
        self.data.append(text)
    def getvalue(self):
        if self.encoding:
            return u''.join(self.data).encode(self.encoding, self.errors)
        else:
            return u''.join(self.data)

class LRUCache(dict):
    """A dictionary-like object that stores a limited number of items, discarding
    lesser used items periodically.

    this is a rewrite of LRUCache from Myghty to use a periodic timestamp-based
    paradigm so that synchronization is not really needed.  the size management
    is inexact.
    """

    class _Item(object):
        def __init__(self, key, value):
            self.key = key
            self.value = value
            self.timestamp = time.time()
        def __repr__(self):
            return repr(self.value)

    def __init__(self, capacity, threshold=.5):
        self.capacity = capacity
        self.threshold = threshold

    def __getitem__(self, key):
        item = dict.__getitem__(self, key)
        item.timestamp = time.time()
        return item.value

    def values(self):
        return [i.value for i in dict.values(self)]

    def setdefault(self, key, value):
        if key in self:
            return self[key]
        else:
            self[key] = value
            return value

    def __setitem__(self, key, value):
        item = dict.get(self, key)
        if item is None:
            item = self._Item(key, value)
            dict.__setitem__(self, key, item)
        else:
            item.value = value
        self._manage_size()

    def _manage_size(self):
        while len(self) > self.capacity + self.capacity * self.threshold:
            bytime = dict.values(self)
            bytime.sort(lambda a, b: cmp(b.timestamp, a.timestamp))
            for item in bytime[self.capacity:]:
                try:
                    del self[item.key]
                except KeyError:
                    # if we couldnt find a key, most likely some other thread broke in
                    # on us. loop around and try again
                    break
<commit_msg>fix indentation for [ticket:70]<commit_after># util.py
# Copyright (C) 2006, 2007, 2008 Michael Bayer mike_mp@zzzcomputing.com
#
# This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

try:
    Set = set
except:
    import sets
    Set = sets.Set

try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO

import weakref, os, time

try:
    import threading
    import thread
except ImportError:
    import dummy_threading as threading
    import dummy_thread as thread

def verify_directory(dir):
    """create and/or verify a filesystem directory."""

    tries = 0

    while not os.access(dir, os.F_OK):
        try:
            tries += 1
            os.makedirs(dir, 0750)
        except:
            if tries > 5:
                raise

class SetLikeDict(dict):
    """a dictionary that has some setlike methods on it"""
    def union(self, other):
        """produce a 'union' of this dict and another (at the key level).

        values in the second dict take precedence over that of the first"""
        x = SetLikeDict(**self)
        x.update(other)
        return x

class FastEncodingBuffer(object):
    """a very rudimentary buffer that is faster than StringIO, but doesnt crash on unicode data like cStringIO."""

    def __init__(self, encoding=None, errors='strict'):
        self.data = []
        self.encoding = encoding
        self.errors = errors

    def write(self, text):
        self.data.append(text)

    def getvalue(self):
        if self.encoding:
            return u''.join(self.data).encode(self.encoding, self.errors)
        else:
            return u''.join(self.data)

class LRUCache(dict):
    """A dictionary-like object that stores a limited number of items, discarding
    lesser used items periodically.

    this is a rewrite of LRUCache from Myghty to use a periodic timestamp-based
    paradigm so that synchronization is not really needed.  the size management
    is inexact.
    """

    class _Item(object):
        def __init__(self, key, value):
            self.key = key
            self.value = value
            self.timestamp = time.time()
        def __repr__(self):
            return repr(self.value)

    def __init__(self, capacity, threshold=.5):
        self.capacity = capacity
        self.threshold = threshold

    def __getitem__(self, key):
        item = dict.__getitem__(self, key)
        item.timestamp = time.time()
        return item.value

    def values(self):
        return [i.value for i in dict.values(self)]

    def setdefault(self, key, value):
        if key in self:
            return self[key]
        else:
            self[key] = value
            return value

    def __setitem__(self, key, value):
        item = dict.get(self, key)
        if item is None:
            item = self._Item(key, value)
            dict.__setitem__(self, key, item)
        else:
            item.value = value
        self._manage_size()

    def _manage_size(self):
        while len(self) > self.capacity + self.capacity * self.threshold:
            bytime = dict.values(self)
            bytime.sort(lambda a, b: cmp(b.timestamp, a.timestamp))
            for item in bytime[self.capacity:]:
                try:
                    del self[item.key]
                except KeyError:
                    # if we couldnt find a key, most likely some other thread broke in
                    # on us. loop around and try again
                    break
<|endoftext|>


<commit_before>from __future__ import division, absolute_import, print_function

from numpy.testing import assert_, run_module_suite

import numpy.distutils.fcompiler

nagfor_version_strings = [('NAG Fortran Compiler Release 6.2(Chiyoda) Build 6200', '6.2')]

nag_version_strings = [('NAGWare Fortran 95 compiler Release 5.1(347,355-367,375,'
                        '380-383,389,394,399,401-402,407,431,435,437,446,459-460,'
                        '463,472,494,496,503,508,511,517,529,555,557,565)', '5.1')]

class TestNagFCompilerVersions(object):
    def test_version_match(self):
        fc = numpy.distutils.fcompiler.new_fcompiler(compiler='nagfor')
        for vs, version in nagfor_version_strings:
            v = fc.version_match(vs)
            assert_(v == version)

        fc = numpy.distutils.fcompiler.new_fcompiler(compiler='nag')
        for vs, version in nag_version_strings:
            v = fc.version_match(vs)
            assert_(v == version)

if __name__ == '__main__':
    run_module_suite()
<commit_msg>TST: Improved tests for nag fortran compiler<commit_after>from __future__ import division, absolute_import, print_function

from numpy.testing import assert_, run_module_suite

import numpy.distutils.fcompiler

nagfor_version_strings = [('nagfor', 'NAG Fortran Compiler Release '
                           '6.2(Chiyoda) Build 6200', '6.2'),
                          ('nagfor', 'NAG Fortran Compiler Release '
                           '6.1(Tozai) Build 6136', '6.1'),
                          ('nagfor', 'NAG Fortran Compiler Release '
                           '6.0(Hibiya) Build 1021', '6.0'),
                          ('nagfor', 'NAG Fortran Compiler Release '
                           '5.3.2(971)', '5.3.2')]

nag_version_strings = [('nag', 'NAGWare Fortran 95 compiler Release 5.1'
                        '(347,355-367,375,380-383,389,394,399,401-402,407,'
                        '431,435,437,446,459-460,463,472,494,496,503,508,'
                        '511,517,529,555,557,565)', '5.1')]

class TestNagFCompilerVersions(object):
    def test_version_match(self):
        for comp, vs, version in nagfor_version_strings:
            fc = numpy.distutils.fcompiler.new_fcompiler(compiler=comp)
            v = fc.version_match(vs)
            assert_(v == version)

if __name__ == '__main__':
    run_module_suite()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Presence analyzer unit tests.
"""
import os.path
import json
import datetime
import unittest

from presence_analyzer import main, views, utils

TEST_DATA_CSV = os.path.join(
    os.path.dirname(__file__), '..', '..', 'runtime', 'data', 'test_data.csv'
)

# pylint: disable=E1103
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
    """
    Views tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': TEST_DATA_CSV})
        self.client = main.app.test_client()

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    def test_mainpage(self):
        """
        Test main page redirect.
        """
        resp = self.client.get('/')
        self.assertEqual(resp.status_code, 302)
        assert resp.headers['Location'].endswith('/presence_weekday.html')

    def test_api_users(self):
        """
        Test users listing.
        """
        resp = self.client.get('/api/v1/users')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 2)
        self.assertDictEqual(data[0], {u'user_id': 10, u'name': u'User 10'})

    def test_mean_time_weekday_view(self):
        """
        Test mean presence time of given user grouped by weekday.
        """
        resp = self.client.get('/api/v1/mean_time_weekday/10')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 7)
        self.assertListEqual(data[0], [u'Mon', 0])
        self.assertListEqual(data[1], [u'Tue', 30047.0])
        self.assertListEqual(data[2], [u'Wed', 24465.0])
        self.assertListEqual(data[3], [u'Thu', 23705.0])
        self.assertListEqual(data[4], [u'Fri', 0])
        self.assertListEqual(data[5], [u'Sat', 0])
        self.assertListEqual(data[6], [u'Sun', 0])

    def test_presence_weekday_view(self):
        """
        Test total presence time of given user grouped by weekday.
        """
        resp = self.client.get('/api/v1/presence_weekday/10')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 8)
        self.assertListEqual(data[0], [u'Weekday', u'Presence (s)'])
        self.assertListEqual(data[1], [u'Mon', 0])
        self.assertListEqual(data[2], [u'Tue', 30047])
        self.assertListEqual(data[3], [u'Wed', 24465])
        self.assertListEqual(data[4], [u'Thu', 23705])
        self.assertListEqual(data[5], [u'Fri', 0])
        self.assertListEqual(data[6], [u'Sat', 0])
        self.assertListEqual(data[7], [u'Sun', 0])

class PresenceAnalyzerUtilsTestCase(unittest.TestCase):
    """
    Utility functions tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': TEST_DATA_CSV})

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    def test_get_data(self):
        """
        Test parsing of CSV file.
        """
        data = utils.get_data()
        self.assertIsInstance(data, dict)
        self.assertItemsEqual(data.keys(), [10, 11])
        sample_date = datetime.date(2013, 9, 10)
        self.assertIn(sample_date, data[10])
        self.assertItemsEqual(data[10][sample_date].keys(), ['start', 'end'])
        self.assertEqual(data[10][sample_date]['start'],
                         datetime.time(9, 39, 5))

    def test_group_by_weekday(self):
        """
        Test grouping items
        """
        data = utils.get_data()
        weekdays = utils.group_by_weekday(data[10])
        self.assertEqual(len(weekdays), 7)

    def test_seconds_since_midnight(self):
        """
        Test calculating time
        """
        data = utils.get_data()
        sample_date = datetime.date(2013, 9, 10)

        start = utils.seconds_since_midnight(data[10][sample_date]['start'])
        self.assertGreaterEqual(start, 0)
        self.assertLess(start, 86400)

        end = utils.seconds_since_midnight(data[10][sample_date]['end'])
        self.assertGreaterEqual(end, 0)
        self.assertLess(end, 86400)

    def test_interval(self):
        """
        Test if interval is returned properly
        """
        data = utils.get_data()
        sample_date = datetime.date(2013, 9, 10)
        start_date = data[10][sample_date]['start']
        end_date = data[10][sample_date]['end']

        self.assertGreaterEqual(utils.interval(start_date, end_date), 0)
        self.assertLess(utils.interval(start_date, end_date), 86400)

    def test_mean(self):
        """
        Test if mean is returned corretly
        """
        data = utils.get_data()
        grouped_data = utils.group_by_weekday(data[10])

        self.assertGreaterEqual(utils.mean(grouped_data), 0)

def suite():
    """
    Default test suite.
    """
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(PresenceAnalyzerViewsTestCase))
    suite.addTest(unittest.makeSuite(PresenceAnalyzerUtilsTestCase))
    return suite

if __name__ == '__main__':
    unittest.main()
<commit_msg>added more hardcoded data<commit_after># -*- coding: utf-8 -*-
"""
Presence analyzer unit tests.
"""
import os.path
import json
import datetime
import unittest

from presence_analyzer import main, views, utils

TEST_DATA_CSV = os.path.join(
    os.path.dirname(__file__), '..', '..', 'runtime', 'data', 'test_data.csv'
)

# pylint: disable=E1103
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
    """
    Views tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': TEST_DATA_CSV})
        self.client = main.app.test_client()

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    def test_mainpage(self):
        """
        Test main page redirect.
        """
        resp = self.client.get('/')
        self.assertEqual(resp.status_code, 302)
        assert resp.headers['Location'].endswith('/presence_weekday.html')

    def test_api_users(self):
        """
        Test users listing.
        """
        resp = self.client.get('/api/v1/users')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 2)
        self.assertDictEqual(data[0], {u'user_id': 10, u'name': u'User 10'})

    def test_mean_time_weekday_view(self):
        """
        Test mean presence time of given user grouped by weekday.
        """
        resp = self.client.get('/api/v1/mean_time_weekday/10')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 7)
        self.assertListEqual(data[0], [u'Mon', 0])
        self.assertListEqual(data[1], [u'Tue', 30047.0])
        self.assertListEqual(data[2], [u'Wed', 24465.0])
        self.assertListEqual(data[3], [u'Thu', 23705.0])
        self.assertListEqual(data[4], [u'Fri', 0])
        self.assertListEqual(data[5], [u'Sat', 0])
        self.assertListEqual(data[6], [u'Sun', 0])

    def test_presence_weekday_view(self):
        """
        Test total presence time of given user grouped by weekday.
        """
        resp = self.client.get('/api/v1/presence_weekday/10')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 8)
        self.assertListEqual(data[0], [u'Weekday', u'Presence (s)'])
        self.assertListEqual(data[1], [u'Mon', 0])
        self.assertListEqual(data[2], [u'Tue', 30047])
        self.assertListEqual(data[3], [u'Wed', 24465])
        self.assertListEqual(data[4], [u'Thu', 23705])
        self.assertListEqual(data[5], [u'Fri', 0])
        self.assertListEqual(data[6], [u'Sat', 0])
        self.assertListEqual(data[7], [u'Sun', 0])

class PresenceAnalyzerUtilsTestCase(unittest.TestCase):
    """
    Utility functions tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': TEST_DATA_CSV})

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    def test_get_data(self):
        """
        Test parsing of CSV file.
        """
        data = utils.get_data()
        self.assertIsInstance(data, dict)
        self.assertItemsEqual(data.keys(), [10, 11])
        sample_date = datetime.date(2013, 9, 10)
        self.assertIn(sample_date, data[10])
        self.assertItemsEqual(data[10][sample_date].keys(), ['start', 'end'])
        self.assertEqual(data[10][sample_date]['start'],
                         datetime.time(9, 39, 5))

    def test_group_by_weekday(self):
        """
        Test grouping items
        """

        data = {
            10: {
                datetime.date(2013, 9, 10): {
                    'start': datetime.time(9, 39, 5),
                    'end': datetime.time(17, 59, 52)},
                datetime.date(2013, 9, 12): {
                    'start': datetime.time(10, 48, 46),
                    'end': datetime.time(17, 23, 51)},
                datetime.date(2013, 9, 11): {
                    'start': datetime.time(9, 19, 52),
                    'end': datetime.time(16, 7, 37)
                }
            }
        }

        weekdays = utils.group_by_weekday(data[10])
        self.assertEqual(len(weekdays), 7)
        self.assertEqual(weekdays[0], [])
        self.assertEqual(weekdays[1], [30047])
        self.assertEqual(weekdays[2], [24465])
        self.assertEqual(weekdays[3], [23705])
        self.assertEqual(weekdays[4], [])
        self.assertEqual(weekdays[5], [])
        self.assertEqual(weekdays[6], [])

    def test_seconds_since_midnight(self):
        """
        Test calculating time
        """
        data = {
            10: {
                datetime.date(2013, 9, 10): {
                    'start': datetime.time(9, 39, 5),
                    'end': datetime.time(17, 59, 52)
                }
            }
        }

        sample_date = datetime.date(2013, 9, 10)

        start = utils.seconds_since_midnight(data[10][sample_date]['start'])
        self.assertGreaterEqual(start, 0)
        self.assertLess(start, 86400)
        self.assertEqual(start, 34745)

        end = utils.seconds_since_midnight(data[10][sample_date]['end'])
        self.assertGreaterEqual(end, 0)
        self.assertLess(end, 86400)
        self.assertEqual(end, 64792)

    def test_interval(self):
        """
        Test if interval is returned properly
        """
        data = {
            10: {
                datetime.date(2013, 9, 10): {
                    'start': datetime.time(9, 39, 5),
                    'end': datetime.time(17, 59, 52)
                }
            }
        }

        sample_date = datetime.date(2013, 9, 10)
        start_date = data[10][sample_date]['start']
        end_date = data[10][sample_date]['end']

        self.assertGreaterEqual(utils.interval(start_date, end_date), 0)
        self.assertLess(utils.interval(start_date, end_date), 86400)
        self.assertEqual(utils.interval(start_date, end_date), 30047)

    def test_mean(self):
        """
        Test if mean is returned corretly
        """
        self.assertEqual(utils.mean([0]), 0)
        self.assertEqual(utils.mean([0, 0]), 0)
        self.assertEqual(utils.mean([300, 400]), 350.0)

        sample_data = [0, 30047, 24465, 23705, 0, 0, 0]
        self.assertEqual(utils.mean(sample_data), 11173.857142857143)

def suite():
    """
    Default test suite.
    """
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(PresenceAnalyzerViewsTestCase))
    suite.addTest(unittest.makeSuite(PresenceAnalyzerUtilsTestCase))
    return suite

if __name__ == '__main__':
    unittest.main()
<|endoftext|>


<commit_before>import sys
import hashlib
import unittest
from nxdrive.utils import guess_mime_type
from nxdrive.utils import guess_digest_algorithm
from nxdrive.manager import ProxySettings

class TestUtils(unittest.TestCase):

    def test_proxy_settings(self):
        proxy = ProxySettings()
        proxy.from_url("localhost:3128")
        self.assertEqual(proxy.username, None)
        self.assertEqual(proxy.password, None)
        self.assertEqual(proxy.authenticated, False)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3128)
        self.assertEqual(proxy.proxy_type, None)
        self.assertEqual(proxy.to_url(), 'localhost:3128')
        self.assertEqual(proxy.to_url(False), 'localhost:3128')
        proxy.from_url("user@localhost:3128")
        self.assertEqual(proxy.username, "user")
        self.assertEqual(proxy.password, None)
        self.assertEqual(proxy.authenticated, False)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3128)
        self.assertEqual(proxy.proxy_type, None)
        self.assertEqual(proxy.to_url(), 'localhost:3128')
        self.assertEqual(proxy.to_url(False), 'localhost:3128')
        proxy.from_url("user:password@localhost:3128")
        self.assertEqual(proxy.username, "user")
        self.assertEqual(proxy.password, "password")
        self.assertEqual(proxy.authenticated, True)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3128)
        self.assertEqual(proxy.proxy_type, None)
        self.assertEqual(proxy.to_url(), 'user:password@localhost:3128')
        self.assertEqual(proxy.to_url(False), 'localhost:3128')
        proxy.from_url("http://user:password@localhost:3128")
        self.assertEqual(proxy.username, "user")
        self.assertEqual(proxy.password, "password")
        self.assertEqual(proxy.authenticated, True)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3128)
        self.assertEqual(proxy.proxy_type, 'http')
        self.assertEqual(proxy.to_url(), 'http://user:password@localhost:3128')
        self.assertEqual(proxy.to_url(False), 'http://localhost:3128')
        proxy.from_url("https://user:password@localhost:3129")
        self.assertEqual(proxy.username, "user")
        self.assertEqual(proxy.password, "password")
        self.assertEqual(proxy.authenticated, True)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3129)
        self.assertEqual(proxy.proxy_type, 'https')
        self.assertEqual(proxy.to_url(), 'https://user:password@localhost:3129')
        self.assertEqual(proxy.to_url(False), 'https://localhost:3129')

    def test_guess_mime_type(self):

        # Text
        self.assertEquals(guess_mime_type('text.txt'), 'text/plain')
        self.assertEquals(guess_mime_type('text.html'), 'text/html')
        self.assertEquals(guess_mime_type('text.css'), 'text/css')
        self.assertEquals(guess_mime_type('text.js'), 'application/javascript')

        # Image
        self.assertEquals(guess_mime_type('picture.jpg'), 'image/jpeg')
        self.assertEquals(guess_mime_type('picture.png'), 'image/png')
        self.assertEquals(guess_mime_type('picture.gif'), 'image/gif')
        self.assertIn(guess_mime_type('picture.bmp'), ['image/x-ms-bmp',
                                                       'image/bmp'])
        self.assertEquals(guess_mime_type('picture.tiff'), 'image/tiff')
        self.assertIn(guess_mime_type('picture.ico'), ['image/x-icon', 'image/vnd.microsoft.icon'])

        # Audio
        self.assertEquals(guess_mime_type('sound.mp3'), 'audio/mpeg')
        self.assertIn(guess_mime_type('sound.wma'), ['audio/x-ms-wma', 'application/octet-stream'])
        self.assertEquals(guess_mime_type('sound.wav'), 'audio/x-wav')

        # Video
        self.assertEquals(guess_mime_type('video.mpeg'), 'video/mpeg')
        self.assertEquals(guess_mime_type('video.mp4'), 'video/mp4')
        self.assertEquals(guess_mime_type('video.mov'), 'video/quicktime')
        self.assertIn(guess_mime_type('video.wmv'), ['video/x-ms-wmv', 'application/octet-stream'])
        self.assertIn(guess_mime_type('video.avi'), ['video/x-msvideo',
                                                     'video/avi'])

        # Office
        self.assertEquals(guess_mime_type('office.doc'),
                          'application/msword')
        self.assertEquals(guess_mime_type('office.xls'),
                          'application/vnd.ms-excel')
        self.assertEquals(guess_mime_type('office.ppt'),
                          'application/vnd.ms-powerpoint')

        # PDF
        self.assertEquals(guess_mime_type('document.pdf'),
                          'application/pdf')

        # Unknown
        self.assertEquals(guess_mime_type('file.unknown'),
                          'application/octet-stream')

        # Cases badly handled by Windows
        # See https://jira.nuxeo.com/browse/NXP-11660
        # and http://bugs.python.org/issue15207
        if sys.platform == "win32":
            # Text
            self.assertEquals(guess_mime_type('text.csv'),
                              'application/octet-stream')
            self.assertEquals(guess_mime_type('text.xml'), 'text/xml')

            # Image
            self.assertIn(guess_mime_type('picture.svg'), ['image/svg+xml', 'application/octet-stream'])

            # Video
            self.assertEquals(guess_mime_type('video.flv'),
                              'application/octet-stream')

            # Office
            self.assertIn(guess_mime_type('office.docx'),
                          ['application/vnd.openxmlformats-officedocument.wordprocessingml.document',
                           'application/octet-stream'])
            self.assertIn(guess_mime_type('office.xlsx'),
                          ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                           'application/octet-stream'])
            self.assertIn(guess_mime_type('office.pptx'),
                          ['application/vnd.openxmlformats-officedocument.presentationml.presentation',
                           'application/x-mspowerpoint.12', 'application/octet-stream'])

            self.assertIn(guess_mime_type('office.odt'),
                          ['application/vnd.oasis.opendocument.text', 'application/octet-stream'])
            self.assertIn(guess_mime_type('office.ods'),
                          ['application/vnd.oasis.opendocument.spreadsheet', 'application/octet-stream'])
            self.assertIn(guess_mime_type('office.odp'),
                          ['application/vnd.oasis.opendocument.presentation', 'application/octet-stream'])
        else:
            # Text
            self.assertEquals(guess_mime_type('text.csv'), 'text/csv')
            self.assertEquals(guess_mime_type('text.xml'), 'application/xml')

            # Image
            self.assertEquals(guess_mime_type('picture.svg'), 'image/svg+xml')

            # Video
            self.assertEquals(guess_mime_type('video.flv'), 'video/x-flv')

            # Office
            self.assertEquals(guess_mime_type('office.docx'),
                              'application/vnd.openxmlformats-officedocument.wordprocessingml.document')
            self.assertEquals(guess_mime_type('office.xlsx'),
                              'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
            self.assertEquals(guess_mime_type('office.pptx'),
                              'application/vnd.openxmlformats-officedocument.presentationml.presentation')

            self.assertEquals(guess_mime_type('office.odt'), 'application/vnd.oasis.opendocument.text')
            self.assertEquals(guess_mime_type('office.ods'), 'application/vnd.oasis.opendocument.spreadsheet')
            self.assertEquals(guess_mime_type('office.odp'), 'application/vnd.oasis.opendocument.presentation')

    def test_guess_digest_algorithm(self):
        s = 'joe'
        md5_digest = hashlib.md5(s).hexdigest()
        self.assertEquals(guess_digest_algorithm(md5_digest), 'md5')
        sha1_digest = hashlib.sha1(s).hexdigest()
        self.assertEquals(guess_digest_algorithm(sha1_digest), 'sha1')
        # For now only md5 and sha1 are supported
        sha256_digest = hashlib.sha256(s).hexdigest()
        try:
            guess_digest_algorithm(sha256_digest)
            self.fail('Other algorithms than md5 and sha1 should not be supported for now')
        except:
            pass
<commit_msg>NXDRIVE-170: Some Windows replay audio/wav for .wav<commit_after>import sys
import hashlib
import unittest
from nxdrive.utils import guess_mime_type
from nxdrive.utils import guess_digest_algorithm
from nxdrive.manager import ProxySettings

class TestUtils(unittest.TestCase):

    def test_proxy_settings(self):
        proxy = ProxySettings()
        proxy.from_url("localhost:3128")
        self.assertEqual(proxy.username, None)
        self.assertEqual(proxy.password, None)
        self.assertEqual(proxy.authenticated, False)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3128)
        self.assertEqual(proxy.proxy_type, None)
        self.assertEqual(proxy.to_url(), 'localhost:3128')
        self.assertEqual(proxy.to_url(False), 'localhost:3128')
        proxy.from_url("user@localhost:3128")
        self.assertEqual(proxy.username, "user")
        self.assertEqual(proxy.password, None)
        self.assertEqual(proxy.authenticated, False)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3128)
        self.assertEqual(proxy.proxy_type, None)
        self.assertEqual(proxy.to_url(), 'localhost:3128')
        self.assertEqual(proxy.to_url(False), 'localhost:3128')
        proxy.from_url("user:password@localhost:3128")
        self.assertEqual(proxy.username, "user")
        self.assertEqual(proxy.password, "password")
        self.assertEqual(proxy.authenticated, True)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3128)
        self.assertEqual(proxy.proxy_type, None)
        self.assertEqual(proxy.to_url(), 'user:password@localhost:3128')
        self.assertEqual(proxy.to_url(False), 'localhost:3128')
        proxy.from_url("http://user:password@localhost:3128")
        self.assertEqual(proxy.username, "user")
        self.assertEqual(proxy.password, "password")
        self.assertEqual(proxy.authenticated, True)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3128)
        self.assertEqual(proxy.proxy_type, 'http')
        self.assertEqual(proxy.to_url(), 'http://user:password@localhost:3128')
        self.assertEqual(proxy.to_url(False), 'http://localhost:3128')
        proxy.from_url("https://user:password@localhost:3129")
        self.assertEqual(proxy.username, "user")
        self.assertEqual(proxy.password, "password")
        self.assertEqual(proxy.authenticated, True)
        self.assertEqual(proxy.server, "localhost")
        self.assertEqual(proxy.port, 3129)
        self.assertEqual(proxy.proxy_type, 'https')
        self.assertEqual(proxy.to_url(), 'https://user:password@localhost:3129')
        self.assertEqual(proxy.to_url(False), 'https://localhost:3129')

    def test_guess_mime_type(self):

        # Text
        self.assertEquals(guess_mime_type('text.txt'), 'text/plain')
        self.assertEquals(guess_mime_type('text.html'), 'text/html')
        self.assertEquals(guess_mime_type('text.css'), 'text/css')
        self.assertEquals(guess_mime_type('text.js'), 'application/javascript')

        # Image
        self.assertEquals(guess_mime_type('picture.jpg'), 'image/jpeg')
        self.assertEquals(guess_mime_type('picture.png'), 'image/png')
        self.assertEquals(guess_mime_type('picture.gif'), 'image/gif')
        self.assertIn(guess_mime_type('picture.bmp'), ['image/x-ms-bmp',
                                                       'image/bmp'])
        self.assertEquals(guess_mime_type('picture.tiff'), 'image/tiff')
        self.assertIn(guess_mime_type('picture.ico'), ['image/x-icon', 'image/vnd.microsoft.icon'])

        # Audio
        self.assertEquals(guess_mime_type('sound.mp3'), 'audio/mpeg')
        self.assertIn(guess_mime_type('sound.wma'), ['audio/x-ms-wma', 'application/octet-stream'])
        self.assertIn(guess_mime_type('sound.wav'), ['audio/x-wav', 'audio/wav'])

        # Video
        self.assertEquals(guess_mime_type('video.mpeg'), 'video/mpeg')
        self.assertEquals(guess_mime_type('video.mp4'), 'video/mp4')
        self.assertEquals(guess_mime_type('video.mov'), 'video/quicktime')
        self.assertIn(guess_mime_type('video.wmv'), ['video/x-ms-wmv', 'application/octet-stream'])
        self.assertIn(guess_mime_type('video.avi'), ['video/x-msvideo',
                                                     'video/avi'])

        # Office
        self.assertEquals(guess_mime_type('office.doc'),
                          'application/msword')
        self.assertEquals(guess_mime_type('office.xls'),
                          'application/vnd.ms-excel')
        self.assertEquals(guess_mime_type('office.ppt'),
                          'application/vnd.ms-powerpoint')

        # PDF
        self.assertEquals(guess_mime_type('document.pdf'),
                          'application/pdf')

        # Unknown
        self.assertEquals(guess_mime_type('file.unknown'),
                          'application/octet-stream')

        # Cases badly handled by Windows
        # See https://jira.nuxeo.com/browse/NXP-11660
        # and http://bugs.python.org/issue15207
        if sys.platform == "win32":
            # Text
            self.assertEquals(guess_mime_type('text.csv'),
                              'application/octet-stream')
            self.assertEquals(guess_mime_type('text.xml'), 'text/xml')

            # Image
            self.assertIn(guess_mime_type('picture.svg'), ['image/svg+xml', 'application/octet-stream'])

            # Video
            self.assertEquals(guess_mime_type('video.flv'),
                              'application/octet-stream')

            # Office
            self.assertIn(guess_mime_type('office.docx'),
                          ['application/vnd.openxmlformats-officedocument.wordprocessingml.document',
                           'application/octet-stream'])
            self.assertIn(guess_mime_type('office.xlsx'),
                          ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                           'application/octet-stream'])
            self.assertIn(guess_mime_type('office.pptx'),
                          ['application/vnd.openxmlformats-officedocument.presentationml.presentation',
                           'application/x-mspowerpoint.12', 'application/octet-stream'])

            self.assertIn(guess_mime_type('office.odt'),
                          ['application/vnd.oasis.opendocument.text', 'application/octet-stream'])
            self.assertIn(guess_mime_type('office.ods'),
                          ['application/vnd.oasis.opendocument.spreadsheet', 'application/octet-stream'])
            self.assertIn(guess_mime_type('office.odp'),
                          ['application/vnd.oasis.opendocument.presentation', 'application/octet-stream'])
        else:
            # Text
            self.assertEquals(guess_mime_type('text.csv'), 'text/csv')
            self.assertEquals(guess_mime_type('text.xml'), 'application/xml')

            # Image
            self.assertEquals(guess_mime_type('picture.svg'), 'image/svg+xml')

            # Video
            self.assertEquals(guess_mime_type('video.flv'), 'video/x-flv')

            # Office
            self.assertEquals(guess_mime_type('office.docx'),
                              'application/vnd.openxmlformats-officedocument.wordprocessingml.document')
            self.assertEquals(guess_mime_type('office.xlsx'),
                              'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
            self.assertEquals(guess_mime_type('office.pptx'),
                              'application/vnd.openxmlformats-officedocument.presentationml.presentation')

            self.assertEquals(guess_mime_type('office.odt'), 'application/vnd.oasis.opendocument.text')
            self.assertEquals(guess_mime_type('office.ods'), 'application/vnd.oasis.opendocument.spreadsheet')
            self.assertEquals(guess_mime_type('office.odp'), 'application/vnd.oasis.opendocument.presentation')

    def test_guess_digest_algorithm(self):
        s = 'joe'
        md5_digest = hashlib.md5(s).hexdigest()
        self.assertEquals(guess_digest_algorithm(md5_digest), 'md5')
        sha1_digest = hashlib.sha1(s).hexdigest()
        self.assertEquals(guess_digest_algorithm(sha1_digest), 'sha1')
        # For now only md5 and sha1 are supported
        sha256_digest = hashlib.sha256(s).hexdigest()
        try:
            guess_digest_algorithm(sha256_digest)
            self.fail('Other algorithms than md5 and sha1 should not be supported for now')
        except:
            pass
<|endoftext|>


<commit_before>"""

For more details about HAChina,
https://www.hachina.io/
"""
import asyncio
import logging

import voluptuous as vol

DOMAIN = 'introduction'

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({}),
}, extra=vol.ALLOW_EXTRA)

@asyncio.coroutine
def async_setup(hass, config=None):
    """Set up the introduction component."""
    log = logging.getLogger(__name__)
    log.info("""

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        欢迎使用HACHINA.IO创建的镜像文件！

        我们的网站https://www.hachina.io

        This message is generated by the introduction_hachina component. You can
        disable it in configuration.yaml.

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    """)

    hass.components.persistent_notification.async_create("""
[![HACHINA](https://www.hachina.io/wp-content/themes/haChina/images/logo@2x.png)](https://www.hachina.io)

在此镜像文件中，我们安装与开放了以下服务:

 - [HomeAssistant](https://home-assistant.io/)
 - [Jupyter Notebook](http://jupyter.org/)
 - [Mosquitto](http://www.mosquitto.org/)
 - [Samba](https://www.samba.org/)
 - [SshD](https://www.openssh.com/)
 - [AppDaemon&DashBoard](https://appdaemon.readthedocs.io/)
注：[AppDaemon&DashBoard]服务已安装，但没有初始化启动。请在`/home/pi/appdaemon/appdaemon.yaml`中配置token后使用。设置自启动服务命令`sudo systemctl enable appdaemon@pi`。

密码与修改:

 - 操作系统的`pi`账号，初始密码为`hachina`。以`pi`账号登录后，使用`passwd`命令修改
 - HomeAssistant的未设置API密码（Legacy API Password）
 - Jupyter Notebook的初始访问密码为`hachina`。以`pi`账号登录后，使用`jupyter notebook password`命令修改
 - Mosquitto的用户名为`pi`，初始密码为`hachina`。以`pi`账号登录后，使用`sudo mosquitto_passwd /etc/mosquitto/passwd pi`命令修改
 - DashBoard的初始访问密码为`hachina`（访问端口为5050）。以`pi`账号登录后，在文件`/home/pi/appdaemon/appdaemon.yaml`中修改。

第一次启动，HomeAssistant会自动生成配置文件，与标准的HomeAssistant缺省配置比较，有以下不同：

 - 配置目录下空白的`known_devices.yaml`文件
 - 配置在sensor组件下的bitcoin平台
 - 将tts组件中google平台设置为中文
 - 媒体播放器（media_player）组件中配置vlc平台
 - [introduction_hachina组件](https://github.com/zhujisheng/HAComponent/tree/master/introduction_hachina)
 - [tunnel2local组件](https://github.com/zhujisheng/HAComponent/tree/master/tunnel2local)
 - [redpoint组件](https://github.com/HAChina/redpoint)

我们的网站：[https://www.hachina.io](https://www.hachina.io)

欲去除本卡信息，请编辑`configuration.yaml`文件，删除或注释调`introduction_hachina`组件
""", '欢迎使用HACHINA.IO创建的镜像文件！')  # noqa

    return True
<commit_msg>Update introduction_hachina/introduction_hachina.py<commit_after>"""

For more details about HAChina,
https://www.hachina.io/
"""
import asyncio
import logging

import voluptuous as vol

DOMAIN = 'introduction'

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({}),
}, extra=vol.ALLOW_EXTRA)

@asyncio.coroutine
def async_setup(hass, config=None):
    """Set up the introduction component."""
    log = logging.getLogger(__name__)
    log.info("""

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        欢迎使用HACHINA.IO创建的镜像文件！

        我们的网站https://www.hachina.io

        This message is generated by the introduction_hachina component. You can
        disable it in configuration.yaml.

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    """)

    hass.components.persistent_notification.async_create("""
[![HACHINA](https://www.hachina.io/wp-content/themes/haChina/images/logo@2x.png)](https://www.hachina.io)

在此镜像文件中，我们安装与开放了以下服务:

 - [HomeAssistant](https://home-assistant.io/)
 - [Jupyter Notebook](http://jupyter.org/)
 - [Mosquitto](http://www.mosquitto.org/)
 - [Samba](https://www.samba.org/)
 - [SshD](https://www.openssh.com/)
 - [AppDaemon&DashBoard](https://appdaemon.readthedocs.io/)
注：AppDaemon&DashBoard服务已安装，但没有初始化启动。请在`/home/pi/appdaemon/appdaemon.yaml`中配置token后使用。设置自启动服务命令`sudo systemctl enable appdaemon@pi`。

密码与修改:

 - 操作系统的`pi`账号，初始密码为`hachina`。以`pi`账号登录后，使用`passwd`命令修改
 - Jupyter Notebook的初始访问密码为`hachina`。以`pi`账号登录后，使用`jupyter notebook password`命令修改
 - Mosquitto的用户名为`pi`，初始密码为`hachina`。以`pi`账号登录后，使用`sudo mosquitto_passwd /etc/mosquitto/passwd pi`命令修改
 - DashBoard的初始访问密码为`hachina`（访问端口为5050）。以`pi`账号登录后，在文件`/home/pi/appdaemon/appdaemon.yaml`中修改。

第一次启动，HomeAssistant会自动生成配置文件，与标准的HomeAssistant缺省配置比较，有以下不同：

 - 配置目录下空白的`known_devices.yaml`文件
 - 配置在sensor组件下的bitcoin平台
 - 将tts组件中google平台设置为中文
 - 媒体播放器（media_player）组件中配置vlc平台
 - [introduction_hachina组件](https://github.com/zhujisheng/HAComponent/tree/master/introduction_hachina)
 - [tunnel2local组件](https://github.com/zhujisheng/HAComponent/tree/master/tunnel2local)
 - [redpoint组件](https://github.com/HAChina/redpoint)

我们的网站：[https://www.hachina.io](https://www.hachina.io)

欲去除本卡信息，请编辑`configuration.yaml`文件，删除或注释调`introduction_hachina`组件
""", '欢迎使用HACHINA.IO创建的镜像文件！')  # noqa

    return True
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Presence analyzer unit tests.
"""
import os.path
import json
import datetime
import unittest
from mock import patch
from numpy.oldnumeric.random_array import randint

from presence_analyzer import main, views, utils

CURRENT_PATH = os.path.dirname(__file__)
TEST_DATA_CSV = os.path.join(
    CURRENT_PATH, '..', '..', 'runtime', 'data', 'test_data.csv'
)

BAD_TEST_DATA_CSV = os.path.join(
    CURRENT_PATH, '..', '..', 'runtime', 'data', 'bad_test_data.csv'
)

# pylint: disable=E1103
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
    """
    Views tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': TEST_DATA_CSV})
        self.client = main.app.test_client()

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    def test_mainpage(self):
        """
        Test main page redirect.
        """
        resp = self.client.get('/')
        self.assertEqual(resp.status_code, 302)
        assert resp.headers['Location'].endswith('/presence_weekday.html')

    def test_api_users(self):
        """
        Test users listing.
        """
        resp = self.client.get('/api/v1/users')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 2)
        self.assertDictEqual(data[0], {u'user_id': 10, u'name': u'User 10'})

    def test_mean_time_weekday_view(self):
        """
        Test daily mean time for user
        """
        base_url = '/api/v1/mean_time_weekday/%d'

        resp = self.client.get(base_url % 10)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 7)
        self.assertListEqual(data[1], [u'Tue', 30047.0])
        self.assertListEqual(data[6], [u'Sun', 0])

        resp = self.client.get(base_url % 11)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 7)
        self.assertListEqual(data[2], [u'Wed', 25321.0])
        self.assertListEqual(data[6], [u'Sun', 0])

    @patch.object(views.log, 'debug')
    def test_mean_time_weekday_view_log(self, mock_logger):
        """
        Checks if log.debug is called when requesting for non-existing user
        """
        user_id = 31111111111111111
        resp = self.client.get('/api/v1/mean_time_weekday/%d' % user_id)
        mock_logger.assert_called_once_with('User %s not found!', user_id)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(data, [])

    def test_presence_weekday_view(self):
        """
        Test daily user presence
        """
        base_url = '/api/v1/presence_weekday/%d'

        resp = self.client.get(base_url % 10)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 8)
        self.assertListEqual(data[0], [u'Weekday', u'Presence (s)'])
        self.assertListEqual(data[2], [u'Tue', 30047.0])
        self.assertListEqual(data[6], [u'Sat', 0])

        resp = self.client.get(base_url % 11)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 8)
        self.assertListEqual(data[0], [u'Weekday', u'Presence (s)'])
        self.assertListEqual(data[4], [u'Thu', 45968])
        self.assertListEqual(data[6], [u'Sat', 0])

    @patch.object(views.log, 'debug')
    def test_presence_weekday_view_log(self, mock_logger):
        """
        Test daily user presence for non-existing user
        """
        user_id = 31111111111111111
        resp = self.client.get('/api/v1/presence_weekday/%d' % user_id)
        mock_logger.assert_called_once_with('User %s not found!', user_id)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(data, [])

class PresenceAnalyzerUtilsTestCase(unittest.TestCase):
    """
    Utility functions tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': TEST_DATA_CSV})

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    def test_get_data(self):
        """
        Test parsing of CSV file.
        """
        data = utils.get_data()
        self.assertIsInstance(data, dict)
        self.assertItemsEqual(data.keys(), [10, 11])
        sample_date = datetime.date(2013, 9, 10)
        self.assertIn(sample_date, data[10])
        self.assertItemsEqual(data[10][sample_date].keys(), ['start', 'end'])
        self.assertEqual(data[10][sample_date]['start'],
                         datetime.time(9, 39, 5))

    def test_group_by_weekday(self):
        data = utils.get_data()
        user_10 = utils.group_by_weekday(data[10])

        self.assertEqual(len(user_10), 7)
        self.assertIsInstance(user_10, dict)

        for i in xrange(7):
            self.assertTrue(i in user_10)
            self.assertIsInstance(user_10[i], list)

        self.assertEqual(user_10[0], [])
        self.assertIsInstance(user_10[1][0], int)

    def test_mean(self):
        """
        Test calculation of mean
        """
        self.assertEqual(utils.mean([]), 0)
        self.assertIsInstance(utils.mean([]), int)
        self.assertIsInstance(utils.mean([1, 2, 3]), float)
        self.assertEqual(utils.mean([1, 2, 3]), 2)
        self.assertEqual(utils.mean([a for a in xrange(-100, 101, -1)]), 0)
        self.assertEqual(utils.mean(
            [123, 234, 345, 456, 567, 678, 789, 890]), 510.25)

        for a in [randint(2, 123) for i in xrange(randint(2, 123))]:
            self.assertEqual(utils.mean(xrange(1, a)), a/2.0)

    def test_seconds_since_midnight(self):
        """
        Test secounds since midnight calculation
        """
        self.assertEquals(utils.seconds_since_midnight(
            datetime.datetime(1, 1, 1)), 0)
        self.assertIsInstance(utils.seconds_since_midnight(
            datetime.datetime(1, 1, 1)), int)
        self.assertEquals(utils.seconds_since_midnight(
            datetime.datetime(1, 1, 1)), 0)
        self.assertEquals(utils.seconds_since_midnight(
            datetime.time(0, 0, 1)), 1)
        self.assertEquals(utils.seconds_since_midnight(
            datetime.time(12, 0, 0)), 43200)

    def test_interval(self):
        """
        Test interval calculation
        """
        td = datetime.timedelta(hours=4)
        dd1 = datetime.datetime(2013, 5, 1, 12, 05, 04)
        self.assertIsInstance(utils.interval(dd1-td, dd1), int)
        self.assertEqual(utils.interval(dd1-td, dd1), td.seconds)

        dd2 = datetime.datetime(2013, 5, 1, 1, 05, 04)
        self.assertEqual(utils.interval(dd2-td, dd2), td.seconds-24*60*60)

class PresenceAnalyzerUtilsWithBadDataTestCase(unittest.TestCase):
    """
    Utility functions tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': BAD_TEST_DATA_CSV})

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    @patch.object(utils.log, 'debug')
    def test_get_data(self, mock_logger):
        """
        Test parsing of CSV file with bad entries
        """
        data = utils.get_data()
        msg = 'Problem with line %d: '
        mock_logger.assert_call_with(msg, 3, exc_info=True)
        mock_logger.assert_call_with(msg, 8, exc_info=True)
        self.assertIsInstance(data, dict)
        self.assertItemsEqual(data.keys(), [10, 11])
        self.assertEqual(len(data), 2)
        self.assertEqual(len(data[10])+len(data[11]), 9)

def suite():
    """
    Default test suite.
    """
    test_suite = unittest.TestSuite()
    test_suite.addTest(unittest.makeSuite(PresenceAnalyzerViewsTestCase))
    test_suite.addTest(unittest.makeSuite(PresenceAnalyzerUtilsTestCase))
    test_suite.addTest(unittest.makeSuite(
        PresenceAnalyzerUtilsWithBadDataTestCase))
    return test_suite

if __name__ == '__main__':
    unittest.main()
<commit_msg>Forgotten docstring<commit_after># -*- coding: utf-8 -*-
"""
Presence analyzer unit tests.
"""
import os.path
import json
import datetime
import unittest
from mock import patch
from numpy.oldnumeric.random_array import randint

from presence_analyzer import main, views, utils

CURRENT_PATH = os.path.dirname(__file__)
TEST_DATA_CSV = os.path.join(
    CURRENT_PATH, '..', '..', 'runtime', 'data', 'test_data.csv'
)

BAD_TEST_DATA_CSV = os.path.join(
    CURRENT_PATH, '..', '..', 'runtime', 'data', 'bad_test_data.csv'
)

# pylint: disable=E1103
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
    """
    Views tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': TEST_DATA_CSV})
        self.client = main.app.test_client()

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    def test_mainpage(self):
        """
        Test main page redirect.
        """
        resp = self.client.get('/')
        self.assertEqual(resp.status_code, 302)
        assert resp.headers['Location'].endswith('/presence_weekday.html')

    def test_api_users(self):
        """
        Test users listing.
        """
        resp = self.client.get('/api/v1/users')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 2)
        self.assertDictEqual(data[0], {u'user_id': 10, u'name': u'User 10'})

    def test_mean_time_weekday_view(self):
        """
        Test daily mean time for user
        """
        base_url = '/api/v1/mean_time_weekday/%d'

        resp = self.client.get(base_url % 10)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 7)
        self.assertListEqual(data[1], [u'Tue', 30047.0])
        self.assertListEqual(data[6], [u'Sun', 0])

        resp = self.client.get(base_url % 11)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 7)
        self.assertListEqual(data[2], [u'Wed', 25321.0])
        self.assertListEqual(data[6], [u'Sun', 0])

    @patch.object(views.log, 'debug')
    def test_mean_time_weekday_view_log(self, mock_logger):
        """
        Checks if log.debug is called when requesting for non-existing user
        """
        user_id = 31111111111111111
        resp = self.client.get('/api/v1/mean_time_weekday/%d' % user_id)
        mock_logger.assert_called_once_with('User %s not found!', user_id)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(data, [])

    def test_presence_weekday_view(self):
        """
        Test daily user presence
        """
        base_url = '/api/v1/presence_weekday/%d'

        resp = self.client.get(base_url % 10)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 8)
        self.assertListEqual(data[0], [u'Weekday', u'Presence (s)'])
        self.assertListEqual(data[2], [u'Tue', 30047.0])
        self.assertListEqual(data[6], [u'Sat', 0])

        resp = self.client.get(base_url % 11)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(len(data), 8)
        self.assertListEqual(data[0], [u'Weekday', u'Presence (s)'])
        self.assertListEqual(data[4], [u'Thu', 45968])
        self.assertListEqual(data[6], [u'Sat', 0])

    @patch.object(views.log, 'debug')
    def test_presence_weekday_view_log(self, mock_logger):
        """
        Test daily user presence for non-existing user
        """
        user_id = 31111111111111111
        resp = self.client.get('/api/v1/presence_weekday/%d' % user_id)
        mock_logger.assert_called_once_with('User %s not found!', user_id)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        data = json.loads(resp.data)
        self.assertEqual(data, [])

class PresenceAnalyzerUtilsTestCase(unittest.TestCase):
    """
    Utility functions tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': TEST_DATA_CSV})

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    def test_get_data(self):
        """
        Test parsing of CSV file.
        """
        data = utils.get_data()
        self.assertIsInstance(data, dict)
        self.assertItemsEqual(data.keys(), [10, 11])
        sample_date = datetime.date(2013, 9, 10)
        self.assertIn(sample_date, data[10])
        self.assertItemsEqual(data[10][sample_date].keys(), ['start', 'end'])
        self.assertEqual(data[10][sample_date]['start'],
                         datetime.time(9, 39, 5))

    def test_group_by_weekday(self):
        """
        Test grouping by weekday
        """
        data = utils.get_data()
        user_10 = utils.group_by_weekday(data[10])

        self.assertEqual(len(user_10), 7)
        self.assertIsInstance(user_10, dict)

        for i in xrange(7):
            self.assertTrue(i in user_10)
            self.assertIsInstance(user_10[i], list)

        self.assertEqual(user_10[0], [])
        self.assertIsInstance(user_10[1][0], int)

    def test_mean(self):
        """
        Test calculation of mean
        """
        self.assertEqual(utils.mean([]), 0)
        self.assertIsInstance(utils.mean([]), int)
        self.assertIsInstance(utils.mean([1, 2, 3]), float)
        self.assertEqual(utils.mean([1, 2, 3]), 2)
        self.assertEqual(utils.mean([a for a in xrange(-100, 101, -1)]), 0)
        self.assertEqual(utils.mean(
            [123, 234, 345, 456, 567, 678, 789, 890]), 510.25)

        for a in [randint(2, 123) for i in xrange(randint(2, 123))]:
            self.assertEqual(utils.mean(xrange(1, a)), a/2.0)

    def test_seconds_since_midnight(self):
        """
        Test secounds since midnight calculation
        """
        self.assertEquals(utils.seconds_since_midnight(
            datetime.datetime(1, 1, 1)), 0)
        self.assertIsInstance(utils.seconds_since_midnight(
            datetime.datetime(1, 1, 1)), int)
        self.assertEquals(utils.seconds_since_midnight(
            datetime.datetime(1, 1, 1)), 0)
        self.assertEquals(utils.seconds_since_midnight(
            datetime.time(0, 0, 1)), 1)
        self.assertEquals(utils.seconds_since_midnight(
            datetime.time(12, 0, 0)), 43200)

    def test_interval(self):
        """
        Test interval calculation
        """
        td = datetime.timedelta(hours=4)
        dd1 = datetime.datetime(2013, 5, 1, 12, 05, 04)
        self.assertIsInstance(utils.interval(dd1-td, dd1), int)
        self.assertEqual(utils.interval(dd1-td, dd1), td.seconds)

        dd2 = datetime.datetime(2013, 5, 1, 1, 05, 04)
        self.assertEqual(utils.interval(dd2-td, dd2), td.seconds-24*60*60)

class PresenceAnalyzerUtilsWithBadDataTestCase(unittest.TestCase):
    """
    Utility functions tests.
    """

    def setUp(self):
        """
        Before each test, set up a environment.
        """
        main.app.config.update({'DATA_CSV': BAD_TEST_DATA_CSV})

    def tearDown(self):
        """
        Get rid of unused objects after each test.
        """
        pass

    @patch.object(utils.log, 'debug')
    def test_get_data(self, mock_logger):
        """
        Test parsing of CSV file with bad entries
        """
        data = utils.get_data()
        msg = 'Problem with line %d: '
        mock_logger.assert_call_with(msg, 3, exc_info=True)
        mock_logger.assert_call_with(msg, 8, exc_info=True)
        self.assertIsInstance(data, dict)
        self.assertItemsEqual(data.keys(), [10, 11])
        self.assertEqual(len(data), 2)
        self.assertEqual(len(data[10])+len(data[11]), 9)

def suite():
    """
    Default test suite.
    """
    test_suite = unittest.TestSuite()
    test_suite.addTest(unittest.makeSuite(PresenceAnalyzerViewsTestCase))
    test_suite.addTest(unittest.makeSuite(PresenceAnalyzerUtilsTestCase))
    test_suite.addTest(unittest.makeSuite(
        PresenceAnalyzerUtilsWithBadDataTestCase))
    return test_suite

if __name__ == '__main__':
    unittest.main()
<|endoftext|>