<commit_before># SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0

# pylint: disable=W0621  # redefined-outer-name

# This file is a pytest root configuration file and provide the following functionalities:
# 1. Defines a few fixtures that could be used under the whole project.
# 2. Defines a few hook functions.
#
# IDF is using [pytest](https://github.com/pytest-dev/pytest) and
# [pytest-embedded plugin](https://github.com/espressif/pytest-embedded) as its example test framework.
#
# This is an experimental feature, and if you found any bug or have any question, please report to
# https://github.com/espressif/pytest-embedded/issues

import logging
import os
import sys
import xml.etree.ElementTree as ET
from typing import Callable, List, Optional

import pytest
from _pytest.config import Config
from _pytest.fixtures import FixtureRequest
from _pytest.main import Session
from _pytest.nodes import Item
from _pytest.python import Function
from pytest_embedded.plugin import parse_configuration
from pytest_embedded.utils import find_by_suffix

SUPPORTED_TARGETS = ['esp32', 'esp32s2', 'esp32c3', 'esp32s3']
PREVIEW_TARGETS = ['linux', 'esp32h2', 'esp32c2']
DEFAULT_SDKCONFIG = 'default'

##################
# Help Functions #
##################
def is_target_marker(marker: str) -> bool:
    if marker.startswith('esp32'):
        return True

    if marker.startswith('esp8'):
        return True

    return False

def format_case_id(target: Optional[str], config: Optional[str], case: str) -> str:
    return f'{target}.{config}.{case}'

def item_marker_names(item: Item) -> List[str]:
    return [marker.name for marker in item.iter_markers()]

############
# Fixtures #
############
@pytest.fixture
def config(request: FixtureRequest) -> str:
    return getattr(request, 'param', None) or DEFAULT_SDKCONFIG

@pytest.fixture
def test_func_name(request: FixtureRequest) -> str:
    return request.node.function.__name__  # type: ignore

@pytest.fixture
def test_case_name(request: FixtureRequest, target: str, config: str) -> str:
    return format_case_id(target, config, request.node.originalname)

@pytest.fixture
@parse_configuration
def build_dir(
    request: FixtureRequest, app_path: str, target: Optional[str], config: Optional[str]
) -> str:
    """
    Check local build dir with the following priority:

    1. build_<target>_<config>
    2. build_<target>
    3. build_<config>
    4. build

    Args:
        request: pytest fixture
        app_path: app path
        target: target
        config: config

    Returns:
        valid build directory
    """
    param_or_cli: str = getattr(
        request, 'param', None
    ) or request.config.getoption('build_dir')
    if param_or_cli is not None:  # respect the param and the cli
        return param_or_cli

    check_dirs = []
    if target is not None and config is not None:
        check_dirs.append(f'build_{target}_{config}')
    if target is not None:
        check_dirs.append(f'build_{target}')
    if config is not None:
        check_dirs.append(f'build_{config}')
    check_dirs.append('build')

    for check_dir in check_dirs:
        binary_path = os.path.join(app_path, check_dir)
        if os.path.isdir(binary_path):
            logging.info(f'find valid binary path: {binary_path}')
            return check_dir

        logging.warning(
            'checking binary path: %s... missing... try another place', binary_path
        )

    recommend_place = check_dirs[0]
    logging.error(
        f'no build dir valid. Please build the binary via "idf.py -B {recommend_place} build" and run pytest again'
    )
    sys.exit(1)

@pytest.fixture(autouse=True)
def junit_properties(
    test_case_name: str, record_xml_attribute: Callable[[str, object], None]
) -> None:
    """
    This fixture is autoused and will modify the junit report test case name to <target>.<config>.<case_name>
    """
    record_xml_attribute('name', test_case_name)

##################
# Hook functions #
##################
def pytest_addoption(parser: pytest.Parser) -> None:
    base_group = parser.getgroup('idf')
    base_group.addoption(
        '--sdkconfig',
        help='sdkconfig postfix, like sdkconfig.ci.<config>. (Default: None, which would build all found apps)',
    )

@pytest.hookimpl(tryfirst=True)
def pytest_sessionstart(session: Session) -> None:
    if session.config.option.target:
        session.config.option.target = session.config.getoption('target').lower()

@pytest.hookimpl(tryfirst=True)
def pytest_collection_modifyitems(config: Config, items: List[Function]) -> None:
    target = config.getoption('target', None)  # use the `build` dir
    if not target:
        return

    # sort by file path and callspec.config
    # implement like this since this is a limitation of pytest, couldn't get fixture values while collecting
    # https://github.com/pytest-dev/pytest/discussions/9689
    def _get_param_config(_item: Function) -> str:
        if hasattr(_item, 'callspec'):
            return _item.callspec.params.get('config', DEFAULT_SDKCONFIG)  # type: ignore
        return DEFAULT_SDKCONFIG

    items.sort(key=lambda x: (os.path.dirname(x.path), _get_param_config(x)))

    # add markers for special markers
    for item in items:
        if 'supported_targets' in item_marker_names(item):
            for _target in SUPPORTED_TARGETS:
                item.add_marker(_target)
        if 'preview_targets' in item_marker_names(item):
            for _target in PREVIEW_TARGETS:
                item.add_marker(_target)
        if 'all_targets' in item_marker_names(item):
            for _target in [*SUPPORTED_TARGETS, *PREVIEW_TARGETS]:
                item.add_marker(_target)

    # filter all the test cases with "--target"
    items[:] = [item for item in items if target in item_marker_names(item)]

    # filter all the test cases with cli option "config"
    if config.getoption('sdkconfig'):
        items[:] = [
            item
            for item in items
            if _get_param_config(item) == config.getoption('sdkconfig')
        ]

@pytest.hookimpl(trylast=True)
def pytest_runtest_teardown(item: Function) -> None:
    """
    Format the test case generated junit reports
    """
    tempdir = item.funcargs.get('test_case_tempdir')
    if not tempdir:
        return

    junits = find_by_suffix('.xml', tempdir)
    if not junits:
        return

    target = item.funcargs['target']
    config = item.funcargs['config']
    for junit in junits:
        xml = ET.parse(junit)
        testcases = xml.findall('.//testcase')
        for case in testcases:
            case.attrib['name'] = format_case_id(target, config, case.attrib['name'])
            if 'file' in case.attrib:
                case.attrib['file'] = case.attrib['file'].replace(
                    '/IDF/', ''
                )  # our unity test framework
        xml.write(junit)
<commit_msg>ci(pytest): move to class plugin<commit_after># SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0

# pylint: disable=W0621  # redefined-outer-name

# This file is a pytest root configuration file and provide the following functionalities:
# 1. Defines a few fixtures that could be used under the whole project.
# 2. Defines a few hook functions.
#
# IDF is using [pytest](https://github.com/pytest-dev/pytest) and
# [pytest-embedded plugin](https://github.com/espressif/pytest-embedded) as its example test framework.
#
# This is an experimental feature, and if you found any bug or have any question, please report to
# https://github.com/espressif/pytest-embedded/issues

import logging
import os
import sys
import xml.etree.ElementTree as ET
from typing import Callable, List, Optional

import pytest
from _pytest.config import Config
from _pytest.fixtures import FixtureRequest
from _pytest.main import Session
from _pytest.nodes import Item
from _pytest.python import Function
from pytest_embedded.plugin import parse_configuration
from pytest_embedded.utils import find_by_suffix

SUPPORTED_TARGETS = ['esp32', 'esp32s2', 'esp32c3', 'esp32s3']
PREVIEW_TARGETS = ['linux', 'esp32h2', 'esp32c2']
DEFAULT_SDKCONFIG = 'default'

##################
# Help Functions #
##################
def is_target_marker(marker: str) -> bool:
    if marker.startswith('esp32'):
        return True

    if marker.startswith('esp8'):
        return True

    return False

def format_case_id(target: Optional[str], config: Optional[str], case: str) -> str:
    return f'{target}.{config}.{case}'

def item_marker_names(item: Item) -> List[str]:
    return [marker.name for marker in item.iter_markers()]

############
# Fixtures #
############
@pytest.fixture
def config(request: FixtureRequest) -> str:
    return getattr(request, 'param', None) or DEFAULT_SDKCONFIG

@pytest.fixture
def test_func_name(request: FixtureRequest) -> str:
    return request.node.function.__name__  # type: ignore

@pytest.fixture
def test_case_name(request: FixtureRequest, target: str, config: str) -> str:
    return format_case_id(target, config, request.node.originalname)

@pytest.fixture
@parse_configuration
def build_dir(
    request: FixtureRequest, app_path: str, target: Optional[str], config: Optional[str]
) -> str:
    """
    Check local build dir with the following priority:

    1. build_<target>_<config>
    2. build_<target>
    3. build_<config>
    4. build

    Args:
        request: pytest fixture
        app_path: app path
        target: target
        config: config

    Returns:
        valid build directory
    """
    param_or_cli: str = getattr(
        request, 'param', None
    ) or request.config.getoption('build_dir')
    if param_or_cli is not None:  # respect the param and the cli
        return param_or_cli

    check_dirs = []
    if target is not None and config is not None:
        check_dirs.append(f'build_{target}_{config}')
    if target is not None:
        check_dirs.append(f'build_{target}')
    if config is not None:
        check_dirs.append(f'build_{config}')
    check_dirs.append('build')

    for check_dir in check_dirs:
        binary_path = os.path.join(app_path, check_dir)
        if os.path.isdir(binary_path):
            logging.info(f'find valid binary path: {binary_path}')
            return check_dir

        logging.warning(
            'checking binary path: %s... missing... try another place', binary_path
        )

    recommend_place = check_dirs[0]
    logging.error(
        f'no build dir valid. Please build the binary via "idf.py -B {recommend_place} build" and run pytest again'
    )
    sys.exit(1)

@pytest.fixture(autouse=True)
def junit_properties(
    test_case_name: str, record_xml_attribute: Callable[[str, object], None]
) -> None:
    """
    This fixture is autoused and will modify the junit report test case name to <target>.<config>.<case_name>
    """
    record_xml_attribute('name', test_case_name)

##################
# Hook functions #
##################
def pytest_addoption(parser: pytest.Parser) -> None:
    base_group = parser.getgroup('idf')
    base_group.addoption(
        '--sdkconfig',
        help='sdkconfig postfix, like sdkconfig.ci.<config>. (Default: None, which would build all found apps)',
    )

_idf_pytest_embedded_key = pytest.StashKey['IdfPytestEmbedded']

def pytest_configure(config: Config) -> None:
    config.stash[_idf_pytest_embedded_key] = IdfPytestEmbedded(
        target=config.getoption('target'),
        sdkconfig=config.getoption('sdkconfig'),
    )
    config.pluginmanager.register(config.stash[_idf_pytest_embedded_key])

def pytest_unconfigure(config: Config) -> None:
    _pytest_embedded = config.stash.get(_idf_pytest_embedded_key, None)
    if _pytest_embedded:
        del config.stash[_idf_pytest_embedded_key]
        config.pluginmanager.unregister(_pytest_embedded)

class IdfPytestEmbedded:

    def __init__(self, target: Optional[str] = None, sdkconfig: Optional[str] = None):
        # CLI options to filter the test cases
        self.target = target
        self.sdkconfig = sdkconfig

    @pytest.hookimpl(tryfirst=True)
    def pytest_sessionstart(self, session: Session) -> None:
        if self.target:
            self.target = self.target.lower()
            session.config.option.target = self.target

    @pytest.hookimpl(tryfirst=True)
    def pytest_collection_modifyitems(self, items: List[Function]) -> None:
        # sort by file path and callspec.config
        # implement like this since this is a limitation of pytest, couldn't get fixture values while collecting
        # https://github.com/pytest-dev/pytest/discussions/9689
        def _get_param_config(_item: Function) -> str:
            if hasattr(_item, 'callspec'):
                return _item.callspec.params.get('config', DEFAULT_SDKCONFIG)  # type: ignore
            return DEFAULT_SDKCONFIG

        items.sort(key=lambda x: (os.path.dirname(x.path), _get_param_config(x)))

        # add markers for special markers
        for item in items:
            if 'supported_targets' in item_marker_names(item):
                for _target in SUPPORTED_TARGETS:
                    item.add_marker(_target)
            if 'preview_targets' in item_marker_names(item):
                for _target in PREVIEW_TARGETS:
                    item.add_marker(_target)
            if 'all_targets' in item_marker_names(item):
                for _target in [*SUPPORTED_TARGETS, *PREVIEW_TARGETS]:
                    item.add_marker(_target)

        # filter all the test cases with "--target"
        if self.target:
            items[:] = [item for item in items if self.target in item_marker_names(item)]

        # filter all the test cases with cli option "config"
        if self.sdkconfig:
            items[:] = [
                item
                for item in items
                if _get_param_config(item) == self.sdkconfig
            ]

    @pytest.hookimpl(trylast=True)
    def pytest_runtest_teardown(self, item: Function) -> None:
        """
        Format the test case generated junit reports
        """
        tempdir = item.funcargs.get('test_case_tempdir')
        if not tempdir:
            return

        junits = find_by_suffix('.xml', tempdir)
        if not junits:
            return

        target = item.funcargs['target']
        config = item.funcargs['config']
        for junit in junits:
            xml = ET.parse(junit)
            testcases = xml.findall('.//testcase')
            for case in testcases:
                case.attrib['name'] = format_case_id(target, config, case.attrib['name'])
                if 'file' in case.attrib:
                    case.attrib['file'] = case.attrib['file'].replace(
                        '/IDF/', ''
                    )  # our unity test framework
            xml.write(junit)
<|endoftext|>


<commit_before>from django.contrib import admin

from django_object_actions import (
    DjangoObjectActions,
    takes_instance_or_queryset,
)

from easy_select2 import select2_modelform

from .models import (
    Convention,
    Contest,
    Contestant,
    Group,
    District,
    Song,
    Person,
    Performance,
    Singer,
    Director,
    Chart,
)

class PerformancesInline(admin.TabularInline):
    form = select2_modelform(
        Performance,
        attrs={'width': '100px'},
    )
    fields = (
        'contestant',
        'round',
        'order',
        'chart',
        'mus_points',
        'prs_points',
        'sng_points',
    )
    ordering = (
        'round',
        'order',
    )
    model = Performance
    extra = 0
    raw_id_fields = (
        'contestant',
        'chart',
    )
    can_delete = True
    show_change_link = True

class SingersInline(admin.TabularInline):
    form = select2_modelform(
        Singer,
        attrs={'width': '100px'},
    )
    fields = (
        'contestant',
        'person',
        'part',
    )
    ordering = (
        'part',
        'contestant',
    )
    model = Singer
    extra = 0
    raw_id_fields = (
        'person',
        'contestant',
    )
    can_delete = True

class DirectorsInline(admin.TabularInline):
    form = select2_modelform(
        Director,
        attrs={'width': '100px'},
    )
    fields = (
        'contestant',
        'person',
        'part',
    )
    ordering = (
        'part',
        'contestant',
    )
    model = Director
    extra = 0
    raw_id_fields = (
        'person',
        'contestant',
    )
    can_delete = True

class ContestantsInline(admin.TabularInline):
    form = select2_modelform(
        Contestant,
        attrs={'width': '100px'},
    )
    fields = (
        'contest',
        'group',
        'district',
        'draw',
        'stagetime',
        'place',
        'score',
        'men',
    )
    ordering = (
        'draw',
        'place',
    )
    show_change_link = True

    model = Contestant
    extra = 0
    raw_id_fields = (
        'contest',
        'group',
    )
    can_delete = True
    readonly_fields = (
        'group',
    )

@admin.register(Convention)
class ConventionAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Convention,
        attrs={'width': '100px'},
    )

    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'location',
        'dates',
        'kind',
        'year',
        'district',
        'is_active',
    )

    fields = (
        'is_active',
        'name',
        'location',
        'dates',
        'start',
        'end',
        'timezone',
        'district',
        'kind',
        'year',
    )

    list_filter = (
        'kind',
        'year',
        'district',
    )

    readonly_fields = (
        'name',
    )

    save_on_top = True

@admin.register(Contest)
class ContestAdmin(DjangoObjectActions, admin.ModelAdmin):
    @takes_instance_or_queryset
    def import_legacy(self, request, queryset):
        for obj in queryset:
            obj.import_legacy()
    import_legacy.label = 'Import Legacy'
    form = select2_modelform(
        Contest,
        attrs={'width': '100px'},
    )
    save_on_top = True
    objectactions = [
        'import_legacy',
    ]

    inlines = [
        ContestantsInline,
    ]

    search_fields = (
        'name',
    )

    list_filter = (
        'level',
        'kind',
        'year',
        'district',
    )

    list_display = (
        'name',
        'panel',
        'scoresheet_pdf',
        'scoresheet_csv',
        'is_active',
        'is_complete',
        'is_place',
    )

    fields = (
        'status',
        ('is_active', 'is_complete', 'is_place',),
        'name',
        'convention',
        'level',
        'kind',
        'year',
        'district',
        'panel',
        'scoresheet_pdf',
        'scoresheet_csv',
    )

    raw_id_fields = (
        'convention',
    )

    readonly_fields = (
        'name',
    )

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Group,
        attrs={'width': '100px'},
    )

    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'chapter_name',
        'chapter_code',
        'picture',
    )

    fields = (
        'name',
        ('kind', 'is_active',),
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        ('chapter_name', 'chapter_code',),
        'notes',
    )

    list_filter = (
        'kind',
    )

    save_on_top = True

@admin.register(Contestant)
class ContestantAdmin(admin.ModelAdmin):
    @takes_instance_or_queryset
    def update_contestants(self, request, queryset):
        for obj in queryset:
            obj.save()
    update_contestants.label = 'Update Contestants'

    form = select2_modelform(
        Contestant,
        attrs={'width': '150px'},
    )

    objectactions = [
        'update_contestants',
    ]

    inlines = [
        SingersInline,
        DirectorsInline,
        PerformancesInline,
    ]

    list_display = (
        'name',
        'place',
        'score',
        'points',
        'men',
    )

    search_fields = (
        'name',
    )

    list_filter = (
        'contest__level',
        'contest__kind',
        'contest__year',
    )

    raw_id_fields = (
        'contest',
        'group',
    )

    fields = (
        (
            'contest',
        ), (
            'group',
        ), (
            'district',
        ), (
            'seed',
            'prelim',
            'draw',
            'stagetime',
        ), (
            'place',
            'score',
        ),
    )

    readonly_fields = (
        'name',
    )

    save_on_top = True

@admin.register(Song)
class SongAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Song,
        attrs={'width': '100px'},
    )

    save_on_top = True
    fields = (
        'name',
    )

    search_fields = (
        'name',
    )

@admin.register(District)
class DistrictAdmin(admin.ModelAdmin):
    form = select2_modelform(
        District,
        attrs={'width': '100px'},
    )

    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'long_name',
        'kind',
    )

    list_filter = (
        'kind',
    )

    save_on_top = True

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Person,
        attrs={'width': '100px'},
    )

    search_fields = (
        'name',
    )

    save_on_top = True
    list_display = (
        'name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
    )

    fields = (
        'name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        'notes',
    )

    inlines = [
        DirectorsInline,
        SingersInline,
    ]

@admin.register(Performance)
class PerformanceAdmin(admin.ModelAdmin):
    save_on_top = True
    list_display = (
        'name',
        'chart',
        'mus_points',
        'prs_points',
        'sng_points',
    )

    search_fields = (
        'name',
    )

    fields = (
        (
            'name',
        ),
        (
            'contestant',
        ),
        (
            'round',
            'order',
        ),
        (
            'chart',
        ),
        (
            'mus_points',
            'prs_points',
            'sng_points',
        ),
        (
            'penalty',
        ),
    )

    list_filter = (
        'contestant__contest__level',
        'contestant__contest__kind',
        'round',
        'contestant__contest__year',
    )

    readonly_fields = (
        'name',
    )
    raw_id_fields = (
        'contestant',
        'chart',
    )

@admin.register(Chart)
class ChartAdmin(admin.ModelAdmin):
    save_on_top = True
    fields = (
        'name',
        'song',
        'arranger',
    )
    raw_id_fields = (
        'song',
        'arranger',
    )
    search_fields = (
        'name',
    )

<commit_msg>Add M2M to admin<commit_after>from django.contrib import admin

from django_object_actions import (
    DjangoObjectActions,
    takes_instance_or_queryset,
)

from easy_select2 import select2_modelform

from .models import (
    Convention,
    Contest,
    Contestant,
    Group,
    District,
    Song,
    Person,
    Performance,
    Singer,
    Director,
    Chart,
)

class PerformancesInline(admin.TabularInline):
    form = select2_modelform(
        Performance,
        attrs={'width': '100px'},
    )
    fields = (
        'contestant',
        'round',
        'order',
        'chart',
        'mus_points',
        'prs_points',
        'sng_points',
    )
    ordering = (
        'round',
        'order',
    )
    model = Performance
    extra = 0
    raw_id_fields = (
        'contestant',
        'chart',
    )
    can_delete = True
    show_change_link = True

class SingersInline(admin.TabularInline):
    form = select2_modelform(
        Singer,
        attrs={'width': '100px'},
    )
    fields = (
        'contestant',
        'person',
        'part',
    )
    ordering = (
        'part',
        'contestant',
    )
    model = Singer
    extra = 0
    raw_id_fields = (
        'person',
        'contestant',
    )
    can_delete = True

class DirectorsInline(admin.TabularInline):
    form = select2_modelform(
        Director,
        attrs={'width': '100px'},
    )
    fields = (
        'contestant',
        'person',
        'part',
    )
    ordering = (
        'part',
        'contestant',
    )
    model = Director
    extra = 0
    raw_id_fields = (
        'person',
        'contestant',
    )
    can_delete = True

class ContestantsInline(admin.TabularInline):
    form = select2_modelform(
        Contestant,
        attrs={'width': '100px'},
    )
    fields = (
        'contest',
        'group',
        'district',
        'draw',
        'stagetime',
        'place',
        'score',
        'men',
    )
    ordering = (
        'draw',
        'place',
    )
    show_change_link = True

    model = Contestant
    extra = 0
    raw_id_fields = (
        'contest',
        'group',
    )
    can_delete = True
    readonly_fields = (
        'group',
    )

@admin.register(Convention)
class ConventionAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Convention,
        attrs={'width': '100px'},
    )

    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'location',
        'dates',
        'kind',
        'year',
        'district',
        'is_active',
    )

    fields = (
        'is_active',
        'name',
        'location',
        'dates',
        'start',
        'end',
        'timezone',
        'district',
        'kind',
        'year',
    )

    list_filter = (
        'kind',
        'year',
        'district',
    )

    readonly_fields = (
        'name',
    )

    save_on_top = True

@admin.register(Contest)
class ContestAdmin(DjangoObjectActions, admin.ModelAdmin):
    @takes_instance_or_queryset
    def import_legacy(self, request, queryset):
        for obj in queryset:
            obj.import_legacy()
    import_legacy.label = 'Import Legacy'
    form = select2_modelform(
        Contest,
        attrs={'width': '100px'},
    )
    save_on_top = True
    objectactions = [
        'import_legacy',
    ]

    inlines = [
        ContestantsInline,
    ]

    search_fields = (
        'name',
    )

    list_filter = (
        'level',
        'kind',
        'year',
        'district',
    )

    list_display = (
        'name',
        'panel',
        'scoresheet_pdf',
        'scoresheet_csv',
        'is_active',
        'is_complete',
        'is_place',
    )

    fields = (
        'status',
        ('is_active', 'is_complete', 'is_place',),
        'name',
        'convention',
        'level',
        'kind',
        'year',
        'district',
        'panel',
        'scoresheet_pdf',
        'scoresheet_csv',
    )

    raw_id_fields = (
        'convention',
    )

    readonly_fields = (
        'name',
    )

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Group,
        attrs={'width': '100px'},
    )

    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'chapter_name',
        'chapter_code',
        'picture',
    )

    fields = (
        'name',
        ('kind', 'is_active',),
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        ('chapter_name', 'chapter_code',),
        'notes',
    )

    list_filter = (
        'kind',
    )

    save_on_top = True

@admin.register(Contestant)
class ContestantAdmin(admin.ModelAdmin):
    @takes_instance_or_queryset
    def update_contestants(self, request, queryset):
        for obj in queryset:
            obj.save()
    update_contestants.label = 'Update Contestants'

    form = select2_modelform(
        Contestant,
        attrs={'width': '150px'},
    )

    objectactions = [
        'update_contestants',
    ]

    inlines = [
        SingersInline,
        DirectorsInline,
        PerformancesInline,
    ]

    list_display = (
        'name',
        'place',
        'score',
        'points',
        'men',
    )

    search_fields = (
        'name',
    )

    list_filter = (
        'contest__level',
        'contest__kind',
        'contest__year',
    )

    raw_id_fields = (
        'contest',
        'group',
    )

    fields = (
        (
            'contest',
        ), (
            'group',
        ), (
            'district',
        ), (
            'seed',
            'prelim',
            'draw',
            'stagetime',
        ), (
            'place',
            'score',
        ),
    )

    readonly_fields = (
        'name',
    )

    save_on_top = True

@admin.register(Song)
class SongAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Song,
        attrs={'width': '100px'},
    )

    save_on_top = True
    fields = (
        'name',
    )

    search_fields = (
        'name',
    )

@admin.register(District)
class DistrictAdmin(admin.ModelAdmin):
    form = select2_modelform(
        District,
        attrs={'width': '100px'},
    )

    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'long_name',
        'kind',
    )

    list_filter = (
        'kind',
    )

    save_on_top = True

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Person,
        attrs={'width': '100px'},
    )

    search_fields = (
        'name',
    )

    save_on_top = True
    list_display = (
        'name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
    )

    fields = (
        'name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        'notes',
    )

    inlines = [
        DirectorsInline,
        SingersInline,
    ]

@admin.register(Performance)
class PerformanceAdmin(admin.ModelAdmin):
    save_on_top = True
    list_display = (
        'name',
        'chart',
        'mus_points',
        'prs_points',
        'sng_points',
    )

    search_fields = (
        'name',
    )

    fields = (
        (
            'name',
        ),
        (
            'contestant',
        ),
        (
            'round',
            'order',
        ),
        (
            'chart',
        ),
        (
            'mus_points',
            'prs_points',
            'sng_points',
        ),
        (
            'penalty',
        ),
    )

    list_filter = (
        'contestant__contest__level',
        'contestant__contest__kind',
        'round',
        'contestant__contest__year',
    )

    readonly_fields = (
        'name',
    )
    raw_id_fields = (
        'contestant',
        'chart',
    )

@admin.register(Chart)
class ChartAdmin(admin.ModelAdmin):
    save_on_top = True
    fields = (
        'name',
        'song',
        'arranger',
        'songs',
        'arrangers',
    )
    raw_id_fields = (
        'song',
        'arranger',
    )
    search_fields = (
        'name',
    )
    filter_vertical = (
        'songs',
        'arrangers',
    )

<|endoftext|>


<commit_before>###############################################################################
#
# Simple py.test hooks to enable execution and collection of C unit tests.
#
# Copyright 2014, John McNamara, jmcnamara@cpan.org
#
import subprocess
import pytest
import os

def pytest_collect_file(parent, path):
    """
    A hook into py.test to collect test_*.c test files.

    """
    if path.ext == ".c" and path.basename.startswith("test_"):
        return CTestFile(path, parent)

class CTestFile(pytest.File):
    """
    A custom file handler class for C unit test files.

    """

    def collect(self):
        """
        Overridden collect method to collect the results from each
        C unit test executable.

        """
        # Run the exe that corresponds to the .c file and capture the output.
        test_exe = os.path.splitext(str(self.fspath))[0]
        test_output = subprocess.check_output(test_exe)

        # Clean up the unit test output and remove non test data lines.
        lines = test_output.split("\n")
        lines = [line.strip() for line in lines]
        lines = [line for line in lines if line.startswith("[")]

        # Extract the test metadata from the unit test output.
        test_results = []
        for line in lines:
            token, data = line.split(" ", 1)
            token = token[1:-1]

            if token in ("PASS", "FAIL"):
                file_name, function_name, line_number = data.split(":")
                test_results.append({"condition": token,
                                     "file_name": file_name,
                                     "function_name": function_name,
                                     "line_number": int(line_number),
                                     "EXP": 'EXP(no data found)',
                                     "GOT": 'GOT(no data found)',
                                     "TST": 'TST(no data found)',
                                     })
            elif token in ("EXP", "GOT", "TST"):
                test_results[-1][token] = data

        for test_result in test_results:
            yield CTestItem(test_result["function_name"], self, test_result)

class CTestItem(pytest.Item):
    """
    Pytest.Item subclass to handle each test result item. There may be
    more than one test result from a test function.

    """

    def __init__(self, name, parent, test_result):
        """Overridden constructor to pass test results dict."""
        super(CTestItem, self).__init__(name, parent)
        self.test_result = test_result

    def runtest(self):
        """The test has already been run. We just evaluate the result."""
        if self.test_result["condition"] == "FAIL":
            raise CTestException(self, self.name)

    def repr_failure(self, exception):
        """
        Called when runtest() raises an exception. The method is used
        to format the output of the failed test result.

        """
        if isinstance(exception.value, CTestException):
            return ("Test failed : {TST} at {file_name}:{line_number}\n"
                    "         got: {GOT}\n"
                    "    expected: {EXP}\n".format(**self.test_result))

    def reportinfo(self):
        """"Called to display header information about the test case."""
        return self.fspath, self.test_result["line_number"] - 1, self.name

class CTestException(Exception):
    """Custom exception to distinguish C unit test failures from others."""
    pass
<commit_msg>Minor fix for Python3<commit_after>###############################################################################
#
# Simple py.test hooks to enable execution and collection of C unit tests.
#
# Copyright 2014, John McNamara, jmcnamara@cpan.org
#
import subprocess
import pytest
import os

def pytest_collect_file(parent, path):
    """
    A hook into py.test to collect test_*.c test files.

    """
    if path.ext == ".c" and path.basename.startswith("test_"):
        return CTestFile(path, parent)

class CTestFile(pytest.File):
    """
    A custom file handler class for C unit test files.

    """

    def collect(self):
        """
        Overridden collect method to collect the results from each
        C unit test executable.

        """
        # Run the exe that corresponds to the .c file and capture the output.
        test_exe = os.path.splitext(str(self.fspath))[0]
        test_output = subprocess.check_output(test_exe)

        # Clean up the unit test output and remove non test data lines.
        lines = test_output.decode().split("\n")
        lines = [line.strip() for line in lines]
        lines = [line for line in lines if line.startswith("[")]

        # Extract the test metadata from the unit test output.
        test_results = []
        for line in lines:
            token, data = line.split(" ", 1)
            token = token[1:-1]

            if token in ("PASS", "FAIL"):
                file_name, function_name, line_number = data.split(":")
                test_results.append({"condition": token,
                                     "file_name": file_name,
                                     "function_name": function_name,
                                     "line_number": int(line_number),
                                     "EXP": 'EXP(no data found)',
                                     "GOT": 'GOT(no data found)',
                                     "TST": 'TST(no data found)',
                                     })
            elif token in ("EXP", "GOT", "TST"):
                test_results[-1][token] = data

        for test_result in test_results:
            yield CTestItem(test_result["function_name"], self, test_result)

class CTestItem(pytest.Item):
    """
    Pytest.Item subclass to handle each test result item. There may be
    more than one test result from a test function.

    """

    def __init__(self, name, parent, test_result):
        """Overridden constructor to pass test results dict."""
        super(CTestItem, self).__init__(name, parent)
        self.test_result = test_result

    def runtest(self):
        """The test has already been run. We just evaluate the result."""
        if self.test_result["condition"] == "FAIL":
            raise CTestException(self, self.name)

    def repr_failure(self, exception):
        """
        Called when runtest() raises an exception. The method is used
        to format the output of the failed test result.

        """
        if isinstance(exception.value, CTestException):
            return ("Test failed : {TST} at {file_name}:{line_number}\n"
                    "         got: {GOT}\n"
                    "    expected: {EXP}\n".format(**self.test_result))

    def reportinfo(self):
        """"Called to display header information about the test case."""
        return self.fspath, self.test_result["line_number"] - 1, self.name

class CTestException(Exception):
    """Custom exception to distinguish C unit test failures from others."""
    pass
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import sys
import re
from os import remove, stat
from os.path import join, isfile
from time import time

from module.ConfigParser import IGNORE
from module.network.RequestFactory import getURL
from module.plugins.Hook import threaded, Expose, Hook

class UpdateManager(Hook):
    __name__ = "UpdateManager"
    __version__ = "0.16"
    __description__ = """Checks for updates"""
    __config__ = [("activated", "bool", "Activated", True),
                  ("interval", "int", "Check interval in minutes", 480),
                  ("debug", "bool", "Check for plugin changes when in debug mode", False)]
    __author_name__ = ("RaNaN", "stickell")
    __author_mail__ = ("ranan@pyload.org", "l.stickell@yahoo.it")

    URL = "http://updatemanager.pyload.org"
    MIN_TIME = 3 * 60 * 60  # 3h minimum check interval

    @property
    def debug(self):
        return self.core.debug and self.getConfig("debug")

    def setup(self):
        if self.debug:
            self.logDebug("Monitoring file changes")
            self.interval = 4
            self.last_check = 0  # timestamp of updatecheck
            self.old_periodical = self.periodical
            self.periodical = self.checkChanges
            self.mtimes = {}  # recordes times
        else:
            self.interval = max(self.getConfig("interval") * 60, self.MIN_TIME)

        self.updated = False
        self.reloaded = True
        self.version = "None"

        self.info = {"pyload": False, "plugins": False}

    @threaded
    def periodical(self):
        updates = self.checkForUpdate()
        if updates:
            self.checkPlugins(updates)

        if self.updated and not self.reloaded:
            self.info["plugins"] = True
            self.logInfo(_("*** Plugins have been updated, please restart pyLoad ***"))
        elif self.updated and self.reloaded:
            self.logInfo(_("Plugins updated and reloaded"))
            self.updated = False
        elif self.version == "None":
            self.logInfo(_("No plugin updates available"))

    @Expose
    def recheckForUpdates(self):
        """recheck if updates are available"""
        self.periodical()

    def checkForUpdate(self):
        """checks if an update is available, return result"""
        try:
            if self.version == "None":  # No updated known
                version_check = getURL(self.URL, get={'v': self.core.api.getServerVersion()}).splitlines()
                self.version = version_check[0]

                # Still no updates, plugins will be checked
                if self.version == "None":
                    self.logInfo(_("No Updates for pyLoad"))
                    return version_check[1:]

            self.info["pyload"] = True
            self.logInfo(_("***  New pyLoad Version %s available  ***") % self.version)
            self.logInfo(_("***  Get it here: http://pyload.org/download  ***"))
        except:
            self.logWarning(_("Not able to connect server for updates"))

        return None  # Nothing will be done

    def checkPlugins(self, updates):
        """ checks for plugins updates"""

        # plugins were already updated
        if self.info["plugins"]:
            return

        reloads = []

        vre = re.compile(r'__version__.*=.*("|\')([0-9.]+)')
        url = updates[0]
        schema = updates[1].split("|")
        if 'BLACKLIST' in updates:
            blacklist = updates[updates.index('BLACKLIST') + 1:]
            updates = updates[2:updates.index('BLACKLIST')]
        else:
            blacklist = None
            updates = updates[2:]

        for plugin in updates:
            info = dict(zip(schema, plugin.split("|")))
            filename = info["name"]
            prefix = info["type"]
            version = info["version"]

            if filename.endswith(".pyc"):
                name = filename[:filename.find("_")]
            else:
                name = filename.replace(".py", "")

            #TODO: obsolete in 0.5.0
            if prefix.endswith("s"):
                type = prefix[:-1]
            else:
                type = prefix

            plugins = getattr(self.core.pluginManager, "%sPlugins" % type)

            if name in plugins:
                if float(plugins[name]["v"]) >= float(version):
                    continue

            if name in IGNORE or (type, name) in IGNORE:
                continue

            self.logInfo(_("New version of %(type)s|%(name)s : %(version).2f") % {
                "type": type,
                "name": name,
                "version": float(version)
            })

            try:
                content = getURL(url % info)
            except Exception, e:
                self.logWarning(_("Error when updating %s") % filename, str(e))
                continue

            m = vre.search(content)
            if not m or m.group(2) != version:
                self.logWarning(_("Error when updating %s") % name, _("Version mismatch"))
                continue

            f = open(join("userplugins", prefix, filename), "wb")
            f.write(content)
            f.close()
            self.updated = True

            reloads.append((prefix, name))

        if blacklist:
            self.executeBlacklist(blacklist)

        self.reloaded = self.core.pluginManager.reloadPlugins(reloads)

    def executeBlacklist(self, blacklist):
        for b in blacklist:
            type, name = b.split('|')
            if isfile(join("userplugins", type, name)):
                self.logInfo(_("Removing blacklisted plugin %(type)s|%(name)s") % {
                    "type": type,
                    "name": name
                })
                remove(join("userplugins", type, name))
            if isfile(join("userplugins", type, name.replace('.py', '.pyc'))):
                remove(join("userplugins", type, name.replace('.py', '.pyc')))

    def checkChanges(self):
        if self.last_check + max(self.getConfig("interval") * 60, self.MIN_TIME) < time():
            self.old_periodical()
            self.last_check = time()

        modules = filter(
            lambda m: m and (m.__name__.startswith("module.plugins.") or m.__name__.startswith(
                "userplugins.")) and m.__name__.count(".") >= 2, sys.modules.itervalues())

        reloads = []

        for m in modules:
            root, type, name = m.__name__.rsplit(".", 2)
            id = (type, name)
            if type in self.core.pluginManager.plugins:
                f = m.__file__.replace(".pyc", ".py")
                if not isfile(f):
                    continue

                mtime = stat(f).st_mtime

                if id not in self.mtimes:
                    self.mtimes[id] = mtime
                elif self.mtimes[id] < mtime:
                    reloads.append(id)
                    self.mtimes[id] = mtime

        self.core.pluginManager.reloadPlugins(reloads)
<commit_msg>[UpdateManager] rewritten<commit_after># -*- coding: utf-8 -*-

import sys
import re

from os import remove, stat
from os.path import join, isfile
from time import time

from module.ConfigParser import IGNORE
from module.network.RequestFactory import getURL
from module.plugins.Hook import threaded, Expose, Hook

class UpdateManager(Hook):
    __name__ = "UpdateManager"
    __version__ = "0.22"
    __description__ = """Check for updates"""
    __config__ = [("activated", "bool", "Activated", True),
                  ("mode", "pyLoad + plugins;plugins only", "Check updates for", "pyLoad + plugins"),
                  ("interval", "int", "Check interval in hours", 8),
                  ("reloadplugins", "bool", "Monitor plugins for code changes (debug mode only)", True),
                  ("nodebugupdate", "bool", "Don't check for updates in debug mode", True)]
    __author_name__ = ("RaNaN", "stickell", "Walter Purcaro")
    __author_mail__ = ("ranan@pyload.org", "l.stickell@yahoo.it", "vuolter@gmail.com")

    SERVER_URL = "http://updatemanager.pyload.org"
    MIN_TIME = 3 * 60 * 60  #: 3h minimum check interval

    event_list = ["pluginConfigChanged"]

    def pluginConfigChanged(self, plugin, name, value):
        if name == "interval" and 0 < value != self.interval:
            self.interval = max(value * 60 * 60, self.MIN_TIME)
            self.initPeriodical()
        elif name == "reloadplugins":
            if self.cb2:
                self.core.scheduler.removeJob(self.cb2)
            if value and self.core.debug:
                self.periodical2()

    def coreReady(self):
        self.pluginConfigChanged(self.__name__, "reloadplugins", self.getConfig("reloadplugins"))

    def setup(self):
        self.cb2 = None
        self.interval = self.MIN_TIME
        self.updating = False
        self.info = {"pyload": False, "plugins": False}
        self.mtimes = {}  #: store modification time for each plugin

    def periodical2(self):
        if not self.updating:
            self.autoreloadPlugins()
        self.cb2 = self.core.scheduler.addJob(10, self.periodical2, threaded=True)

    @Expose
    def autoreloadPlugins(self):
        """ reload and reindex all modified plugins """
        modules = filter(
            lambda m: m and (m.__name__.startswith("module.plugins.") or m.__name__.startswith(
                "userplugins.")) and m.__name__.count(".") >= 2, sys.modules.itervalues())

        reloads = []

        for m in modules:
            root, type, name = m.__name__.rsplit(".", 2)
            id = (type, name)
            if type in self.core.pluginManager.plugins:
                f = m.__file__.replace(".pyc", ".py")
                if not isfile(f):
                    continue

                mtime = stat(f).st_mtime

                if id not in self.mtimes:
                    self.mtimes[id] = mtime
                elif self.mtimes[id] < mtime:
                    reloads.append(id)
                    self.mtimes[id] = mtime

        return True if self.core.pluginManager.reloadPlugins(reloads) else False

    @threaded
    def periodical(self):
        if not self.info["pyload"] and not (self.getConfig("nodebugupdate") and self.core.debug):
            self.updating = True
            self.update(onlyplugin=True if self.getConfig("mode") == "plugins only" else False)
            self.updating = False

    def server_response(self):
        try:
            return getURL(self.SERVER_URL, get={'v': self.core.api.getServerVersion()}).splitlines()
        except:
            self.logWarning(_("Not able to connect server to get updates"))

    @Expose
    def updatePlugins(self):
        """ simple wrapper for calling plugin update quickly """
        return self.update(onlyplugin=True)

    @Expose
    def update(self, onlyplugin=False):
        """ check for updates """
        data = self.server_response()
        if not data:
            r = False
        elif data[0] == "None":
            self.logInfo(_("No pyLoad version available"))
            updates = data[1:]
            r = self._updatePlugins(updates)
        elif onlyplugin:
            r = False
        else:
            newversion = data[0]
            # self.info["pyload"] = newversion
            self.logInfo(_("***  New pyLoad Version %s available  ***") % newversion)
            self.logInfo(_("***  Get it here: https://github.com/pyload/pyload/releases  ***"))
            r = self.info["pyload"] = True
        return r

    def _updatePlugins(self, updates):
        """ check for plugin updates """

        if self.info["plugins"]:
            return False  #: plugins were already updated

        updated = []

        vre = re.compile(r'__version__.*=.*("|\')([0-9.]+)')
        url = updates[0]
        schema = updates[1].split("|")
        if 'BLACKLIST' in updates:
            blacklist = updates[updates.index('BLACKLIST') + 1:]
            updates = updates[2:updates.index('BLACKLIST')]
        else:
            blacklist = None
            updates = updates[2:]

        for plugin in updates:
            info = dict(zip(schema, plugin.split("|")))
            filename = info["name"]
            prefix = info["type"]
            version = info["version"]

            if filename.endswith(".pyc"):
                name = filename[:filename.find("_")]
            else:
                name = filename.replace(".py", "")

            #TODO: obsolete in 0.5.0
            if prefix.endswith("s"):
                type = prefix[:-1]
            else:
                type = prefix

            plugins = getattr(self.core.pluginManager, "%sPlugins" % type)

            if name not in plugins or name in IGNORE or (type, name) in IGNORE:
                continue

            oldver = float(plugins[name]["v"])
            newver = float(version)

            if oldver >= newver:
                continue
            else:
                self.logInfo(_("New version of [%(type)s] %(name)s (v%(oldver)s -> v%(newver)s)") % {
                    "type": type,
                    "name": name,
                    "oldver": oldver,
                    "newver": newver
                })

            try:
                content = getURL(url % info)
            except Exception, e:
                self.logError(_("Error when updating plugin %s") % filename, str(e))
                continue

            m = vre.search(content)
            if not m or m.group(2) != version:
                self.logError(_("Error when updating plugin %s") % name, _("Version mismatch"))
                continue

            f = open(join("userplugins", prefix, filename), "wb")
            f.write(content)
            f.close()
            updated.append((prefix, name))

        if blacklist:
            removed = self.removePlugins(map(lambda x: x.split('|'), blacklist))
            for t, n in removed:
                self.logInfo(_("Removed blacklisted plugin: [%(type)s] %(name)s") % {
                    "type": t,
                    "name": n
                })

        if updated:
            reloaded = self.core.pluginManager.reloadPlugins(updated)
            if reloaded:
                self.logInfo(_("Plugins updated and reloaded"))
            else:
                self.logInfo(_("*** Plugins have been updated, pyLoad will be restarted now ***"))
                self.info["plugins"] = True
                self.core.scheduler.addJob(4, self.core.api.restart(), threaded=False)  #: risky, but pyload doesn't let more
            return True
        else:
            self.logInfo(_("No plugin updates available"))
            return False

    @Expose
    def removePlugins(self, type_plugins):
        """ delete plugins under userplugins directory"""
        if not type_plugins:
            return None

        self.logDebug("Request deletion of plugins: %s" % type_plugins)

        removed = []

        for type, name in type_plugins:
            py = join("userplugins", type, name)
            pyc = py[::-1].replace('.py', '.pyc')[::-1]
            if isfile(py):
                id = (type, name)
                remove(py)
                removed.append(id)
            if isfile(pyc):
                remove(pyc)

        return removed  #: return a list of the plugins successfully removed
<|endoftext|>


<commit_before>from __future__ import absolute_import

import json
from decorator import decorator
from redis import ConnectionPool, Redis, WatchError
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.common.exceptions import WebDriverException

from flask import request, Response
from flask.views import MethodView
from werkzeug.contrib.fixers import ProxyFix
from werkzeug.exceptions import NotFound

from .webdriver import ResumableRemote
from .utils import permit, merge, retry

from flask import Flask
app = Flask(__name__)

__all__ = ['app']

app.debug = True

REDIS_KEY_PREFIX = '_shale'
SESSION_SET_KEY = '{}_session_set'.format(REDIS_KEY_PREFIX)
SESSION_KEY_TEMPLATE = REDIS_KEY_PREFIX + '_session_{}'
SESSION_TAGS_KEY_TEMPLATE = REDIS_KEY_PREFIX + '_session_{}_tags'

pool = ConnectionPool(host='localhost', port=6379, db=0)

def returns_json(func):
    def inner(*args, **kwargs):
        status=200
        data = {}
        try:
            data = json.dumps(func(*args, **kwargs))
        except NotFound:
            status = 404
        return Response(data, content_type="application/json", status=status)
    return inner

class RedisView(object):
    def dispatch_request(self, *args, **kwargs):
        self.redis = Redis(connection_pool=pool)
        return super(RedisView, self).dispatch_request(*args, **kwargs)

class SessionAPI(RedisView, MethodView):

    decorators = [returns_json]

    def get(self, session_id):
        if session_id is None:
            return self.view_models()
        # get the specified session or 404
        view_model = self.view_model(session_id)
        if view_model is None:
            raise NotFound()
        return view_model

    def post(self):
        data = json.loads(request.data) if request.data else {}

        defaults = {
            'browser_name':'firefox',
            'hub':'localhost:4444',
            'tags':[],
            'reserved':False,
        }
        permitted = permit(data,
                ['browser_name', 'hub', 'tags', 'reserved', 'current_url'])
        cleaned = merge(permitted, defaults)

        cap = {
            'chrome':DesiredCapabilities.CHROME,
            'firefox':DesiredCapabilities.FIREFOX,
            'phantomjs':DesiredCapabilities.PHANTOMJS,
        }.get(cleaned['browser_name'])

        wd = ResumableRemote(command_executor='http://{}/wd/hub'.format(cleaned['hub']),
                             desired_capabilities=cap)
        if 'current_url' in cleaned:
            # we do this in JS to prevent a blocking call
            wd.execute_script('window.location = "{}";'.format(cleaned['current_url']))
        with self.redis.pipeline() as pipe:
            pipe.sadd(SESSION_SET_KEY, wd.session_id)
            session_key = SESSION_KEY_TEMPLATE.format(wd.session_id)
            permitted = permit(cleaned, ['browser_name', 'hub', 'reserved', 'current_url'])
            for key, value in permitted.iteritems():
                pipe.hset(session_key, key, value)
            tags_key = SESSION_TAGS_KEY_TEMPLATE.format(wd.session_id)
            pipe.delete(tags_key)
            for tag in cleaned['tags']:
                pipe.sadd(tags_key, tag)
            pipe.execute()
        return self.view_model(wd.session_id, data=cleaned)

    def put(self, session_id):
        data = json.loads(request.data) if request.data else {}
        cleaned = permit(data, ['tags', 'reserved'])

        with self.redis.pipeline() as pipe:
            tags_key = SESSION_TAGS_KEY_TEMPLATE.format(session_id)
            if 'tags' in cleaned:
                pipe.delete(tags_key)
                for tag in cleaned['tags']:
                    pipe.sadd(tags_key, tag)
                if 'reserved' in cleaned:
                    pipe.hset(SESSION_KEY_TEMPLATE.format(session_id),
                              'reserved', cleaned['reserved'])
            pipe.execute()
            return self.view_model(session_id)

    def delete(self, session_id):
        wd = ResumableRemote(session_id=session_id)
        try:
            wd.quit()
        except WebDriverException:
            pass
        with self.redis.pipeline() as pipe:
            pipe.srem(SESSION_SET_KEY, session_id)
            pipe.delete(SESSION_KEY_TEMPLATE.format(session_id))
            pipe.delete(SESSION_TAGS_KEY_TEMPLATE.format(session_id))
            pipe.execute()
        return True

    def view_model(self, session_id, data=None, should_retry=True):
        def get_data():
            ret = None
            with self.redis.pipeline() as pipe:
                session_key = SESSION_KEY_TEMPLATE.format(session_id)
                tags_key = SESSION_TAGS_KEY_TEMPLATE.format(session_id)
                pipe.watch(session_key)
                pipe.watch(tags_key)
                if pipe.exists(session_key):
                    ret = pipe.hgetall(session_key)
                    ret['tags'] = list(pipe.smembers(tags_key))
                pipe.execute()
            return ret
        if data is None:
            if should_retry:
                data = retry(get_data, exception_type=WatchError, raises=False)
            else:
                data = get_data()
        if data and 'reserved' in data:
            data['reserved'] = {'True':True, 'False':False}[data['reserved']]
        return merge({'id':session_id}, data) if data else None

    def view_models(self, session_ids=None):
        def get_data():
            data = []
            with self.redis.pipeline() as pipe:
                pipe.watch(SESSION_SET_KEY)
                ids = session_ids
                ids = ids or list(pipe.smembers(SESSION_SET_KEY))
                for session_id in ids:
                    data.append(self.view_model(session_id))
                pipe.execute()
            return data
        return retry(get_data, exception_type=WatchError, raises=False) or []

session_view = SessionAPI.as_view('session_api')
app.add_url_rule('/sessions/', defaults={'session_id': None},
                 view_func=session_view, methods=['GET',])
app.add_url_rule('/sessions/', view_func=session_view, methods=['POST',])
app.add_url_rule('/sessions/<session_id>', view_func=session_view,
                 methods=['GET', 'PUT', 'DELETE'])

app.wsgi_app = ProxyFix(app.wsgi_app)
<commit_msg>line length<commit_after>from __future__ import absolute_import

import json
from decorator import decorator
from redis import ConnectionPool, Redis, WatchError
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.common.exceptions import WebDriverException

from flask import request, Response
from flask.views import MethodView
from werkzeug.contrib.fixers import ProxyFix
from werkzeug.exceptions import NotFound

from .webdriver import ResumableRemote
from .utils import permit, merge, retry

from flask import Flask
app = Flask(__name__)

__all__ = ['app']

app.debug = True

REDIS_KEY_PREFIX = '_shale'
SESSION_SET_KEY = '{}_session_set'.format(REDIS_KEY_PREFIX)
SESSION_KEY_TEMPLATE = REDIS_KEY_PREFIX + '_session_{}'
SESSION_TAGS_KEY_TEMPLATE = REDIS_KEY_PREFIX + '_session_{}_tags'

pool = ConnectionPool(host='localhost', port=6379, db=0)

def returns_json(func):
    def inner(*args, **kwargs):
        status=200
        data = {}
        try:
            data = json.dumps(func(*args, **kwargs))
        except NotFound:
            status = 404
        return Response(data, content_type="application/json", status=status)
    return inner

class RedisView(object):
    def dispatch_request(self, *args, **kwargs):
        self.redis = Redis(connection_pool=pool)
        return super(RedisView, self).dispatch_request(*args, **kwargs)

class SessionAPI(RedisView, MethodView):

    decorators = [returns_json]

    def get(self, session_id):
        if session_id is None:
            return self.view_models()
        # get the specified session or 404
        view_model = self.view_model(session_id)
        if view_model is None:
            raise NotFound()
        return view_model

    def post(self):
        data = json.loads(request.data) if request.data else {}

        defaults = {
            'browser_name':'firefox',
            'hub':'localhost:4444',
            'tags':[],
            'reserved':False,
        }
        permitted = permit(data,
                ['browser_name', 'hub', 'tags', 'reserved', 'current_url'])
        cleaned = merge(permitted, defaults)

        cap = {
            'chrome':DesiredCapabilities.CHROME,
            'firefox':DesiredCapabilities.FIREFOX,
            'phantomjs':DesiredCapabilities.PHANTOMJS,
        }.get(cleaned['browser_name'])

        wd = ResumableRemote(
            command_executor='http://{}/wd/hub'.format(cleaned['hub']),
            desired_capabilities=cap)
        if 'current_url' in cleaned:
            # we do this in JS to prevent a blocking call
            wd.execute_script(
                'window.location = "{}";'.format(cleaned['current_url']))
        with self.redis.pipeline() as pipe:
            pipe.sadd(SESSION_SET_KEY, wd.session_id)
            session_key = SESSION_KEY_TEMPLATE.format(wd.session_id)
            for key, value in permitted.iteritems():
            permitted = permit(
                cleaned, ['browser_name', 'hub', 'reserved', 'current_url'])
                pipe.hset(session_key, key, value)
            tags_key = SESSION_TAGS_KEY_TEMPLATE.format(wd.session_id)
            pipe.delete(tags_key)
            for tag in cleaned['tags']:
                pipe.sadd(tags_key, tag)
            pipe.execute()
        return self.view_model(wd.session_id, data=cleaned)

    def put(self, session_id):
        data = json.loads(request.data) if request.data else {}
        cleaned = permit(data, ['tags', 'reserved'])

        with self.redis.pipeline() as pipe:
            tags_key = SESSION_TAGS_KEY_TEMPLATE.format(session_id)
            if 'tags' in cleaned:
                pipe.delete(tags_key)
                for tag in cleaned['tags']:
                    pipe.sadd(tags_key, tag)
                if 'reserved' in cleaned:
                    pipe.hset(SESSION_KEY_TEMPLATE.format(session_id),
                              'reserved', cleaned['reserved'])
            pipe.execute()
            return self.view_model(session_id)

    def delete(self, session_id):
        wd = ResumableRemote(session_id=session_id)
        try:
            wd.quit()
        except WebDriverException:
            pass
        with self.redis.pipeline() as pipe:
            pipe.srem(SESSION_SET_KEY, session_id)
            pipe.delete(SESSION_KEY_TEMPLATE.format(session_id))
            pipe.delete(SESSION_TAGS_KEY_TEMPLATE.format(session_id))
            pipe.execute()
        return True

    def view_model(self, session_id, data=None, should_retry=True):
        def get_data():
            ret = None
            with self.redis.pipeline() as pipe:
                session_key = SESSION_KEY_TEMPLATE.format(session_id)
                tags_key = SESSION_TAGS_KEY_TEMPLATE.format(session_id)
                pipe.watch(session_key)
                pipe.watch(tags_key)
                if pipe.exists(session_key):
                    ret = pipe.hgetall(session_key)
                    ret['tags'] = list(pipe.smembers(tags_key))
                pipe.execute()
            return ret
        if data is None:
            if should_retry:
                data = retry(get_data, exception_type=WatchError, raises=False)
            else:
                data = get_data()
        if data and 'reserved' in data:
            data['reserved'] = {'True':True, 'False':False}[data['reserved']]
        return merge({'id':session_id}, data) if data else None

    def view_models(self, session_ids=None):
        def get_data():
            data = []
            with self.redis.pipeline() as pipe:
                pipe.watch(SESSION_SET_KEY)
                ids = session_ids
                ids = ids or list(pipe.smembers(SESSION_SET_KEY))
                for session_id in ids:
                    data.append(self.view_model(session_id))
                pipe.execute()
            return data
        return retry(get_data, exception_type=WatchError, raises=False) or []

session_view = SessionAPI.as_view('session_api')
app.add_url_rule('/sessions/', defaults={'session_id': None},
                 view_func=session_view, methods=['GET',])
app.add_url_rule('/sessions/', view_func=session_view, methods=['POST',])
app.add_url_rule('/sessions/<session_id>', view_func=session_view,
                 methods=['GET', 'PUT', 'DELETE'])

app.wsgi_app = ProxyFix(app.wsgi_app)
<|endoftext|>


<commit_before>import logging
log = logging.getLogger(__name__)

from django.contrib import admin
from django.utils.safestring import mark_safe

from ajax_select.admin import (
    AjaxSelectAdminTabularInline,
    AjaxSelectAdmin,
)

from ajax_select import make_ajax_form

from .models import (
    Singer,
    Chorus,
    Quartet,
    District,
    Chapter,
    Contest,
    QuartetPerformance,
    ChorusPerformance,
)

class ChorusPerformanceInline(AjaxSelectAdminTabularInline):
    model = ChorusPerformance
    form = make_ajax_form(
        ChorusPerformance,
        {'chorus': 'chorus'},
    )
    fields = (
        'chorus',
        'queue',
        'stagetime',
        # 'song1',
        # 'mus1',
        # 'prs1',
        # 'sng1',
        # 'song2',
        # 'mus2',
        # 'prs2',
        # 'sng2',
    )

class QuartetPerformanceInline(AjaxSelectAdminTabularInline):
    model = QuartetPerformance
    form = make_ajax_form(
        QuartetPerformance,
        {'quartet': 'quartet'},
    )
    fields = (
        'quartet',
        'round',
        'queue',
        'stagetime',
        # 'song1',
        # 'mus1',
        # 'prs1',
        # 'sng1',
        # 'song2',
        # 'mus2',
        # 'prs2',
        # 'sng2',
    )

class CommonAdmin(admin.ModelAdmin):
    def show_website(self, obj):
        url = '{1}'.format(obj.website, obj.website)
        return mark_safe(url)
    show_website.allow_tags = True

    def show_email(self, obj):
        url = '{1}'.format(obj.email, obj.email)
        return mark_safe(url)
    show_email.allow_tags = True

    def show_facebook(self, obj):
        url = '{1}'.format(obj.facebook, obj.facebook)
        return mark_safe(url)
    show_facebook.allow_tags = True

    list_display = [
        'name',
        'location',
        'show_website',
        'show_email',
        'show_facebook',
        'phone',
        'twitter',
        'picture',
    ]
    search_fields = ['name']
    save_on_top = True

@admin.register(Singer)
class SingerAdmin(CommonAdmin):
    pass

@admin.register(District)
class DistrictAdmin(CommonAdmin):
    pass

@admin.register(Chapter)
class ChatperAdmin(CommonAdmin):
    pass

@admin.register(Contest)
class ContestAdmin(admin.ModelAdmin):

    list_filter = [
        'year',
        'kind',
        'level',
    ]

    inlines = [
        ChorusPerformanceInline,
        QuartetPerformanceInline,
    ]

    def get_formsets_with_inlines(self, request, obj=None):
        for inline in self.get_inline_instances(request, obj):
            if obj.kind == Contest.CHORUS:
                if isinstance(inline, ChorusPerformanceInline):
                    yield inline.get_formset(request, obj), inline
                    break
                else:
                    continue
            else:
                if isinstance(inline, ChorusPerformanceInline):
                    continue
                else:
                    yield inline.get_formset(request, obj), inline

@admin.register(Quartet)
class QuartetAdmin(AjaxSelectAdmin, CommonAdmin):
    form = make_ajax_form(
        Quartet,
        {
            'lead': 'singer',
            'tenor': 'singer',
            'baritone': 'singer',
            'bass': 'singer'
        },
    )

@admin.register(Chorus)
class ChorusAdmin(CommonAdmin):
    pass

# @admin.register(QuartetPerformance)
# class QuartetPerformanceAdmin(admin.ModelAdmin):
#     save_on_top = True

# @admin.register(ChorusPerformance)
# class ChorusPerformanceAdmin(admin.ModelAdmin):
#     save_on_top = True
<commit_msg>Specify International until district support required<commit_after>import logging
log = logging.getLogger(__name__)

from django.contrib import admin
from django.utils.safestring import mark_safe

from ajax_select.admin import (
    AjaxSelectAdminTabularInline,
    AjaxSelectAdmin,
)

from ajax_select import make_ajax_form

from .models import (
    Singer,
    Chorus,
    Quartet,
    District,
    Chapter,
    Contest,
    QuartetPerformance,
    ChorusPerformance,
)

class ChorusPerformanceInline(AjaxSelectAdminTabularInline):
    model = ChorusPerformance
    form = make_ajax_form(
        ChorusPerformance,
        {'chorus': 'chorus'},
    )
    fields = (
        'chorus',
        'queue',
        'stagetime',
        # 'song1',
        # 'mus1',
        # 'prs1',
        # 'sng1',
        # 'song2',
        # 'mus2',
        # 'prs2',
        # 'sng2',
    )

class QuartetPerformanceInline(AjaxSelectAdminTabularInline):
    model = QuartetPerformance
    form = make_ajax_form(
        QuartetPerformance,
        {'quartet': 'quartet'},
    )
    fields = (
        'quartet',
        'round',
        'queue',
        'stagetime',
        # 'song1',
        # 'mus1',
        # 'prs1',
        # 'sng1',
        # 'song2',
        # 'mus2',
        # 'prs2',
        # 'sng2',
    )

class CommonAdmin(admin.ModelAdmin):
    def show_website(self, obj):
        url = '{1}'.format(obj.website, obj.website)
        return mark_safe(url)
    show_website.allow_tags = True

    def show_email(self, obj):
        url = '{1}'.format(obj.email, obj.email)
        return mark_safe(url)
    show_email.allow_tags = True

    def show_facebook(self, obj):
        url = '{1}'.format(obj.facebook, obj.facebook)
        return mark_safe(url)
    show_facebook.allow_tags = True

    list_display = [
        'name',
        'location',
        'show_website',
        'show_email',
        'show_facebook',
        'phone',
        'twitter',
        'picture',
    ]
    search_fields = ['name']
    save_on_top = True

@admin.register(Singer)
class SingerAdmin(CommonAdmin):
    pass

@admin.register(District)
class DistrictAdmin(CommonAdmin):
    pass

@admin.register(Chapter)
class ChatperAdmin(CommonAdmin):
    pass

@admin.register(Contest)
class ContestAdmin(admin.ModelAdmin):

    fields = (
        'year',
        'kind',
        'dates',
        'location',
        'timezone',
    )

    list_filter = [
        'year',
        'kind',
        'level',
    ]

    inlines = [
        ChorusPerformanceInline,
        QuartetPerformanceInline,
    ]

    def get_formsets_with_inlines(self, request, obj=None):
        for inline in self.get_inline_instances(request, obj):
            if obj.kind == Contest.CHORUS:
                if isinstance(inline, ChorusPerformanceInline):
                    yield inline.get_formset(request, obj), inline
                    break
                else:
                    continue
            else:
                if isinstance(inline, ChorusPerformanceInline):
                    continue
                else:
                    yield inline.get_formset(request, obj), inline

@admin.register(Quartet)
class QuartetAdmin(AjaxSelectAdmin, CommonAdmin):
    form = make_ajax_form(
        Quartet,
        {
            'lead': 'singer',
            'tenor': 'singer',
            'baritone': 'singer',
            'bass': 'singer'
        },
    )

@admin.register(Chorus)
class ChorusAdmin(CommonAdmin):
    pass

# @admin.register(QuartetPerformance)
# class QuartetPerformanceAdmin(admin.ModelAdmin):
#     save_on_top = True

# @admin.register(ChorusPerformance)
# class ChorusPerformanceAdmin(admin.ModelAdmin):
#     save_on_top = True
<|endoftext|>


<commit_before>import pytest

def pytest_ignore_collect(path, config):
    ignore_list = [
        'integration/fileclient_test.py',
        'integration/cli/batch.py',
        'integration/cli/custom_module.py',
        'integration/cli/grains.py',
        'integration/cloud/providers/msazure.py',
        'integration/files/file/base/*',
        'integration/modules/git.py',
        'integration/client/*',
        'integration/wheel/*'
    ]
    return any(map(path.fnmatch, ignore_list))

@pytest.fixture(scope="session")
def test_daemon(request):
    from integration import TestDaemon
    return TestDaemon(request.instance)

@pytest.fixture(scope="session")
def transplant_configs(test_daemon):
    test_daemon.transplant_configs(transport='zeromq')

@pytest.fixture(scope="session")
def add_options(request):
    from tests.runtests import SaltTestsuiteParser
    parser = SaltTestsuiteParser([])
    request.instance.options, args = parser.parse_args([])

@pytest.fixture(scope="session")
def salt_test_deamon(add_options, transplant_configs, test_daemon, request):
    request.addfinalizer(test_daemon.__exit__)
    test_daemon.__enter__()
<commit_msg>Fix daemon finalizer<commit_after>import pytest

def pytest_ignore_collect(path, config):
    ignore_list = [
        # ImportError: No module named integration
        'integration/fileclient_test.py',
        'integration/cli/batch.py',
        'integration/cli/custom_module.py',
        'integration/cli/grains.py',
        'integration/utils/test_reactor.py',
        'integration/renderers/pydsl_test.py',
        # NameError: global name 'azure' is not defined
        'integration/cloud/providers/msazure.py',
        'integration/files/file/base/*',
        # OSError: [Errno 2] No such file or directory
        'integration/modules/git.py',
    ]
    return any(map(path.fnmatch, ignore_list))

@pytest.fixture(scope="session")
def test_daemon(request):
    from integration import TestDaemon
    return TestDaemon(request.instance)

@pytest.fixture(scope="session")
def transplant_configs(test_daemon):
    test_daemon.transplant_configs(transport='zeromq')

@pytest.fixture(scope="session")
def add_options(request):
    from tests.runtests import SaltTestsuiteParser
    parser = SaltTestsuiteParser([])
    request.instance.options, args = parser.parse_args([])

@pytest.fixture(scope="session")
def salt_test_deamon(add_options, transplant_configs, test_daemon, request):
    from functools import partial
    finalizer = partial(test_daemon.__exit__, None, None, None)
    request.addfinalizer(finalizer)
    test_daemon.__enter__()
<|endoftext|>


<commit_before># import logging
# log = logging.getLogger(__name__)

from django.contrib import admin

from django_object_actions import (
    DjangoObjectActions,
    takes_instance_or_queryset,
)

from easy_select2 import select2_modelform

from .models import (
    Convention,
    Contest,
    Contestant,
    Group,
    Performance,
    Singer,
    Note,
    District,
)

class PerformanceInline(admin.TabularInline):
    form = select2_modelform(
        Performance,
        attrs={'width': '250px'},
    )
    model = Performance
    extra = 0
    show_change_link = True

class ContestantInline(admin.TabularInline):
    form = select2_modelform(
        Contestant,
        attrs={'width': '250px'},
    )
    model = Contestant
    extra = 0
    show_change_link = True
    fields = (
        'group',
        'prelim',
        'seed',
        'score',
        'place',
    )

@admin.register(Convention)
class ConventionAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Convention,
        attrs={'width': '250px'},
    )

    list_display = (
        'name',
        'location',
        'dates',
        'kind',
        'year',
        'district',
    )

    fields = (
        'name',
        'location',
        'dates',
        'timezone',
        'district',
        'kind',
        'year',
    )

    list_filter = (
        'kind',
        'year',
        'district',
    )

    readonly_fields = (
        'name',
    )

    save_on_top = True

@admin.register(Contest)
class ContestAdmin(DjangoObjectActions, admin.ModelAdmin):
    # @takes_instance_or_queryset
    # def import_scores(self, request, queryset):
    #     for obj in queryset:
    #         obj.import_scores()
    # import_scores.label = 'Import Scores'
    form = select2_modelform(
        Contest,
        attrs={'width': '250px'},
    )
    save_on_top = True
    # objectactions = [
    #     'import_scores',
    # ]

    inlines = [
        ContestantInline,
    ]

    list_filter = (
        'level',
        'kind',
        'year',
        'district',
    )

    list_display = (
        '__unicode__',
        'convention',
        'level',
        'kind',
        'year',
        'district',
        'panel',
        'scoresheet_pdf',
        'scoresheet_csv',
    )

    fields = (
        'convention',
        'level',
        'kind',
        'year',
        'district',
        'district_fk',
        'panel',
        'scoresheet_pdf',
        'scoresheet_csv',
    )

@admin.register(Performance)
class PerformanceAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Performance,
        attrs={'width': '250px'},
    )
    list_display = (
        '__unicode__',
        'queue',
        'session',
        'stagetime',
        'place',
        'total_raw',
        'song1',
        'mus1',
        'prs1',
        'sng1',
        'song2',
        'mus2',
        'prs2',
        'sng2',
        'men',
    )

    list_filter = (
        'round',
        'contestant__contest',
        'stagetime',
    )

    ordering = (
        'place',
        'queue',
        'slug',
    )

    save_on_top = True

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Group,
        attrs={'width': '250px'},
    )

    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'lead',
        'tenor',
        'baritone',
        'bass',
        'director',
        'chapter_name',
        'chapter_code',
        'bsmdb_id',
    )

    save_on_top = True

@admin.register(Contestant)
class ContestantAdmin(admin.ModelAdmin):
    @takes_instance_or_queryset
    def update_contestants(self, request, queryset):
        for obj in queryset:
            obj.save()
    update_contestants.label = 'Update Contestants'

    objectactions = [
        'update_contestants',
    ]

    list_display = (
        '__unicode__',
        'seed',
        'prelim',
        'place',
        'score',
    )

    search_fields = (
        'group__name',
    )

    list_filter = (
        'contest__level',
        'contest__kind',
        'contest__year',
    )

    inlines = [
        PerformanceInline,
    ]

    fields = (
        'contest',
        'group',
        'seed',
        'prelim',
        'place',
        'score',
    )

    save_on_top = True

@admin.register(Singer)
class SingerAdmin(admin.ModelAdmin):
    save_on_top = True
    fields = (
        'name',
    )

@admin.register(Note)
class NoteAdmin(admin.ModelAdmin):
    save_on_top = True
    fields = (
        'user',
        'performance',
        'text',
    )

@admin.register(District)
class DistrictAdmin(admin.ModelAdmin):
    save_on_top = True
<commit_msg>District in Admin Lists<commit_after># import logging
# log = logging.getLogger(__name__)

from django.contrib import admin

from django_object_actions import (
    DjangoObjectActions,
    takes_instance_or_queryset,
)

from easy_select2 import select2_modelform

from .models import (
    Convention,
    Contest,
    Contestant,
    Group,
    Performance,
    Singer,
    Note,
    District,
)

class PerformanceInline(admin.TabularInline):
    form = select2_modelform(
        Performance,
        attrs={'width': '250px'},
    )
    model = Performance
    extra = 0
    show_change_link = True

class ContestantInline(admin.TabularInline):
    form = select2_modelform(
        Contestant,
        attrs={'width': '250px'},
    )
    model = Contestant
    extra = 0
    show_change_link = True
    fields = (
        'group',
        'prelim',
        'seed',
        'score',
        'place',
    )

@admin.register(Convention)
class ConventionAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Convention,
        attrs={'width': '250px'},
    )

    list_display = (
        'name',
        'location',
        'dates',
        'kind',
        'year',
        'district',
    )

    fields = (
        'name',
        'location',
        'dates',
        'timezone',
        'district',
        'kind',
        'year',
    )

    list_filter = (
        'kind',
        'year',
        'district',
    )

    readonly_fields = (
        'name',
    )

    save_on_top = True

@admin.register(Contest)
class ContestAdmin(DjangoObjectActions, admin.ModelAdmin):
    # @takes_instance_or_queryset
    # def import_scores(self, request, queryset):
    #     for obj in queryset:
    #         obj.import_scores()
    # import_scores.label = 'Import Scores'
    form = select2_modelform(
        Contest,
        attrs={'width': '250px'},
    )
    save_on_top = True
    # objectactions = [
    #     'import_scores',
    # ]

    inlines = [
        ContestantInline,
    ]

    list_filter = (
        'level',
        'kind',
        'year',
        'district',
    )

    list_display = (
        '__unicode__',
        'district',
        'district_fk',
        'convention',
        'level',
        'kind',
        'year',
        'panel',
        'scoresheet_pdf',
        'scoresheet_csv',
    )

    fields = (
        'convention',
        'level',
        'kind',
        'year',
        'district',
        'district_fk',
        'panel',
        'scoresheet_pdf',
        'scoresheet_csv',
    )

@admin.register(Performance)
class PerformanceAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Performance,
        attrs={'width': '250px'},
    )
    list_display = (
        '__unicode__',
        'queue',
        'session',
        'stagetime',
        'place',
        'total_raw',
        'song1',
        'mus1',
        'prs1',
        'sng1',
        'song2',
        'mus2',
        'prs2',
        'sng2',
        'men',
    )

    list_filter = (
        'round',
        'contestant__contest',
        'stagetime',
    )

    ordering = (
        'place',
        'queue',
        'slug',
    )

    save_on_top = True

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    form = select2_modelform(
        Group,
        attrs={'width': '250px'},
    )

    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'district',
        'district_fk',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'lead',
        'tenor',
        'baritone',
        'bass',
        'director',
        'chapter_name',
        'chapter_code',
        'bsmdb_id',
    )

    save_on_top = True

@admin.register(Contestant)
class ContestantAdmin(admin.ModelAdmin):
    @takes_instance_or_queryset
    def update_contestants(self, request, queryset):
        for obj in queryset:
            obj.save()
    update_contestants.label = 'Update Contestants'

    objectactions = [
        'update_contestants',
    ]

    list_display = (
        '__unicode__',
        'seed',
        'prelim',
        'place',
        'score',
    )

    search_fields = (
        'group__name',
    )

    list_filter = (
        'contest__level',
        'contest__kind',
        'contest__year',
    )

    inlines = [
        PerformanceInline,
    ]

    fields = (
        'contest',
        'group',
        'seed',
        'prelim',
        'place',
        'score',
    )

    save_on_top = True

@admin.register(Singer)
class SingerAdmin(admin.ModelAdmin):
    save_on_top = True
    fields = (
        'name',
    )

@admin.register(Note)
class NoteAdmin(admin.ModelAdmin):
    save_on_top = True
    fields = (
        'user',
        'performance',
        'text',
    )

@admin.register(District)
class DistrictAdmin(admin.ModelAdmin):
    save_on_top = True
<|endoftext|>


<commit_before>#!/usr/bin/env python

import os
import sys
import argparse
import tempfile
import subprocess

from modules.exception import *
from modules.engine import get_plugins, dispatch
from configobj import ConfigObj, ConfigObjError
from validate import Validator

class ThrowingArgumentParser(argparse.ArgumentParser):
    """ Overrides argparse error function and
    Handles conditions like invalid arguments"""
    def error(self, message):
        self.print_help()
        sys.exit()

def parse_args(args):
    """ Parse arguments to the app """
    parser = ThrowingArgumentParser()
    plugins = get_plugins()
    for plugin in plugins:
        parser.add_argument(
            '-{}'.format(plugin[0]), '--{}'.format(plugin),
            action = 'store_true',
            help = 'Post message via {}'.format(plugin).title())

    if len(args) == 0:
        parser.print_help()
        sys.exit()

    return parser.parse_args(args)

def add_field(field, fieldType, cfgFile, cfgSpec):
    """ Adds a field into config file """
    config = ConfigObj(cfgFile, write_empty_values = True)
    config[field] = ''
    config.write()

    spec = ConfigObj(cfgSpec)
    spec[field] = fieldType
    spec.write()

def validate_configfile(cfgFile, cfgSpec):
    """ Validates the config file content types """
    try:
        config = ConfigObj(cfgFile, configspec = cfgSpec)
    except ConfigObjError:
        return False
    validator = Validator()
    result = config.validate(validator)
    os.unlink(cfgSpec)
    return result

def main(args):
    field_list = []
    channels = []
    fields = {}
    plugins = get_plugins()
    args = parse_args(args)._get_kwargs()

    (fn, cfgFile) = tempfile.mkstemp() # File to hold the message details
    (fn, cfgSpec) = tempfile.mkstemp() # File to hold the message specs

    for arg in args:
        if arg[1] and arg[0] in plugins:
            field_list.extend(plugins[arg[0]].__fields__)
            channels.append(arg[0])

    if len(channels) != 0:
        field_list = list(set(field_list))
        if 'Message' in field_list:
            field_list.remove('Message')
            field_list.append('Message')
        for field in field_list:
            add_field(field, 'string', cfgFile, cfgSpec)

        subprocess.call('%s %s' % (os.getenv('EDITOR'), cfgFile), shell = True)
        if validate_configfile(cfgFile, cfgSpec):
            config = ConfigObj(cfgFile)
            for field in field_list:
                fields[field] = config[field]
            response = {}
            for channel in plugins:
                if channel in channels:
                    plugin = plugins[channel]
                    response.update(dispatch(plugin, fields))
            print response
        else:
            print 'Input file validation failed'
            os.unlink(cfgFile)
            sys.exit(1)

if __name__ == '__main__':
    import sys
    main(sys.argv[1:])

<commit_msg>Prints formated output & method created for returning channels anf fields list<commit_after>#!/usr/bin/env python

import os
import sys
import argparse
import tempfile
import subprocess

from modules.exception import *
from modules.engine import get_plugins, dispatch
from configobj import ConfigObj, ConfigObjError
from validate import Validator

class ThrowingArgumentParser(argparse.ArgumentParser):
    """ Overrides argparse error function and
    Handles conditions like invalid arguments"""
    def error(self, message):
        self.print_help()
        sys.exit()

def parse_args(args):
    """ Parse arguments to the app """
    parser = ThrowingArgumentParser()
    plugins = get_plugins()
    for plugin in plugins:
        parser.add_argument(
            '-{}'.format(plugin[0]), '--{}'.format(plugin),
            action = 'store_true',
            help = 'Post message via {}'.format(plugin).title())

    if len(args) == 0:
        parser.print_help()
        sys.exit()

    return parser.parse_args(args)

def add_field(field, fieldType, cfgFile, cfgSpec):
    """ Adds a field into config file """
    config = ConfigObj(cfgFile, write_empty_values = True)
    config[field] = ''
    config.write()

    spec = ConfigObj(cfgSpec)
    spec[field] = fieldType
    spec.write()

def validate_configfile(cfgFile, cfgSpec):
    """ Validates the config file content types """
    try:
        config = ConfigObj(cfgFile, configspec = cfgSpec)
    except ConfigObjError:
        return False
    validator = Validator()
    result = config.validate(validator)
    os.unlink(cfgSpec)
    return result

def get_fields_channels(plugins, args):
    channels = []
    field_list = []
    for arg in args:
        if arg[1] and arg[0] in plugins:
            field_list.extend(plugins[arg[0]].__fields__)
            channels.append(arg[0])
    field_list = list(set(field_list))
    if 'Message' in field_list:
        field_list.remove('Message')
        field_list.append('Message')

    return field_list, channels

def main(args):
    fields = {}
    plugins = get_plugins()
    args = parse_args(args)._get_kwargs()

    field_list, channels = get_fields_channels(plugins, args)

    if len(channels) == 0:
        return

    (fn, cfgFile) = tempfile.mkstemp() # File to hold the message details
    (fn, cfgSpec) = tempfile.mkstemp() # File to hold the message specs

    for field in field_list:
        add_field(field, 'string', cfgFile, cfgSpec)

    subprocess.call('%s %s' % (os.getenv('EDITOR'), cfgFile), shell = True)
    if validate_configfile(cfgFile, cfgSpec):
        config = ConfigObj(cfgFile)
        for field in field_list:
            fields[field] = config[field]
        responses = {}
        for channel in plugins:
            if channel in channels:
                plugin = plugins[channel]
                responses.update(dispatch(plugin, fields))
        for channel, response in responses.iteritems():
            print "{:<25} {:<25}".format(channel, response)
    else:
        print 'Input file validation failed'
        os.unlink(cfgFile)
        sys.exit(1)

if __name__ == '__main__':
    import sys
    main(sys.argv[1:])

<|endoftext|>