<commit_before># Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Dictionary of configuration types for cbuildbot.

Each dictionary entry is in turn a dictionary of config_param->value.

config_param's:
board -- The board of the image to build.

master -- This bot pushes changes to the overlays.
important -- Master bot uses important bots to determine overall status.
             i.e. if master bot succeeds and other important slaves succeed
             then the master will uprev packages.  This should align
             with info vs. closer except for the master.and options.tests
hostname -- Needed for 'important' slaves.  The hostname of the bot.  Should
            match hostname in slaves.cfg in buildbot checkout.

useflags -- emerge use flags to use while setting up the board, building
            packages, making images, etc.
chromeos_official -- Set the variable CRHOMEOS_OFFICIAL for the build.
                     Known to affect parallel_emerge, cros_set_lsb_release,
                     and chromeos_version.sh. See bug chromium-os:14649

uprev -- Uprevs the local ebuilds to build new changes since last stable.
         build.  If master then also pushes these changes on success.
rev_overlays -- Select what overlays to look at for revving. This can be
                'public', 'private' or 'both'.
push_overlays -- Select what overlays to push at. This should be a subset of
                 rev_overlays for the particular builder.  Must be None if
                 not a master.  There should only be one master bot pushing
                 changes to each overlay per branch.
chrome_rev -- Uprev Chrome, values of 'tot', 'stable_release', or None.

chrome_tests -- runs chrome testing binaries in a vm.

unittests -- Runs unittests for packages.
quick_unit -- If unittests is true, only run the unit tests for packages which
              have changed since the previous build.

vm_tests -- Runs the smoke suite and au test harness in a qemu-based VM
            using KVM.
quick_vm -- If vm_tests is true, run a minimal au test harness suite.

usepkg -- Use binary packages to bootstrap, when possible. (emerge --usepkg)

chroot_replace -- wipe and replace chroot, but not source.

gs_path -- Google Storage path to offload files to.
           None - No upload
           'default' - 'gs://chromeos-archive/' + bot_id
           value - Upload to explicit path

build_type -- Upload prebuilts under the specified category. Can be any of
              [preflight|full|chrome].
test_mod -- Create a test mod image for archival.
factory_install_mod -- Create a factory install image for archival.
factory_test_mod -- Create a factory test image for archival.

push_image -- Do we push a final release image to chromeos-images.
upload_symbols -- Do we upload debug symbols.

git_url -- git repository URL for our manifests.
           External: http://git.chromium.org/chromiumos/manifest.git
           Internal:
               ssh://gerrit-int.chromium.org:29419/chromeos/manifest-internal

manifest_version -- URL to git repo to store per-build manifest.
"""

GS_PATH_DEFAULT = 'default'

default = {
  # 'board' No default value

  'master' : False,
  'important' : False,
  # 'hostname' No default value

  'useflags' : None,
  'chromeos_official' : False,
  'usepkg' : True,
  'chroot_replace' : False,

  'uprev' : False,
  'rev_overlays': 'public',
  'push_overlays': None,
  'chrome_rev' : None,

  'chrome_tests' : False,

  'unittests' : True,
  'quick_unit' : True,

  'vm_tests' : True,
  'quick_vm' : True,

  'gs_path': GS_PATH_DEFAULT,

  'build_type' : False,
  'archive_build_debug' : False,

  'test_mod' : False,
  'factory_install_mod' : False,
  'factory_test_mod' : False,

  'push_image' : False,
  'upload_symbols' : False,

  'git_url' : 'http://git.chromium.org/chromiumos/manifest.git',
  'manifest_version' : None,
}

arm = {
  # VM/tests are broken on arm.
  'unittests' : False,
  'vm_tests' : False,

  # These images don't work for arm.
  'factory_install_mod' : False,
  'factory_test_mod' : False,
}

full = {
  # Full builds are test build to show that we can build from scratch,
  # so use settings to build from scratch, and archive the results.
  'usepkg' : False,
  'chroot_replace' : True,

  'quick_unit' : False,
  'quick_vm' : True,

  'build_type': 'full',
  'archive_build_debug' : True,
  'test_mod' : True,
  'factory_install_mod' : True,
  'factory_test_mod' : True,
}

internal = {
  'git_url' : 'ssh://gerrit-int.chromium.org:29419/chromeos/manifest-internal',
}

#
# Internal Builds
#

release = {
  # Typical matching cbuild command line
  # --autorev --official --officialversion --chromeos --with-pdf
  #   --bvt --clean --no-gstorage --ctest

  'master' : False,
  'uprev' : True,
  'rev_overlays': 'both',

  'useflags' : ['chrome_internal', 'chrome_pdf'],
  'chromeos_official' : True,

  'chroot_replace' : True,

  'unittests' : True,
  'quick_unit' : False,

  'vm_tests' : True,
  'quick_vm' : False,

  'chrome_tests' : True,

  'usepkg' : False,
  'chroot_replace' : True,

  'build_type': 'full',
  'archive_build_debug' : True,
  'test_mod' : True,
  'factory_install_mod' : True,
  'factory_test_mod' : True,

  'push_image' : True,
  'upload_symbols' : True,

  'gs_path' : None,

# --official
# --officialversion
}

internal_full = {

  # Typical matching cbuild command line
  # master --official --chromeos --clean --upload-board-prebuilt
  #   --ctest --unittests --bvt

  'useflags' : ['chrome_internal', 'chrome_pdf'],
  'chromeos_official' : True,

  'usepkg' : False,
  'chroot_replace' : True,

  'unittests' : True,
  'quick_unit' : False,

  'vm_tests' : True,
  'quick_vm' : False,

  'build_type': 'full',
  'test_mod' : True,
  'factory_install_mod' : True,
  'factory_test_mod' : True,

  'git_url' : 'ssh://gerrit-int.chromium.org:29419/chromeos/manifest-internal',

  'gs_path' : None,

  # cbuild --official
}

config = {}

def add_config(name, updates):
  new_config = default.copy()
  for update_config in updates:
    new_config.update(update_config)

  config[name] = new_config

#
# External Builds
#

add_config('x86-generic-pre-flight-queue', [{
  'board' : 'x86-generic',
  'master' : True,
  'hostname' : 'chromeosbuild2',

  'uprev' : True,
  'build_type': 'preflight',
  'rev_overlays': 'public',
  'push_overlays': 'public',
}])

add_config('x86-generic-chrome-pre-flight-queue', [{
  'board' : 'x86-generic',
  'master' : True,

  'build_type': 'chrome',
  'uprev' : False,
  'chrome_tests' : True,
  'rev_overlays': 'public',
  'push_overlays': 'public',
}])

add_config('x86-mario-pre-flight-queue', [internal, {
  'board' : 'x86-mario',
  'master' : True,

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
  'gs_path': 'gs://chromeos-x86-mario/pre-flight-master'
}])

add_config('x86-alex-pre-flight-branch', [internal, {
  'board' : 'x86-alex',
  'master' : False,

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': None,
}])

add_config('x86-mario-pre-flight-branch', [internal, {
  'board' : 'x86-mario',
  'master' : True,

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'both',
}])

add_config('x86-agz-bin', [{
  'board' : 'x86-agz',

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': None,
}])

add_config('x86-dogfood-bin', [{
  'board' : 'x86-dogfood',

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': None,
}])

add_config('x86-pineview-bin', [{
  'board' : 'x86-pineview',

  'uprev' : True,
  'rev_overlays': 'public',
  'push_overlays': None,
}])

add_config('arm-tegra2-bin', [arm, {
  'board' : 'tegra2_dev-board',

  'uprev' : True,
  'rev_overlays': 'public',
  'push_overlays': None,
}])

add_config('arm-generic-bin', [arm, {
  'board' : 'arm-generic',

  'uprev' : True,
  'rev_overlays': 'public',
  'push_overlays': None,
}])

add_config('arm-generic-full', [arm, full, {
  'board' : 'arm-generic',
}])

add_config('arm-tegra2-full', [arm, full, {
  'board' : 'tegra2_dev-board',
}])

add_config('arm-tegra2-seaboard-full', [arm, full, {
  'board' : 'tegra2_seaboard',
}])

add_config('x86-generic-full', [full, {
  'board' : 'x86-generic',
}])

add_config('x86-pineview-full', [full, {
  'board' : 'x86-pineview',
}])

#
# Internal Builds
#

add_config('x86-mario-pre-flight-queue', [internal, {
  'board' : 'x86-mario',
  'master' : True,

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
  'chrome_rev' : 'stable_release',

  'gs_path': 'gs://chromeos-x86-mario/pre-flight-master'
}])

# cbuild --board=x86-mario  master --official --chromeos --clean
#   --upload-board-prebuilt --ctest --unittests --bvt
add_config('x86-mario-private-full', [internal_full, {
  'board' : 'x86-mario',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

# cbuild --board=x86-zgb  master --official --chromeos --clean
#   --upload-board-prebuilt --ctest --unittests --bvt
add_config('x86-zgb-private-full', [internal_full, {
  'board' : 'x86-zgb',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

# cbuild --board=x86-alex master --official --chromeos --clean
#   --upload-board-prebuilt --unittests --bvt
add_config('x86-alex-private-full', [internal_full, {
  'board' : 'x86-alex',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

# cbuild --board=tegra2_seaboard master --official --chromeos --clean
#   --upload-board-prebuilt
add_config('arm-tegra2_seaboard-private-full', [internal_full, {
  'board' : 'arm-tegra2_seaboard',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

# cbuild --board=tegra2_aebl  master --official --chromeos --clean
#   --upload-board-prebuilt
add_config('arm-tegra2_seaboard-private-full', [internal_full, {
  'board' : 'arm-tegra2_seaboard',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

add_config('x86-mario-release', [internal, release, {
  'board' : 'x86-mario',
}])

add_config('x86-alex-release', [internal, release, {
  'board' : 'x86-alex',
}])

add_config('x86-zgb-release', [internal, release, {
  'board' : 'x86-zgb',
}])

add_config('arm-tegra2_seaboard-release', [internal, release, arm, {
  'board' : 'tegra2_seaboard',
}])

add_config('arm-tegra2_aebl-release', [internal, release, arm, {
  'board' : 'tegra2_aebl',
}])

add_config('arm-tegra2_kaen-release', [internal, release, arm, {
  'board' : 'tegra2_kaen',
}])
<commit_msg>Turn on archive_build debugging to confirm correct behavior.<commit_after># Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Dictionary of configuration types for cbuildbot.

Each dictionary entry is in turn a dictionary of config_param->value.

config_param's:
board -- The board of the image to build.

master -- This bot pushes changes to the overlays.
important -- Master bot uses important bots to determine overall status.
             i.e. if master bot succeeds and other important slaves succeed
             then the master will uprev packages.  This should align
             with info vs. closer except for the master.and options.tests
hostname -- Needed for 'important' slaves.  The hostname of the bot.  Should
            match hostname in slaves.cfg in buildbot checkout.

useflags -- emerge use flags to use while setting up the board, building
            packages, making images, etc.
chromeos_official -- Set the variable CRHOMEOS_OFFICIAL for the build.
                     Known to affect parallel_emerge, cros_set_lsb_release,
                     and chromeos_version.sh. See bug chromium-os:14649

uprev -- Uprevs the local ebuilds to build new changes since last stable.
         build.  If master then also pushes these changes on success.
rev_overlays -- Select what overlays to look at for revving. This can be
                'public', 'private' or 'both'.
push_overlays -- Select what overlays to push at. This should be a subset of
                 rev_overlays for the particular builder.  Must be None if
                 not a master.  There should only be one master bot pushing
                 changes to each overlay per branch.
chrome_rev -- Uprev Chrome, values of 'tot', 'stable_release', or None.

chrome_tests -- runs chrome testing binaries in a vm.

unittests -- Runs unittests for packages.
quick_unit -- If unittests is true, only run the unit tests for packages which
              have changed since the previous build.

vm_tests -- Runs the smoke suite and au test harness in a qemu-based VM
            using KVM.
quick_vm -- If vm_tests is true, run a minimal au test harness suite.

usepkg -- Use binary packages to bootstrap, when possible. (emerge --usepkg)

chroot_replace -- wipe and replace chroot, but not source.

gs_path -- Google Storage path to offload files to.
           None - No upload
           'default' - 'gs://chromeos-archive/' + bot_id
           value - Upload to explicit path

build_type -- Upload prebuilts under the specified category. Can be any of
              [preflight|full|chrome].
test_mod -- Create a test mod image for archival.
factory_install_mod -- Create a factory install image for archival.
factory_test_mod -- Create a factory test image for archival.

push_image -- Do we push a final release image to chromeos-images.
upload_symbols -- Do we upload debug symbols.

git_url -- git repository URL for our manifests.
           External: http://git.chromium.org/chromiumos/manifest.git
           Internal:
               ssh://gerrit-int.chromium.org:29419/chromeos/manifest-internal

manifest_version -- URL to git repo to store per-build manifest.
"""

GS_PATH_DEFAULT = 'default'

default = {
  # 'board' No default value

  'master' : False,
  'important' : False,
  # 'hostname' No default value

  'useflags' : None,
  'chromeos_official' : False,
  'usepkg' : True,
  'chroot_replace' : False,

  'uprev' : False,
  'rev_overlays': 'public',
  'push_overlays': None,
  'chrome_rev' : None,

  'chrome_tests' : False,

  'unittests' : True,
  'quick_unit' : True,

  'vm_tests' : True,
  'quick_vm' : True,

  'gs_path': GS_PATH_DEFAULT,

  'build_type' : False,
  'archive_build_debug' : False,

  'test_mod' : False,
  'factory_install_mod' : False,
  'factory_test_mod' : False,

  'push_image' : False,
  'upload_symbols' : False,

  'git_url' : 'http://git.chromium.org/chromiumos/manifest.git',
  'manifest_version' : None,
}

arm = {
  # VM/tests are broken on arm.
  'unittests' : False,
  'vm_tests' : False,

  # These images don't work for arm.
  'factory_install_mod' : False,
  'factory_test_mod' : False,
}

full = {
  # Full builds are test build to show that we can build from scratch,
  # so use settings to build from scratch, and archive the results.
  'usepkg' : False,
  'chroot_replace' : True,

  'quick_unit' : False,
  'quick_vm' : True,

  'build_type': 'full',
  'archive_build_debug' : True,
  'test_mod' : True,
  'factory_install_mod' : True,
  'factory_test_mod' : True,
}

internal = {
  'git_url' : 'ssh://gerrit-int.chromium.org:29419/chromeos/manifest-internal',
}

#
# Internal Builds
#

release = {
  # Typical matching cbuild command line
  # --autorev --official --officialversion --chromeos --with-pdf
  #   --bvt --clean --no-gstorage --ctest

  'master' : False,
  'uprev' : True,
  'rev_overlays': 'both',

  'useflags' : ['chrome_internal', 'chrome_pdf'],
  'chromeos_official' : True,

  'chroot_replace' : True,

  'unittests' : True,
  'quick_unit' : False,

  'vm_tests' : True,
  'quick_vm' : False,

  'chrome_tests' : True,

  'usepkg' : False,
  'chroot_replace' : True,

  'build_type': 'full',
  'archive_build_debug' : True,
  'test_mod' : True,
  'factory_install_mod' : True,
  'factory_test_mod' : True,

  'push_image' : True,
  'upload_symbols' : True,

  'gs_path' : None,

  'archive_build_debug' : True,

# --official
# --officialversion
}

internal_full = {

  # Typical matching cbuild command line
  # master --official --chromeos --clean --upload-board-prebuilt
  #   --ctest --unittests --bvt

  'useflags' : ['chrome_internal', 'chrome_pdf'],
  'chromeos_official' : True,

  'usepkg' : False,
  'chroot_replace' : True,

  'unittests' : True,
  'quick_unit' : False,

  'vm_tests' : True,
  'quick_vm' : False,

  'build_type': 'full',
  'test_mod' : True,
  'factory_install_mod' : True,
  'factory_test_mod' : True,

  'git_url' : 'ssh://gerrit-int.chromium.org:29419/chromeos/manifest-internal',

  'gs_path' : None,

  'archive_build_debug' : True,

  # cbuild --official
}

config = {}

def add_config(name, updates):
  new_config = default.copy()
  for update_config in updates:
    new_config.update(update_config)

  config[name] = new_config

#
# External Builds
#

add_config('x86-generic-pre-flight-queue', [{
  'board' : 'x86-generic',
  'master' : True,
  'hostname' : 'chromeosbuild2',

  'uprev' : True,
  'build_type': 'preflight',
  'rev_overlays': 'public',
  'push_overlays': 'public',
}])

add_config('x86-generic-chrome-pre-flight-queue', [{
  'board' : 'x86-generic',
  'master' : True,

  'build_type': 'chrome',
  'uprev' : False,
  'chrome_tests' : True,
  'rev_overlays': 'public',
  'push_overlays': 'public',
}])

add_config('x86-mario-pre-flight-queue', [internal, {
  'board' : 'x86-mario',
  'master' : True,

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
  'gs_path': 'gs://chromeos-x86-mario/pre-flight-master'
}])

add_config('x86-alex-pre-flight-branch', [internal, {
  'board' : 'x86-alex',
  'master' : False,

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': None,
}])

add_config('x86-mario-pre-flight-branch', [internal, {
  'board' : 'x86-mario',
  'master' : True,

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'both',
}])

add_config('x86-agz-bin', [{
  'board' : 'x86-agz',

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': None,
}])

add_config('x86-dogfood-bin', [{
  'board' : 'x86-dogfood',

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': None,
}])

add_config('x86-pineview-bin', [{
  'board' : 'x86-pineview',

  'uprev' : True,
  'rev_overlays': 'public',
  'push_overlays': None,
}])

add_config('arm-tegra2-bin', [arm, {
  'board' : 'tegra2_dev-board',

  'uprev' : True,
  'rev_overlays': 'public',
  'push_overlays': None,
}])

add_config('arm-generic-bin', [arm, {
  'board' : 'arm-generic',

  'uprev' : True,
  'rev_overlays': 'public',
  'push_overlays': None,
}])

add_config('arm-generic-full', [arm, full, {
  'board' : 'arm-generic',
}])

add_config('arm-tegra2-full', [arm, full, {
  'board' : 'tegra2_dev-board',
}])

add_config('arm-tegra2-seaboard-full', [arm, full, {
  'board' : 'tegra2_seaboard',
}])

add_config('x86-generic-full', [full, {
  'board' : 'x86-generic',
}])

add_config('x86-pineview-full', [full, {
  'board' : 'x86-pineview',
}])

#
# Internal Builds
#

add_config('x86-mario-pre-flight-queue', [internal, {
  'board' : 'x86-mario',
  'master' : True,

  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
  'chrome_rev' : 'stable_release',

  'gs_path': 'gs://chromeos-x86-mario/pre-flight-master'
}])

# cbuild --board=x86-mario  master --official --chromeos --clean
#   --upload-board-prebuilt --ctest --unittests --bvt
add_config('x86-mario-private-full', [internal_full, {
  'board' : 'x86-mario',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

# cbuild --board=x86-zgb  master --official --chromeos --clean
#   --upload-board-prebuilt --ctest --unittests --bvt
add_config('x86-zgb-private-full', [internal_full, {
  'board' : 'x86-zgb',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

# cbuild --board=x86-alex master --official --chromeos --clean
#   --upload-board-prebuilt --unittests --bvt
add_config('x86-alex-private-full', [internal_full, {
  'board' : 'x86-alex',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

# cbuild --board=tegra2_seaboard master --official --chromeos --clean
#   --upload-board-prebuilt
add_config('arm-tegra2_seaboard-private-full', [internal_full, {
  'board' : 'arm-tegra2_seaboard',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

# cbuild --board=tegra2_aebl  master --official --chromeos --clean
#   --upload-board-prebuilt
add_config('arm-tegra2_seaboard-private-full', [internal_full, {
  'board' : 'arm-tegra2_seaboard',

  'master' : True,
  'uprev' : True,
  'rev_overlays': 'both',
  'push_overlays': 'private',
}])

add_config('x86-mario-release', [internal, release, {
  'board' : 'x86-mario',
}])

add_config('x86-alex-release', [internal, release, {
  'board' : 'x86-alex',
}])

add_config('x86-zgb-release', [internal, release, {
  'board' : 'x86-zgb',
}])

add_config('arm-tegra2_seaboard-release', [internal, release, arm, {
  'board' : 'tegra2_seaboard',
}])

add_config('arm-tegra2_aebl-release', [internal, release, arm, {
  'board' : 'tegra2_aebl',
}])

add_config('arm-tegra2_kaen-release', [internal, release, arm, {
  'board' : 'tegra2_kaen',
}])
<|endoftext|>


<commit_before>"""Support for Xiomi Gateway alarm control panels."""
from __future__ import annotations

from functools import partial
import logging

from miio import DeviceException

from homeassistant.components.alarm_control_panel import (
    AlarmControlPanelEntity,
    AlarmControlPanelEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
    STATE_ALARM_ARMED_AWAY,
    STATE_ALARM_ARMING,
    STATE_ALARM_DISARMED,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import CONF_GATEWAY, DOMAIN

_LOGGER = logging.getLogger(__name__)

XIAOMI_STATE_ARMED_VALUE = "on"
XIAOMI_STATE_DISARMED_VALUE = "off"
XIAOMI_STATE_ARMING_VALUE = "oning"

async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up the Xiaomi Gateway Alarm from a config entry."""
    entities = []
    gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY]
    entity = XiaomiGatewayAlarm(
        gateway,
        f"{config_entry.title} Alarm",
        config_entry.data["model"],
        config_entry.data["mac"],
        config_entry.unique_id,
    )
    entities.append(entity)
    async_add_entities(entities, update_before_add=True)

class XiaomiGatewayAlarm(AlarmControlPanelEntity):
    """Representation of the XiaomiGatewayAlarm."""

    _attr_supported_features = AlarmControlPanelEntityFeature.ARM_AWAY

    def __init__(
        self, gateway_device, gateway_name, model, mac_address, gateway_device_id
    ):
        """Initialize the entity."""
        self._gateway = gateway_device
        self._name = gateway_name
        self._gateway_device_id = gateway_device_id
        self._unique_id = f"{model}-{mac_address}"
        self._icon = "mdi:shield-home"
        self._available = None
        self._state = None

    @property
    def unique_id(self):
        """Return an unique ID."""
        return self._unique_id

    @property
    def device_id(self):
        """Return the device id of the gateway."""
        return self._gateway_device_id

    @property
    def device_info(self) -> DeviceInfo:
        """Return the device info of the gateway."""
        return DeviceInfo(
            identifiers={(DOMAIN, self._gateway_device_id)},
        )

    @property
    def name(self):
        """Return the name of this entity, if any."""
        return self._name

    @property
    def icon(self):
        """Return the icon to use for device if any."""
        return self._icon

    @property
    def available(self):
        """Return true when state is known."""
        return self._available

    @property
    def state(self):
        """Return the state of the device."""
        return self._state

    async def _try_command(self, mask_error, func, *args, **kwargs):
        """Call a device command handling error messages."""
        try:
            result = await self.hass.async_add_executor_job(
                partial(func, *args, **kwargs)
            )
            _LOGGER.debug("Response received from miio device: %s", result)
        except DeviceException as exc:
            _LOGGER.error(mask_error, exc)

    async def async_alarm_arm_away(self, code: str | None = None) -> None:
        """Turn on."""
        await self._try_command(
            "Turning the alarm on failed: %s", self._gateway.alarm.on
        )

    async def async_alarm_disarm(self, code: str | None = None) -> None:
        """Turn off."""
        await self._try_command(
            "Turning the alarm off failed: %s", self._gateway.alarm.off
        )

    async def async_update(self) -> None:
        """Fetch state from the device."""
        try:
            state = await self.hass.async_add_executor_job(self._gateway.alarm.status)
        except DeviceException as ex:
            if self._available:
                self._available = False
                _LOGGER.error("Got exception while fetching the state: %s", ex)

            return

        _LOGGER.debug("Got new state: %s", state)

        self._available = True

        if state == XIAOMI_STATE_ARMED_VALUE:
            self._state = STATE_ALARM_ARMED_AWAY
        elif state == XIAOMI_STATE_DISARMED_VALUE:
            self._state = STATE_ALARM_DISARMED
        elif state == XIAOMI_STATE_ARMING_VALUE:
            self._state = STATE_ALARM_ARMING
        else:
            _LOGGER.warning(
                "New state (%s) doesn't match expected values: %s/%s/%s",
                state,
                XIAOMI_STATE_ARMED_VALUE,
                XIAOMI_STATE_DISARMED_VALUE,
                XIAOMI_STATE_ARMING_VALUE,
            )
            self._state = None

        _LOGGER.debug("State value: %s", self._state)
<commit_msg>Use attributes in xiaomi_miio alarm (#74125)<commit_after>"""Support for Xiomi Gateway alarm control panels."""
from __future__ import annotations

from functools import partial
import logging

from miio import DeviceException

from homeassistant.components.alarm_control_panel import (
    AlarmControlPanelEntity,
    AlarmControlPanelEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
    STATE_ALARM_ARMED_AWAY,
    STATE_ALARM_ARMING,
    STATE_ALARM_DISARMED,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import CONF_GATEWAY, DOMAIN

_LOGGER = logging.getLogger(__name__)

XIAOMI_STATE_ARMED_VALUE = "on"
XIAOMI_STATE_DISARMED_VALUE = "off"
XIAOMI_STATE_ARMING_VALUE = "oning"

async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up the Xiaomi Gateway Alarm from a config entry."""
    entities = []
    gateway = hass.data[DOMAIN][config_entry.entry_id][CONF_GATEWAY]
    entity = XiaomiGatewayAlarm(
        gateway,
        f"{config_entry.title} Alarm",
        config_entry.data["model"],
        config_entry.data["mac"],
        config_entry.unique_id,
    )
    entities.append(entity)
    async_add_entities(entities, update_before_add=True)

class XiaomiGatewayAlarm(AlarmControlPanelEntity):
    """Representation of the XiaomiGatewayAlarm."""

    _attr_icon = "mdi:shield-home"
    _attr_supported_features = AlarmControlPanelEntityFeature.ARM_AWAY

    def __init__(
        self, gateway_device, gateway_name, model, mac_address, gateway_device_id
    ):
        """Initialize the entity."""
        self._gateway = gateway_device
        self._attr_name = gateway_name
        self._attr_unique_id = f"{model}-{mac_address}"
        self._attr_available = False
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, gateway_device_id)},
        )

    async def _try_command(self, mask_error, func, *args, **kwargs):
        """Call a device command handling error messages."""
        try:
            result = await self.hass.async_add_executor_job(
                partial(func, *args, **kwargs)
            )
            _LOGGER.debug("Response received from miio device: %s", result)
        except DeviceException as exc:
            _LOGGER.error(mask_error, exc)

    async def async_alarm_arm_away(self, code: str | None = None) -> None:
        """Turn on."""
        await self._try_command(
            "Turning the alarm on failed: %s", self._gateway.alarm.on
        )

    async def async_alarm_disarm(self, code: str | None = None) -> None:
        """Turn off."""
        await self._try_command(
            "Turning the alarm off failed: %s", self._gateway.alarm.off
        )

    async def async_update(self) -> None:
        """Fetch state from the device."""
        try:
            state = await self.hass.async_add_executor_job(self._gateway.alarm.status)
        except DeviceException as ex:
            if self._attr_available:
                self._attr_available = False
                _LOGGER.error("Got exception while fetching the state: %s", ex)

            return

        _LOGGER.debug("Got new state: %s", state)

        self._attr_available = True

        if state == XIAOMI_STATE_ARMED_VALUE:
            self._attr_state = STATE_ALARM_ARMED_AWAY
        elif state == XIAOMI_STATE_DISARMED_VALUE:
            self._attr_state = STATE_ALARM_DISARMED
        elif state == XIAOMI_STATE_ARMING_VALUE:
            self._attr_state = STATE_ALARM_ARMING
        else:
            _LOGGER.warning(
                "New state (%s) doesn't match expected values: %s/%s/%s",
                state,
                XIAOMI_STATE_ARMED_VALUE,
                XIAOMI_STATE_DISARMED_VALUE,
                XIAOMI_STATE_ARMING_VALUE,
            )
            self._attr_state = None

        _LOGGER.debug("State value: %s", self._attr_state)
<|endoftext|>


<commit_before># import nxt
#
# # brick = nxt.locator.find_one_brick(name = 'Hooper')
# b = nxt.locator.find_one_brick(
#     name="NXT", strict=True,
#     method=nxt.locator.Method(
#         bluetooth=False, fantomusb=True, fantombt=False, usb=False))

from ev3dev.auto import OUTPUT_D, LargeMotor, OUTPUT_A, MediumMotor, OUTPUT_C
import time

mup = LargeMotor(OUTPUT_D)
mgrab = MediumMotor(OUTPUT_A)
mright = LargeMotor(OUTPUT_C)
#print(m)
# m.run_forever(speed_sp = 360)
# time.sleep(1)
# m.stop()
# print('Hooray')

# mright.run_timed(speed_sp = -360, time_sp = 200)

#mup.run_timed(speed_sp = -360, time_sp = 300)
# mright.run_timed(speed_sp = -360, time_sp = 200)
# mgrab.run_timed(speed_sp = 360, time_sp = 400)

#move the arm down
mup.run_timed(speed_sp = 360, time_sp = 705)
#grab the object
mgrab.run_timed(speed_sp = 360, time_sp = 400)
#move arm back up
# mup.run_timed(speed_sp = -360, time_sp = 500)
# #move arm right
# mright.run_timed(speed_sp = 360, time_sp = 300)
# #move arm down at target location
# mup.run_timed(speed_sp = 360, time_sp = 705)
# #release the object
# mgrab.run_timed(speed_sp = -360, time_sp = 400)
# #reset position
# mup.run_timed(speed_sp = -360, time_sp = 705)
# mright.run_timed(speed_sp =-360, time_sp = 300)<commit_msg>Now trying moving down and grabbing<commit_after># import nxt
#
# # brick = nxt.locator.find_one_brick(name = 'Hooper')
# b = nxt.locator.find_one_brick(
#     name="NXT", strict=True,
#     method=nxt.locator.Method(
#         bluetooth=False, fantomusb=True, fantombt=False, usb=False))

from ev3dev.auto import OUTPUT_D, LargeMotor, OUTPUT_A, MediumMotor, OUTPUT_C
import time

mup = LargeMotor(OUTPUT_D)
mgrab = MediumMotor(OUTPUT_A)
mright = LargeMotor(OUTPUT_C)
#print(m)
# m.run_forever(speed_sp = 360)
# time.sleep(1)
# m.stop()
# print('Hooray')

# mright.run_timed(speed_sp = -360, time_sp = 200)

#mup.run_timed(speed_sp = -360, time_sp = 300)
# mright.run_timed(speed_sp = -360, time_sp = 200)
# mgrab.run_timed(speed_sp = 360, time_sp = 400)

#move the arm down
mup.run_timed(speed_sp = 360, time_sp = 705)
#grab the object
mgrab.run_timed(speed_sp = 360, time_sp = 400)
# move arm back up
# mup.run_timed(speed_sp = -360, time_sp = 500)
# #move arm right
# mright.run_timed(speed_sp = 360, time_sp = 300)
# #move arm down at target location
# mup.run_timed(speed_sp = 360, time_sp = 705)
# #release the object
# mgrab.run_timed(speed_sp = -360, time_sp = 400)
# #reset position
# mup.run_timed(speed_sp = -360, time_sp = 705)
# mright.run_timed(speed_sp =-360, time_sp = 300)<|endoftext|>


<commit_before>from setuptools import find_packages
from setuptools import setup

REQUIRED_PACKAGES = [
    'tensorflow==1.13.1',
    'tensorflow-model-analysis==0.13.0'
]

setup(
    name='trainer',
    version='0.1',
    install_requires=REQUIRED_PACKAGES,
    packages=find_packages(),
    include_package_data=True,
    description='babyweight trainer package.'
)
<commit_msg>Update setup.py<commit_after>from setuptools import find_packages
from setuptools import setup

REQUIRED_PACKAGES = [
    'tensorflow==1.13.1',
    'tensorflow-model-analysis==0.15.0'
]

setup(
    name='trainer',
    version='0.1',
    install_requires=REQUIRED_PACKAGES,
    packages=find_packages(),
    include_package_data=True,
    description='babyweight trainer package.'
)
<|endoftext|>


<commit_before>import urlparse

from django.contrib import admin
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.html import format_html, mark_safe
from django.template.loader import render_to_string

from polymorphic.admin import (PolymorphicParentModelAdmin,
                               PolymorphicChildModelAdmin)
from sorl.thumbnail.shortcuts import get_thumbnail

from bluebottle.utils.utils import set_author_editor_ip
from bluebottle.wallposts.models import SystemWallpost
from bluebottle.utils.widgets import SecureAdminURLFieldWidget

from .models import (Wallpost, MediaWallpost, TextWallpost,
                     MediaWallpostPhoto, Reaction)

class MediaWallpostPhotoInline(admin.TabularInline):
    model = MediaWallpostPhoto
    extra = 0
    raw_id_fields = ('author', 'editor')

    readonly_fields = ('image_tag',)

    fields = ('image_tag', 'photo')

    def image_tag(self, obj):
        data = {}
        if obj.photo:
            data['image_full_url'] = obj.photo.url
            data['image_thumb_url'] = get_thumbnail(obj.photo, "120x120",
                                                    crop="center").url

        return mark_safe(render_to_string(
            "admin/wallposts/mediawallpost_photoinline.html", data
        ))

    image_tag.short_description = 'Preview'

class MediaWallpostAdmin(PolymorphicChildModelAdmin):
    base_model = Wallpost
    readonly_fields = ('ip_address', 'deleted', 'view_online', 'gallery', 'donation',
                       'share_with_facebook', 'share_with_twitter',
                       'share_with_linkedin', 'email_followers')
    fields = readonly_fields + ('text', 'author', 'editor')
    raw_id_fields = ('author', 'editor')
    list_display = ('created', 'view_online', 'get_text', 'thumbnail', 'author', 'deleted')
    search_fields = ('text', 'author__first_name', 'author__last_name')
    exclude = ('object_id', 'content_type')

    extra_fields = ('gallery',)

    ordering = ('-created',)
    inlines = (MediaWallpostPhotoInline,)

    formfield_overrides = {
        models.URLField: {'widget': SecureAdminURLFieldWidget()},
    }

    def get_text(self, obj):
        if len(obj.text) > 150:
            return format_html(
                u'{} [...]',
                obj.text, obj.text[:145])

    def thumbnail(self, obj):
        data = {}
        if obj.video_url:
            data['video_url'] = obj.video_url
            if 'youtube.com' in obj.video_url:
                try:
                    urlparts = urlparse.urlparse(obj.video_url)
                    data['youtubeid'] = urlparse.parse_qs(urlparts.query)['v'][
                        0]
                except (KeyError, ValueError, IndexError):
                    pass

        photos = MediaWallpostPhoto.objects.filter(mediawallpost=obj)
        data['count'] = len(photos)
        data['remains'] = max(0, data['count'] - 1)

        if len(photos):
            if photos[0].photo:
                data['firstimage'] = get_thumbnail(photos[0].photo, "120x120",
                                                   crop="center").url
                data['firstimage_url'] = photos[0].photo.url

        return mark_safe(render_to_string("admin/wallposts/preview_thumbnail.html", data))

    def view_online(self, obj):
        if obj.content_object is None:
            return _(u'The project this post belongs to has been deleted')

        if obj.content_type.name == 'project':
            return format_html(
                u'{}',
                obj.content_object.slug, obj.content_object.title
            )
        if obj.content_type.name == 'task':
            if obj.content_object:
                return format_html(
                    u'{}',
                    obj.content_object.id,
                    obj.content_object.title
                )
        if obj.content_type.name == 'fundraiser':
            return format_html(
                u'{}',
                obj.content_object.id, obj.content_object.title
            )
        return '---'

    def gallery(self, obj):
        data = {}
        data['images'] = [dict(full=p.photo.url,
                               thumb=get_thumbnail(p.photo, "120x120",
                                                   crop="center").url)
                          for p in obj.photos.all()]

        return mark_safe(
            render_to_string("admin/wallposts/mediawallpost_gallery.html", data)
        )

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

class TextWallpostAdmin(PolymorphicChildModelAdmin):
    base_model = Wallpost
    readonly_fields = ('ip_address', 'deleted', 'posted_on', 'donation_link')
    search_fields = ('text', 'author__first_name', 'author__last_name')
    list_display = ('created', 'author', 'content_type', 'text', 'deleted')
    raw_id_fields = ('author', 'editor', 'donation')
    fields = readonly_fields + ('text', 'author', 'editor')
    exclude = ('object_id', 'content_type')

    ordering = ('-created',)

    def posted_on(self, obj):
        type = obj.content_type.name
        type_name = unicode(obj.content_type).title()

        if type == 'task':
            url = reverse('admin:tasks_task_change',
                          args=(obj.content_object.id,))
        elif type == 'project':
            url = reverse('admin:projects_project_change',
                          args=(obj.content_object.id,))
        elif type == 'fundraiser':
            url = reverse('admin:fundraisers_fundraiser_change',
                          args=(obj.content_object.id,))
        else:
            return ''

        title = obj.content_object.title
        return format_html(
            u'{}: {}',
            type_name, url, title
        )

    def donation_link(self, obj):
        if obj.donation:
            link = reverse('admin:donations_donation_change', args=(obj.donation.id,))
            return format_html(
                u"{}",
                link, obj.donation
            )

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

class SystemWallpostAdmin(PolymorphicChildModelAdmin):
    base_model = SystemWallpost
    readonly_fields = ('ip_address', 'content_type', 'related_type',
                       'donation_link', 'project_link',
                       'related_id', 'object_id')
    fields = readonly_fields + ('author', 'donation', 'text')
    list_display = ('created', 'author', 'content_type', 'related_type', 'text', 'deleted')
    raw_id_fields = ('author', 'editor', 'donation')
    ordering = ('-created',)
    exclude = ('object_id', 'content_type')

    def project_link(self, obj):
        if obj.donation:
            link = reverse('admin:projects_project_change', args=(obj.donation.project.id,))
            return format_html(
                u"{}",
                link, obj.donation.project.title
            )
    project_link.short_description = _('Project link')

    def donation_link(self, obj):
        if obj.donation:
            link = reverse('admin:donations_donation_change', args=(obj.donation.id,))
            return format_html(
                u"{}",
                link, obj.donation
            )

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

class WallpostParentAdmin(PolymorphicParentModelAdmin):
    """ The parent model admin """
    base_model = Wallpost
    list_display = ('created', 'author', 'content_type', 'text', 'type', 'deleted')
    fields = ('title', 'text', 'author', 'ip_address')
    list_filter = ('created', ('content_type', admin.RelatedOnlyFieldListFilter),)
    ordering = ('-created',)
    search_fields = (
        'textwallpost__text', 'mediawallpost__text',
        'author__username', 'author__email',
        'author__first_name', 'author__last_name', 'ip_address'
    )
    child_models = (
        (MediaWallpost, MediaWallpostAdmin),
        (TextWallpost, TextWallpostAdmin),
        (SystemWallpost, SystemWallpostAdmin),
    )

    def type(self, obj):
        return obj.get_real_instance_class().__name__

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

    def text(self, obj):
        text = '-empty-'
        try:
            text = obj.systemwallpost.text
        except SystemWallpost.DoesNotExist:
            pass
        try:
            text = obj.textwallpost.text
        except TextWallpost.DoesNotExist:
            pass
        try:
            text = obj.mediawallpost.text
        except MediaWallpost.DoesNotExist:
            pass
        if len(text) > 40:
            return format_html(text[:38] + '…')
        return text

admin.site.register(Wallpost, WallpostParentAdmin)
admin.site.register(MediaWallpost, MediaWallpostAdmin)
admin.site.register(TextWallpost, TextWallpostAdmin)
admin.site.register(SystemWallpost, SystemWallpostAdmin)

class ReactionAdmin(admin.ModelAdmin):
    # created and updated are auto-set fields. author, editor and ip_address are auto-set on save.
    readonly_fields = ('project_url', 'created', 'updated', 'author',
                       'editor', 'ip_address')
    list_display = ('author_full_name', 'created', 'updated',
                    'deleted', 'ip_address')
    date_hierarchy = 'created'
    ordering = ('-created',)
    raw_id_fields = ('author', 'editor', 'wallpost')
    search_fields = ('text', 'author__username', 'author__email',
                     'author__first_name', 'author__last_name', 'ip_address')

    fields = ('text', 'project_url', 'wallpost', 'deleted', 'created',
              'updated', 'author', 'editor', 'ip_address')

    def get_fieldsets(self, request, obj=None):
        """ Only show the relevant fields when adding a Reaction. """
        if obj:  # editing an existing object
            return super(ReactionAdmin, self).get_fieldsets(request, obj)
        return [(None, {'fields': ('wallpost', 'text')})]

    def author_full_name(self, obj):
        full_name = obj.author.get_full_name()
        if not full_name:
            return obj.author.username
        else:
            return full_name

    author_full_name.short_description = _('Author')

    def project_url(self, obj):
        project = obj.wallpost.content_object
        if project.__class__.__name__ == 'Project':
            url = project.get_absolute_url()
            return format_html(
                u"{}",
                str(url), project.title
            )
        return ''

    project_url.short_description = _('project link')

    def save_model(self, request, obj, form, change):
        """ Set the author or editor (as required) and ip when saving the model. """
        set_author_editor_ip(request, obj)
        super(ReactionAdmin, self).save_model(request, obj, form, change)

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

admin.site.register(Reaction, ReactionAdmin)
<commit_msg>Show date created and reaction in wallpost admin<commit_after>import urlparse

from django.contrib import admin
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.html import format_html, mark_safe
from django.template.loader import render_to_string

from polymorphic.admin import (PolymorphicParentModelAdmin,
                               PolymorphicChildModelAdmin)
from sorl.thumbnail.shortcuts import get_thumbnail

from bluebottle.utils.utils import set_author_editor_ip
from bluebottle.wallposts.models import SystemWallpost
from bluebottle.utils.widgets import SecureAdminURLFieldWidget

from .models import (Wallpost, MediaWallpost, TextWallpost,
                     MediaWallpostPhoto, Reaction)

class ReactionInline(admin.TabularInline):
    model = Reaction
    readonly_fields = ('reaction_link', 'author', 'ip_address', 'text', 'created', 'deleted')
    fields = readonly_fields
    extra = 0

    def reaction_link(self, obj):
        url = reverse('admin:wallposts_reaction_change', args=(obj.id, ))
        return format_html("Reaction #{}", url, obj.id)

class MediaWallpostPhotoInline(admin.TabularInline):
    model = MediaWallpostPhoto
    extra = 0
    raw_id_fields = ('author', 'editor')

    readonly_fields = ('image_tag',)

    fields = ('image_tag', 'photo')

    def image_tag(self, obj):
        data = {}
        if obj.photo:
            data['image_full_url'] = obj.photo.url
            data['image_thumb_url'] = get_thumbnail(obj.photo, "120x120",
                                                    crop="center").url

        return mark_safe(render_to_string(
            "admin/wallposts/mediawallpost_photoinline.html", data
        ))

    image_tag.short_description = 'Preview'

class MediaWallpostAdmin(PolymorphicChildModelAdmin):
    base_model = Wallpost
    readonly_fields = ('ip_address', 'created', 'deleted', 'view_online', 'gallery', 'donation',
                       'share_with_facebook', 'share_with_twitter',
                       'share_with_linkedin', 'email_followers')
    fields = readonly_fields + ('text', 'author', 'editor')
    raw_id_fields = ('author', 'editor')
    list_display = ('created', 'view_online', 'get_text', 'thumbnail', 'author', 'deleted')
    search_fields = ('text', 'author__first_name', 'author__last_name')
    exclude = ('object_id', 'content_type')

    extra_fields = ('gallery',)

    ordering = ('-created',)
    inlines = (MediaWallpostPhotoInline, ReactionInline)

    formfield_overrides = {
        models.URLField: {'widget': SecureAdminURLFieldWidget()},
    }

    def get_text(self, obj):
        if len(obj.text) > 150:
            return format_html(
                u'{} [...]',
                obj.text, obj.text[:145])

    def thumbnail(self, obj):
        data = {}
        if obj.video_url:
            data['video_url'] = obj.video_url
            if 'youtube.com' in obj.video_url:
                try:
                    urlparts = urlparse.urlparse(obj.video_url)
                    data['youtubeid'] = urlparse.parse_qs(urlparts.query)['v'][
                        0]
                except (KeyError, ValueError, IndexError):
                    pass

        photos = MediaWallpostPhoto.objects.filter(mediawallpost=obj)
        data['count'] = len(photos)
        data['remains'] = max(0, data['count'] - 1)

        if len(photos):
            if photos[0].photo:
                data['firstimage'] = get_thumbnail(photos[0].photo, "120x120",
                                                   crop="center").url
                data['firstimage_url'] = photos[0].photo.url

        return mark_safe(render_to_string("admin/wallposts/preview_thumbnail.html", data))

    def view_online(self, obj):
        if obj.content_object is None:
            return _(u'The project this post belongs to has been deleted')

        if obj.content_type.name == 'project':
            return format_html(
                u'{}',
                obj.content_object.slug, obj.content_object.title
            )
        if obj.content_type.name == 'task':
            if obj.content_object:
                return format_html(
                    u'{}',
                    obj.content_object.id,
                    obj.content_object.title
                )
        if obj.content_type.name == 'fundraiser':
            return format_html(
                u'{}',
                obj.content_object.id, obj.content_object.title
            )
        return '---'

    def gallery(self, obj):
        data = {}
        data['images'] = [dict(full=p.photo.url,
                               thumb=get_thumbnail(p.photo, "120x120",
                                                   crop="center").url)
                          for p in obj.photos.all()]

        return mark_safe(
            render_to_string("admin/wallposts/mediawallpost_gallery.html", data)
        )

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

class TextWallpostAdmin(PolymorphicChildModelAdmin):
    base_model = Wallpost
    readonly_fields = ('ip_address', 'created', 'deleted', 'posted_on', 'donation_link')
    search_fields = ('text', 'author__first_name', 'author__last_name')
    list_display = ('created', 'author', 'content_type', 'text', 'deleted')
    raw_id_fields = ('author', 'editor', 'donation')
    fields = readonly_fields + ('text', 'author', 'editor')
    exclude = ('object_id', 'content_type')

    ordering = ('-created',)

    inlines = (ReactionInline, )

    def posted_on(self, obj):
        type = obj.content_type.name
        type_name = unicode(obj.content_type).title()

        if type == 'task':
            url = reverse('admin:tasks_task_change',
                          args=(obj.content_object.id,))
        elif type == 'project':
            url = reverse('admin:projects_project_change',
                          args=(obj.content_object.id,))
        elif type == 'fundraiser':
            url = reverse('admin:fundraisers_fundraiser_change',
                          args=(obj.content_object.id,))
        else:
            return ''

        title = obj.content_object.title
        return format_html(
            u'{}: {}',
            type_name, url, title
        )

    def donation_link(self, obj):
        if obj.donation:
            link = reverse('admin:donations_donation_change', args=(obj.donation.id,))
            return format_html(
                u"{}",
                link, obj.donation
            )

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

class SystemWallpostAdmin(PolymorphicChildModelAdmin):
    base_model = SystemWallpost
    readonly_fields = ('ip_address', 'created', 'content_type', 'related_type',
                       'donation_link', 'project_link',
                       'related_id', 'object_id')
    fields = readonly_fields + ('author', 'donation', 'text')
    list_display = ('created', 'author', 'content_type', 'related_type', 'text', 'deleted')
    raw_id_fields = ('author', 'editor', 'donation')
    ordering = ('-created',)
    exclude = ('object_id', 'content_type')

    inlines = (ReactionInline, )

    def project_link(self, obj):
        if obj.donation:
            link = reverse('admin:projects_project_change', args=(obj.donation.project.id,))
            return format_html(
                u"{}",
                link, obj.donation.project.title
            )
    project_link.short_description = _('Project link')

    def donation_link(self, obj):
        if obj.donation:
            link = reverse('admin:donations_donation_change', args=(obj.donation.id,))
            return format_html(
                u"{}",
                link, obj.donation
            )

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

class WallpostParentAdmin(PolymorphicParentModelAdmin):
    """ The parent model admin """
    base_model = Wallpost
    list_display = ('created', 'author', 'content_type', 'text', 'type', 'deleted')
    fields = ('title', 'text', 'author', 'ip_address')
    list_filter = ('created', ('content_type', admin.RelatedOnlyFieldListFilter),)
    ordering = ('-created',)
    search_fields = (
        'textwallpost__text', 'mediawallpost__text',
        'author__username', 'author__email',
        'author__first_name', 'author__last_name', 'ip_address'
    )
    child_models = (
        (MediaWallpost, MediaWallpostAdmin),
        (TextWallpost, TextWallpostAdmin),
        (SystemWallpost, SystemWallpostAdmin),
    )

    def type(self, obj):
        return obj.get_real_instance_class().__name__

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

    def text(self, obj):
        text = '-empty-'
        try:
            text = obj.systemwallpost.text
        except SystemWallpost.DoesNotExist:
            pass
        try:
            text = obj.textwallpost.text
        except TextWallpost.DoesNotExist:
            pass
        try:
            text = obj.mediawallpost.text
        except MediaWallpost.DoesNotExist:
            pass
        if len(text) > 40:
            return format_html(text[:38] + '…')
        return text

admin.site.register(Wallpost, WallpostParentAdmin)
admin.site.register(MediaWallpost, MediaWallpostAdmin)
admin.site.register(TextWallpost, TextWallpostAdmin)
admin.site.register(SystemWallpost, SystemWallpostAdmin)

class ReactionAdmin(admin.ModelAdmin):
    # created and updated are auto-set fields. author, editor and ip_address are auto-set on save.
    readonly_fields = ('project_url', 'created', 'updated', 'author',
                       'editor', 'ip_address')
    list_display = ('author_full_name', 'created', 'updated',
                    'deleted', 'ip_address')
    date_hierarchy = 'created'
    ordering = ('-created',)
    raw_id_fields = ('author', 'editor', 'wallpost')
    search_fields = ('text', 'author__username', 'author__email',
                     'author__first_name', 'author__last_name', 'ip_address')

    fields = ('text', 'project_url', 'wallpost', 'deleted', 'created',
              'updated', 'author', 'editor', 'ip_address')

    def get_fieldsets(self, request, obj=None):
        """ Only show the relevant fields when adding a Reaction. """
        if obj:  # editing an existing object
            return super(ReactionAdmin, self).get_fieldsets(request, obj)
        return [(None, {'fields': ('wallpost', 'text')})]

    def author_full_name(self, obj):
        full_name = obj.author.get_full_name()
        if not full_name:
            return obj.author.username
        else:
            return full_name

    author_full_name.short_description = _('Author')

    def project_url(self, obj):
        project = obj.wallpost.content_object
        if project.__class__.__name__ == 'Project':
            url = project.get_absolute_url()
            return format_html(
                u"{}",
                str(url), project.title
            )
        return ''

    project_url.short_description = _('project link')

    def save_model(self, request, obj, form, change):
        """ Set the author or editor (as required) and ip when saving the model. """
        set_author_editor_ip(request, obj)
        super(ReactionAdmin, self).save_model(request, obj, form, change)

    def get_queryset(self, request):
        """ The Admin needs to show all the Reactions. """
        return self.model.objects_with_deleted.all()

admin.site.register(Reaction, ReactionAdmin)
<|endoftext|>


<commit_before>"""
Interfaces for reading configuration.
"""

class Config(object):
    """
    Interface for reading a configuration.
    The config is read only.
    """

    @property
    def lookup(self):
        """
        Get the lookup object used for interpolation.
        :return StrLookup: The lookup object.
        """
        raise NotImplemented()

    @lookup.setter
    def lookup(self, value):
        """
        Set the lookup object used for interpolation.
        :param StrLookup value: The lookup object.
        """
        raise NotImplemented()

    @property
    def updated(self):
        """
        Get the updated event handler.
        :return utils.event.EventHandler: The event handler.
        """
        raise NotImplemented()

    def get(self, key, default=None, cast=None):
        """
        Get the value for given key if key is in the configuration, otherwise default.
        :param str key: The key to be found.
        :param default: The default value if the key is not found.
        :param cast: The data type to convert the value to.
        :return: The value found, otherwise default.
        """
        raise NotImplemented()

    def load(self):
        """
        Load all the keys and values.
        This method does not trigger the updated event.
        """
        raise NotImplemented()

    def prefixed(self, prefix):
        """
        Get a subset of the configuration prefixed by a key.
        :param str prefix: The prefix to prepend to the keys.
        :return abc.Config: The subset of the configuration prefixed by a key.
        """
        raise NotImplemented()

class CompositeConfig(Config):
    """
    Config that is a composite of multiple configuration and as such doesn't track
    properties of its own.

    The composite does not merge the configurations but instead treats them as
    overrides so that a property existing in a configuration supersedes the same
    property in configuration based on some ordering.

    Implementations of this interface should specify and implement the override ordering.
    """

    def add_config(self, name, config):
        """
        Add a named configuration.
        Duplicate configurations are not allowed.
        :param str name: The name of the configuration.
        :param Config config: The configuration.
        """
        raise NotImplemented()

    def get_config(self, name):
        """
        Get a configuration by name.
        :param str name: The name of the configuration.
        :return Config: The configuration found or None.
        """
        raise NotImplemented()

    def get_config_names(self):
        """
        Get the names of all configurations previously added.
        :return list: The list of configuration names.
        """
        raise NotImplemented()

    def remove_config(self, name):
        """
        Remove a configuration by name.
        :param str name: The name of the configuration
        :return Config: The configuration removed or None if not found.
        """
        raise NotImplemented()

class Reader(object):
    """
    Interface for reading a configuration from a specific stream as a dict.
    """

    def read(self, stream):
        """
        Read the given stream and returns it as a dict.
        :param stream: The stream to read the configuration from.
        :return dict: The configuration read from the stream.
        """
        raise NotImplemented()

class Decoder(object):
    """
    Interface decoding values to arbitrary types.
    """

    def decode(self, o, cast):
        """
        Decode the given value to the given data type.
        :param o: The value to be decoded.
        :param cast: The format to be decoded.
        :return: The value decoded.
        """
        raise NotImplemented()

class Scheduler(object):
    """
    Interface for scheduling execution.
    It is intended to be used for polling configuration.
    """

    def schedule(self, func):
        """
        Schedule a given func to be executed in the future.
        :param func: The function to be executed.
        """
        raise NotImplementedError()

class StrInterpolator(object):
    """
    Interface for interpolating a string.

    The interpolator will extract the key and call the lookup to get the value for that key.
    """

    def resolve(self, value, lookup):
        """
        Resolve a string with replaceable variables using the provided
        lookup object to lookup replacement values.

        An exception is thrown for variables without a replacement value.

        :param str value: The value that contains variables to be resolved.
        :param StrLookup lookup: The lookup object to lookup replacement values.
        :return str: The interpolated string.
        """
        raise NotImplemented()

class StrLookup(object):
    """
    Interface for looking up of a raw string for replacements.
    """

    def lookup(self, key):
        """
        Get the value for the given key.
        :param str key: The key to lookup.
        :return str: The value if found, otherwise None.
        """
        raise NotImplemented()

class PropertyManager(object):
    """
    Interface for managing PropertyContainer objects.

    A PropertyManager is attached to the Config source from which it
    receives notification of value changes.
    """

    def get_property(self, name):
        """
        Get a property for the property name.
        :param str name: The name of the property.
        :return PropertyContainer: The property object.
        """
        raise NotImplemented()

class PropertyContainer(object):
    """
    Interface for a single property that can be parsed as any type.
    """

    def as_type(self, cast, default):
        """
        Get a Property object based on the given type.
        :param cast: The type to convert the value to.
        :param default: The default value.
        :return Property: The property object.
        """
        raise NotImplemented()

class Property(object):
    """
    Interface to access latest cached value for a Property.
    """

    def get(self):
        """
        Get the most recent value of the property.
        :return: The most recent value of the property.
        """
        raise NotImplemented()

    @property
    def updated(self):
        """
        Get the updated event handler.
        :return utils.event.EventHandler: The event handler.
        """
        raise NotImplemented()
<commit_msg>Replace raise NotImplemented by raise NotImplementedError<commit_after>"""
Interfaces for reading configuration.
"""

class Config(object):
    """
    Interface for reading a configuration.
    The config is read only.
    """

    @property
    def lookup(self):
        """
        Get the lookup object used for interpolation.
        :return StrLookup: The lookup object.
        """
        raise NotImplementedError()

    @lookup.setter
    def lookup(self, value):
        """
        Set the lookup object used for interpolation.
        :param StrLookup value: The lookup object.
        """
        raise NotImplementedError()

    @property
    def updated(self):
        """
        Get the updated event handler.
        :return utils.event.EventHandler: The event handler.
        """
        raise NotImplementedError()

    def get(self, key, default=None, cast=None):
        """
        Get the value for given key if key is in the configuration, otherwise default.
        :param str key: The key to be found.
        :param default: The default value if the key is not found.
        :param cast: The data type to convert the value to.
        :return: The value found, otherwise default.
        """
        raise NotImplementedError()

    def load(self):
        """
        Load all the keys and values.
        This method does not trigger the updated event.
        """
        raise NotImplementedError()

    def prefixed(self, prefix):
        """
        Get a subset of the configuration prefixed by a key.
        :param str prefix: The prefix to prepend to the keys.
        :return abc.Config: The subset of the configuration prefixed by a key.
        """
        raise NotImplementedError()

class CompositeConfig(Config):
    """
    Config that is a composite of multiple configuration and as such doesn't track
    properties of its own.

    The composite does not merge the configurations but instead treats them as
    overrides so that a property existing in a configuration supersedes the same
    property in configuration based on some ordering.

    Implementations of this interface should specify and implement the override ordering.
    """

    def add_config(self, name, config):
        """
        Add a named configuration.
        Duplicate configurations are not allowed.
        :param str name: The name of the configuration.
        :param Config config: The configuration.
        """
        raise NotImplementedError()

    def get_config(self, name):
        """
        Get a configuration by name.
        :param str name: The name of the configuration.
        :return Config: The configuration found or None.
        """
        raise NotImplementedError()

    def get_config_names(self):
        """
        Get the names of all configurations previously added.
        :return list: The list of configuration names.
        """
        raise NotImplementedError()

    def remove_config(self, name):
        """
        Remove a configuration by name.
        :param str name: The name of the configuration
        :return Config: The configuration removed or None if not found.
        """
        raise NotImplementedError()

class Reader(object):
    """
    Interface for reading a configuration from a specific stream as a dict.
    """

    def read(self, stream):
        """
        Read the given stream and returns it as a dict.
        :param stream: The stream to read the configuration from.
        :return dict: The configuration read from the stream.
        """
        raise NotImplementedError()

class Decoder(object):
    """
    Interface decoding values to arbitrary types.
    """

    def decode(self, o, cast):
        """
        Decode the given value to the given data type.
        :param o: The value to be decoded.
        :param cast: The format to be decoded.
        :return: The value decoded.
        """
        raise NotImplementedError()

class Scheduler(object):
    """
    Interface for scheduling execution.
    It is intended to be used for polling configuration.
    """

    def schedule(self, func):
        """
        Schedule a given func to be executed in the future.
        :param func: The function to be executed.
        """
        raise NotImplementedError()

class StrInterpolator(object):
    """
    Interface for interpolating a string.

    The interpolator will extract the key and call the lookup to get the value for that key.
    """

    def resolve(self, value, lookup):
        """
        Resolve a string with replaceable variables using the provided
        lookup object to lookup replacement values.

        An exception is thrown for variables without a replacement value.

        :param str value: The value that contains variables to be resolved.
        :param StrLookup lookup: The lookup object to lookup replacement values.
        :return str: The interpolated string.
        """
        raise NotImplementedError()

class StrLookup(object):
    """
    Interface for looking up of a raw string for replacements.
    """

    def lookup(self, key):
        """
        Get the value for the given key.
        :param str key: The key to lookup.
        :return str: The value if found, otherwise None.
        """
        raise NotImplementedError()

class PropertyManager(object):
    """
    Interface for managing PropertyContainer objects.

    A PropertyManager is attached to the Config source from which it
    receives notification of value changes.
    """

    def get_property(self, name):
        """
        Get a property for the property name.
        :param str name: The name of the property.
        :return PropertyContainer: The property object.
        """
        raise NotImplementedError()

class PropertyContainer(object):
    """
    Interface for a single property that can be parsed as any type.
    """

    def as_type(self, cast, default):
        """
        Get a Property object based on the given type.
        :param cast: The type to convert the value to.
        :param default: The default value.
        :return Property: The property object.
        """
        raise NotImplementedError()

class Property(object):
    """
    Interface to access latest cached value for a Property.
    """

    def get(self):
        """
        Get the most recent value of the property.
        :return: The most recent value of the property.
        """
        raise NotImplementedError()

    @property
    def updated(self):
        """
        Get the updated event handler.
        :return utils.event.EventHandler: The event handler.
        """
        raise NotImplementedError()
<|endoftext|>


<commit_before>"""The inner workings of the command system."""

from inspect import signature

from functools import wraps

import re

from logging import getLogger

from collections import OrderedDict

class CommandMeta(type):
    """Manage the backend of commands via a metaclass."""

    def __new__(mcs, name, bases, attrs):
        subcommands = OrderedDict()
        for value in attrs.values():
            if getattr(value, "_subcommand", None):
                command = value.__annotations__.get("return") or value.__name__
                subcommands[str(command)] = value
        attrs["subcommands"] = subcommands
        if attrs.get("__command__") is None:
            attrs["__command__"] = name.lower()
        return super().__new__(mcs, name, bases, attrs)

class Command(metaclass=CommandMeta):
    """Parent all command classes."""

    __command__ = None

    REGEX = {
        "username": r'@?([A-Za-z0-9]{,32})',
        "command": r'!?(.+)'
    }

    def __init__(self, api):
        self.logger = getLogger(__name__)

        self.api = api

    async def __call__(self, *args, **data):
        # TODO: user levels
        # TODO: secret subcommands | subcommand(secret=True)
        # TODO: service-specific commands
        # TODO: emote, link, etc. parsing

        subcommand = None

        if args:
            subcommand = self.subcommands.get(args[0], False)
            if subcommand:
                return await self.inject(
                    await subcommand(self, *args, **data),
                    *args, **data
                )

        defaults = tuple(
            s for s in self.subcommands.values()
            if getattr(s, "_default", False)
        )

        if len(defaults) == 1:
            args = (defaults[0].__command__,) + args
            return await self.inject(
                await defaults[0](self, *args, **data),
                *args, **data
            )
        elif defaults:
            self.logger.warning("Multiple defaults defined.")

        if subcommand is False:
            return "Invalid argument: '{}'.".format(args[0])

        return "Not enough arguments. (!{} <{}>)".format(
            self.__command__, '|'.join(self.subcommands.keys())
        )  # FIXME: command OrderedDict ordering

    @staticmethod
    def subcommand(function=None, *, default=False):
        """Decorate a subcommand."""

        def decorator(function):

            if default:
                function._default = True

            function.__command__ = function.__annotations__.get(
                "return", function.__name__).replace(' ', '_')

            params = list(signature(function).parameters.values())

            @wraps(function)
            async def wrapped(self, *args, **data):
                """Parse subcommand data."""

                args = list(args)

                args_params = tuple(
                    p for p in params if p.kind is p.POSITIONAL_OR_KEYWORD)
                star_param = next(
                    (p for p in params if p.kind is p.VAR_POSITIONAL), None)

                data_params = tuple(
                    p for p in params if p.kind is p.KEYWORD_ONLY)
                kwargs = {p.name: data.get(p.annotation) for p in data_params}

                arg_range = (
                    len(tuple(
                        p for p in args_params if p.default is p.empty
                    )) + (
                        bool(star_param) if star_param and
                        star_param.annotation else 0
                    ),
                    float('inf') if star_param else len(args_params)
                )

                if not arg_range[0] <= len(args) <= arg_range[1]:

                    if star_param is not None:
                        args_params += (star_param,)
                    syntax = "!{command} {sub} <{params}>".format(
                        command=self.__command__, sub=function.__command__,
                        params='> <'.join(p.name for p in args_params[1:])
                    )

                    if len(args) < arg_range[0]:
                        return "Not enough arguments. ({})".format(syntax)
                    elif len(args) > arg_range[1]:
                        return "Too many arguments. ({})".format(syntax)

                for index, argument in enumerate(args_params[:len(args)]):
                    annotation = argument.annotation
                    if annotation is not argument.empty:
                        if isinstance(annotation, str):
                            if annotation.startswith('?'):
                                annotation = self.REGEX.get(annotation[1:], '')
                            match = re.match('^'+annotation+'$', args[index])
                            if match is None:
                                return "Invalid {type}: '{value}'.".format(
                                    type=argument.name, value=args[index])
                            else:
                                groups = match.groups()
                                if len(groups) == 1:
                                    args[index] = groups[0]
                                elif len(groups) > 1:
                                    args[index] = groups
                        else:
                            self.logger.warning(
                                "Invalid regex: '%s.%s.%s : %s'.",
                                self.__command__, function.__command__,
                                argument.name, annotation
                            )

                return await function(self, *args[1:], **kwargs)

            wrapped._subcommand = True

            return wrapped

        return decorator(function) if function is not None else decorator

    @staticmethod
    async def inject(response, *args, **data):
        """Inject targets into a response."""

        response = response.replace("%USER%", data.get("username", "%USER%"))

        try:
            response = re.sub(
                r'%ARG(\d+)%',
                lambda match: args[int(match.group(1))],
                response
            )
        except IndexError:
            return "Not enough arguments!"

        response = response.replace("%ARGS%", ' '.join(args))

        # TODO: implement count
        response = response.replace("%COUNT%", "%COUNT%")

        response = response.replace(
            "%CHANNEL%",
            data.get("channel", "%CHANNEL%")
        )

        return response
<commit_msg>Fix docstrings<commit_after>"""The inner workings of the command system."""

from inspect import signature

from functools import wraps

import re

from logging import getLogger

from collections import OrderedDict

class CommandMeta(type):
    """Manage the backend of commands via a metaclass."""

    def __new__(mcs, name, bases, attrs):
        subcommands = OrderedDict()
        for value in attrs.values():
            if getattr(value, "_subcommand", None):
                command = value.__annotations__.get("return") or value.__name__
                subcommands[str(command)] = value
        attrs["subcommands"] = subcommands
        if attrs.get("__command__") is None:
            attrs["__command__"] = name.lower()
        return super().__new__(mcs, name, bases, attrs)

class Command(metaclass=CommandMeta):
    """Parent all command classes."""

    __command__ = None

    REGEX = {
        "username": r'@?([A-Za-z0-9]{,32})',
        "command": r'!?(.+)'
    }

    def __init__(self, api):
        self.logger = getLogger(__name__)

        self.api = api

    async def __call__(self, *args, **data):
        # TODO: user levels
        # TODO: secret subcommands | subcommand(secret=True)
        # TODO: service-specific commands
        # TODO: emote, link, etc. parsing

        subcommand = None

        if args:
            subcommand = self.subcommands.get(args[0], False)
            if subcommand:
                return await self.inject(
                    await subcommand(self, *args, **data),
                    *args, **data
                )

        defaults = tuple(
            s for s in self.subcommands.values()
            if getattr(s, "_default", False)
        )

        if len(defaults) == 1:
            args = (defaults[0].__command__,) + args
            return await self.inject(
                await defaults[0](self, *args, **data),
                *args, **data
            )
        elif defaults:
            self.logger.warning("Multiple defaults defined.")

        if subcommand is False:
            return "Invalid argument: '{}'.".format(args[0])

        return "Not enough arguments. (!{} <{}>)".format(
            self.__command__, '|'.join(self.subcommands.keys())
        )  # FIXME: command OrderedDict ordering

    @staticmethod
    def subcommand(function=None, *, default=False):
        """Decorate a subcommand with optional callability."""

        def decorator(function):
            """Decorate a subcommand."""

            if default:
                function._default = True

            function.__command__ = function.__annotations__.get(
                "return", function.__name__).replace(' ', '_')

            params = list(signature(function).parameters.values())

            @wraps(function)
            async def wrapped(self, *args, **data):
                """Parse subcommand data."""

                args = list(args)

                args_params = tuple(
                    p for p in params if p.kind is p.POSITIONAL_OR_KEYWORD)
                star_param = next(
                    (p for p in params if p.kind is p.VAR_POSITIONAL), None)

                data_params = tuple(
                    p for p in params if p.kind is p.KEYWORD_ONLY)
                kwargs = {p.name: data.get(p.annotation) for p in data_params}

                arg_range = (
                    len(tuple(
                        p for p in args_params if p.default is p.empty
                    )) + (
                        bool(star_param) if star_param and
                        star_param.annotation else 0
                    ),
                    float('inf') if star_param else len(args_params)
                )

                if not arg_range[0] <= len(args) <= arg_range[1]:

                    if star_param is not None:
                        args_params += (star_param,)
                    syntax = "!{command} {sub} <{params}>".format(
                        command=self.__command__, sub=function.__command__,
                        params='> <'.join(p.name for p in args_params[1:])
                    )

                    if len(args) < arg_range[0]:
                        return "Not enough arguments. ({})".format(syntax)
                    elif len(args) > arg_range[1]:
                        return "Too many arguments. ({})".format(syntax)

                for index, argument in enumerate(args_params[:len(args)]):
                    annotation = argument.annotation
                    if annotation is not argument.empty:
                        if isinstance(annotation, str):
                            if annotation.startswith('?'):
                                annotation = self.REGEX.get(annotation[1:], '')
                            match = re.match('^'+annotation+'$', args[index])
                            if match is None:
                                return "Invalid {type}: '{value}'.".format(
                                    type=argument.name, value=args[index])
                            else:
                                groups = match.groups()
                                if len(groups) == 1:
                                    args[index] = groups[0]
                                elif len(groups) > 1:
                                    args[index] = groups
                        else:
                            self.logger.warning(
                                "Invalid regex: '%s.%s.%s : %s'.",
                                self.__command__, function.__command__,
                                argument.name, annotation
                            )

                return await function(self, *args[1:], **kwargs)

            wrapped._subcommand = True

            return wrapped

        return decorator(function) if function is not None else decorator

    @staticmethod
    async def inject(response, *args, **data):
        """Inject targets into a response."""

        response = response.replace("%USER%", data.get("username", "%USER%"))

        try:
            response = re.sub(
                r'%ARG(\d+)%',
                lambda match: args[int(match.group(1))],
                response
            )
        except IndexError:
            return "Not enough arguments!"

        response = response.replace("%ARGS%", ' '.join(args))

        # TODO: implement count
        response = response.replace("%COUNT%", "%COUNT%")

        response = response.replace(
            "%CHANNEL%",
            data.get("channel", "%CHANNEL%")
        )

        return response
<|endoftext|>