<commit_before># Copyright (c) 2010-2012, GEM Foundation.
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.

"""This module contains functions and Django model forms for carrying out job
profile validation.
"""

import re

from django.forms import ModelForm
from nhlib import imt

from openquake.db import models

#: Minimum value for a signed 32-bit int
MIN_SINT_32 = -(2 ** 31)
#: Maximum value for a signed 32-bit int
MAX_SINT_32 = (2 ** 31) - 1

# Silencing 'Missing docstring' and 'Invalid name' for all of the validation
# functions (the latter because some of the function names are very long).
# pylint: disable=C0111,C0103

def description_is_valid(_mdl):
    return True, []

def calculation_mode_is_valid(mdl):
    if not mdl.calculation_mode == 'classical':
        return False, ['Calculation mode must be "classical"']
    return True, []

def region_is_valid(mdl):
    valid = True
    errors = []

    if not mdl.region.valid:
        valid = False
        errors.append('Invalid region geomerty: %s' % mdl.region.valid_reason)

    if len(mdl.region.coords) > 1:
        valid = False
        errors.append('Region geometry can only be a single linear ring')

    # There should only be a single linear ring.
    # Even if there are multiple, we can still check for and report errors.
    for ring in mdl.region.coords:
        lons = [lon for lon, _ in ring]
        lats = [lat for _, lat in ring]
        if not all([-180 <= x <= 180 for x in lons]):
            valid = False
            errors.append('Longitude values must in the range [-180, 180]')
        if not all([-90 <= x <= 90 for x in lats]):
            valid = False
            errors.append('Latitude values must be in the range [-90, 90]')
    return valid, errors

def region_grid_spacing_is_valid(mdl):
    if not mdl.region_grid_spacing > 0:
        return False, ['Region grid spacing must be > 0']
    return True, []

def sites_is_valid(mdl):
    valid = True
    errors = []

    lons = [pt.x for pt in mdl.sites]
    lats = [pt.y for pt in mdl.sites]
    if not all([-180 <= x <= 180 for x in lons]):
        valid = False
        errors.append('Longitude values must in the range [-180, 180]')
    if not all([-90 <= x <= 90 for x in lats]):
        valid = False
        errors.append('Latitude values must be in the range [-90, 90]')

    return valid, errors

def random_seed_is_valid(mdl):
    if not MIN_SINT_32 <= mdl.random_seed <= MAX_SINT_32:
        return False, [('Random seed must be a value from %s to %s (inclusive)'
                       % (MIN_SINT_32, MAX_SINT_32))]
    return True, []

def number_of_logic_tree_samples_is_valid(mdl):
    if not mdl.number_of_logic_tree_samples > 0:
        return False, ['Number of logic tree samples must be > 0']
    return True, []

def rupture_mesh_spacing_is_valid(mdl):
    if not mdl.rupture_mesh_spacing > 0:
        return False, ['Rupture mesh spacing must be > 0']
    return True, []

def width_of_mfd_bin_is_valid(mdl):
    if not mdl.width_of_mfd_bin > 0:
        return False, ['Width of MFD bin must be > 0']
    return True, []

def area_source_discretization_is_valid(mdl):
    if not mdl.area_source_discretization > 0:
        return False, ['Area source discretization must be > 0']
    return True, []

def reference_vs30_value_is_valid(mdl):
    if not mdl.reference_vs30_value > 0:
        return False, ['Reference VS30 value must be > 0']
    return True, []

def reference_vs30_type_is_valid(mdl):
    if not mdl.reference_vs30_type in ('measured', 'inferred'):
        return False, ['Reference VS30 type must be either '
                       '"measured" or "inferred"']
    return True, []

def reference_depth_to_2pt5km_per_sec_is_valid(mdl):
    if not mdl.reference_depth_to_2pt5km_per_sec > 0:
        return False, ['Reference depth to 2.5 km/sec must be > 0']
    return True, []

def reference_depth_to_1pt0km_per_sec_is_valid(mdl):
    if not mdl.reference_depth_to_1pt0km_per_sec > 0:
        return False, ['Reference depth to 1.0 km/sec must be > 0']
    return True, []

def investigation_time_is_valid(mdl):
    if not mdl.investigation_time > 0:
        return False, ['Investigation time must be > 0']
    return True, []

def intensity_measure_types_and_levels_is_valid(mdl):
    im = mdl.intensity_measure_types_and_levels

    valid = True
    errors = []

    # SA intensity measure configs need special handling
    imts = list(set(imt.__all__) - set(['SA']))

    for im_type, imls in im.iteritems():
        if im_type in imts:
            if not isinstance(imls, list):
                valid = False
                errors.append(
                    '%s: IMLs must be specified as a list of floats' % im_type
                )
            else:
                if len(imls) == 0:
                    valid = False
                    errors.append(
                        '%s: IML lists must have at least 1 value' % im_type
                    )
                elif not all([x > 0 for x in imls]):
                    valid = False
                    errors.append('%s: IMLs must be > 0' % im_type)
        elif 'SA' in im_type:
            match = re.match(r'^SA\(([^)]+?)\)$', im_type)
            if match is None:
                # SA key is not formatted properly
                valid = False
                errors.append(
                    '%s: SA must be specified with a period value, in the form'
                    ' `SA(N)`, where N is a value >= 0' % im_type
                )
            else:
                # there's a match; make sure the period value is valid
                sa_period = match.groups()[0]
                try:
                    if float(sa_period) < 0:
                        valid = False
                        errors.append(
                            '%s: SA period values must be >= 0' % im_type
                        )
                except ValueError:
                    valid = False
                    errors.append(
                        '%s: SA period value should be a float >= 0' % im_type
                    )
        else:
            valid = False
            errors.append('%s: Invalid intensity measure type' % im_type)

    return valid, errors

def truncation_level_is_valid(mdl):
    if not mdl.truncation_level >= 0:
        return False, ['Truncation level must be >= 0']
    return True, []

def maximum_distance_is_valid(mdl):
    if not mdl.maximum_distance > 0:
        return False, ['Maximum distance must be > 0']
    return True, []

def mean_hazard_curves_is_valid(_mdl):
    # The validation form should normalize the type to a boolean.
    # We don't need to check anything here.
    return True, []

def quantile_hazard_curves_is_valid(mdl):
    qhc = mdl.quantile_hazard_curves

    if qhc is not None:
        if not all([0.0 <= x <= 1.0 for x in qhc]):
            return False, ['Quantile hazard curve values must in the range '
                           '[0, 1]']
    return True, []

def poes_hazard_maps_is_valid(mdl):
    phm = mdl.poes_hazard_maps

    if phm is not None:
        if not all([0.0 <= x <= 1.0 for x in phm]):
            return False, ['PoEs for hazard maps must be in the range [0, 1]']
    return True, []

class BaseOQModelForm(ModelForm):
    """This class is based on :class:`django.forms.ModelForm`. Constructor
    arguments are the same.

    Since we're using forms (at the moment) purely for model validation, it's
    worth noting how we're using forms and what sort of inputs should be
    supplied.

    At the very least, an `instance` should be specified, which is expected to
    be a Django model object (perhaps one from :mod:`openquake.db.models`).

    `data` can be specified to populate the form and model. If no `data` is
    specified, the form will the current data the `instance`.

    You can also specify `files`. In the Django web form context, this
    represents a `dict` of name-file_object pairs. The file object type can be,
    for example, one of the types in :mod:`django.core.files.uploadedfile`.

    In this case, however, we expect `files` to be a dict of
    :class:`openquake.db.models.Input`, keyed by config file parameter for the
    input. For example::

    {'site_model_file': <Input: 174||site_model||0xdeadbeef||>}
    """

    def __init__(self, *args, **kwargs):
        if not 'data' in kwargs:
            # Because we're not using ModelForms in exactly the
            # originally-intended modus operandi, we need to pass all of the
            # field values from the instance model object as the `data` kwarg
            # (`data` needs to be a dict of fieldname-value pairs).
            # This serves to populate the form (as if a user had done so) and
            # immediately enables validation checking (through `is_valid()`,
            # for example).
            # This is, of course, only applicable if `instance` was supplied to
            # the form. For the purpose of just doing validation (which is why
            # these forms were created), we need to specify the `instance`.
            instance = kwargs.get('instance')
            if instance is not None:
                kwargs['data'] = instance.__dict__
        super(BaseOQModelForm, self).__init__(*args, **kwargs)

class ClassicalHazardCalculationForm(BaseOQModelForm):

    # These fields require more complex validation.
    # The rules for these fields depend on other parameters
    # and files.
    special_fields = (
        'region',
        'region_grid_spacing',
        'sites',
        'reference_vs30_value',
        'reference_vs30_type',
        'reference_depth_to_2pt5km_per_sec',
        'reference_depth_to_1pt0km_per_sec',
    )

    class Meta:
        model = models.HazardCalculation
        fields = (
            'description',
            'calculation_mode',
            'region',
            'region_grid_spacing',
            'sites',
            'random_seed',
            'number_of_logic_tree_samples',
            'rupture_mesh_spacing',
            'width_of_mfd_bin',
            'area_source_discretization',
            'reference_vs30_value',
            'reference_vs30_type',
            'reference_depth_to_2pt5km_per_sec',
            'reference_depth_to_1pt0km_per_sec',
            'investigation_time',
            'intensity_measure_types_and_levels',
            'truncation_level',
            'maximum_distance',
            'mean_hazard_curves',
            'quantile_hazard_curves',
            'poes_hazard_maps',
        )

    def _add_error(self, field_name, error_msg):
        """Add an error to the `errors` dict.

        If errors for the given ``field_name`` already exist append the error
        to that list. Otherwise, a new entry will have to be created for the
        ``field_name`` to hold the ``error_msg``.

        ``error_msg`` can also be a list or tuple of error messages.
        """
        is_list = isinstance(error_msg, (list, tuple))
        if self.errors.get(field_name) is not None:
            if is_list:
                self.errors[field_name].extend(error_msg)
            else:
                self.errors[field_name].append(error_msg)
        else:
            # no errors for this field have been recorded yet
            if is_list:
                if len(error_msg) > 0:
                    self.errors[field_name] = error_msg
            else:
                self.errors[field_name] = [error_msg]

    def is_valid(self):
        """Overrides :meth:`django.forms.ModelForm.is_valid` to perform
        custom validation checks (in addition to superclass validation).

        :returns:
            If valid return `True`, else `False`.
        """
        super_valid = super(ClassicalHazardCalculationForm, self).is_valid()
        all_valid = super_valid

        # HazardCalculation
        hjp = self.instance

        # Exclude special fields that require contextual validation.
        for field in sorted(set(self.fields) - set(self.special_fields)):
            valid, errs = eval('%s_is_valid' % field)(hjp)
            all_valid = all_valid and valid

            self._add_error(field, errs)

        # Now do checks which require more context.

        # Cannot specify region AND sites
        if (hjp.region is not None
            and hjp.sites is not None):
            all_valid = False
            err = 'Cannot specify `region` and `sites`. Choose one.'
            self._add_error('region', err)
        # At least one must be specified (region OR sites)
        elif not (hjp.region is not None or hjp.sites is not None):
            all_valid = False
            err = 'Must specify either `region` or `sites`.'
            self._add_error('region', err)
            self._add_error('sites', err)
        # Only region is specified
        elif hjp.region is not None:
            if hjp.region_grid_spacing is not None:
                valid, errs = region_grid_spacing_is_valid(hjp)
                all_valid = all_valid and valid

                self._add_error('region_grid_spacing', errs)
            else:
                all_valid = False
                err = '`region` requires `region_grid_spacing`'
                self._add_error('region', err)

            # validate the region
            valid, errs = region_is_valid(hjp)
            all_valid = all_valid and valid
            self._add_error('region', errs)
        # Only sites was specified
        else:
            valid, errs = sites_is_valid(hjp)
            all_valid = all_valid and valid
            self._add_error('sites', errs)

        if 'site_model_file' not in self.files:
            # make sure the reference parameters are defined and valid

            for field in (
                'reference_vs30_value',
                'reference_vs30_type',
                'reference_depth_to_2pt5km_per_sec',
                'reference_depth_to_1pt0km_per_sec',
            ):
                valid, errs = eval('%s_is_valid' % field)(hjp)
                all_valid = all_valid and valid
                self._add_error(field, errs)

        return all_valid
<commit_msg>Corrected a comment<commit_after># Copyright (c) 2010-2012, GEM Foundation.
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.

"""This module contains functions and Django model forms for carrying out job
profile validation.
"""

import re

from django.forms import ModelForm
from nhlib import imt

from openquake.db import models

#: Minimum value for a signed 32-bit int
MIN_SINT_32 = -(2 ** 31)
#: Maximum value for a signed 32-bit int
MAX_SINT_32 = (2 ** 31) - 1

# Silencing 'Missing docstring' and 'Invalid name' for all of the validation
# functions (the latter because some of the function names are very long).
# pylint: disable=C0111,C0103

def description_is_valid(_mdl):
    return True, []

def calculation_mode_is_valid(mdl):
    if not mdl.calculation_mode == 'classical':
        return False, ['Calculation mode must be "classical"']
    return True, []

def region_is_valid(mdl):
    valid = True
    errors = []

    if not mdl.region.valid:
        valid = False
        errors.append('Invalid region geomerty: %s' % mdl.region.valid_reason)

    if len(mdl.region.coords) > 1:
        valid = False
        errors.append('Region geometry can only be a single linear ring')

    # There should only be a single linear ring.
    # Even if there are multiple, we can still check for and report errors.
    for ring in mdl.region.coords:
        lons = [lon for lon, _ in ring]
        lats = [lat for _, lat in ring]
        if not all([-180 <= x <= 180 for x in lons]):
            valid = False
            errors.append('Longitude values must in the range [-180, 180]')
        if not all([-90 <= x <= 90 for x in lats]):
            valid = False
            errors.append('Latitude values must be in the range [-90, 90]')
    return valid, errors

def region_grid_spacing_is_valid(mdl):
    if not mdl.region_grid_spacing > 0:
        return False, ['Region grid spacing must be > 0']
    return True, []

def sites_is_valid(mdl):
    valid = True
    errors = []

    lons = [pt.x for pt in mdl.sites]
    lats = [pt.y for pt in mdl.sites]
    if not all([-180 <= x <= 180 for x in lons]):
        valid = False
        errors.append('Longitude values must in the range [-180, 180]')
    if not all([-90 <= x <= 90 for x in lats]):
        valid = False
        errors.append('Latitude values must be in the range [-90, 90]')

    return valid, errors

def random_seed_is_valid(mdl):
    if not MIN_SINT_32 <= mdl.random_seed <= MAX_SINT_32:
        return False, [('Random seed must be a value from %s to %s (inclusive)'
                       % (MIN_SINT_32, MAX_SINT_32))]
    return True, []

def number_of_logic_tree_samples_is_valid(mdl):
    if not mdl.number_of_logic_tree_samples > 0:
        return False, ['Number of logic tree samples must be > 0']
    return True, []

def rupture_mesh_spacing_is_valid(mdl):
    if not mdl.rupture_mesh_spacing > 0:
        return False, ['Rupture mesh spacing must be > 0']
    return True, []

def width_of_mfd_bin_is_valid(mdl):
    if not mdl.width_of_mfd_bin > 0:
        return False, ['Width of MFD bin must be > 0']
    return True, []

def area_source_discretization_is_valid(mdl):
    if not mdl.area_source_discretization > 0:
        return False, ['Area source discretization must be > 0']
    return True, []

def reference_vs30_value_is_valid(mdl):
    if not mdl.reference_vs30_value > 0:
        return False, ['Reference VS30 value must be > 0']
    return True, []

def reference_vs30_type_is_valid(mdl):
    if not mdl.reference_vs30_type in ('measured', 'inferred'):
        return False, ['Reference VS30 type must be either '
                       '"measured" or "inferred"']
    return True, []

def reference_depth_to_2pt5km_per_sec_is_valid(mdl):
    if not mdl.reference_depth_to_2pt5km_per_sec > 0:
        return False, ['Reference depth to 2.5 km/sec must be > 0']
    return True, []

def reference_depth_to_1pt0km_per_sec_is_valid(mdl):
    if not mdl.reference_depth_to_1pt0km_per_sec > 0:
        return False, ['Reference depth to 1.0 km/sec must be > 0']
    return True, []

def investigation_time_is_valid(mdl):
    if not mdl.investigation_time > 0:
        return False, ['Investigation time must be > 0']
    return True, []

def intensity_measure_types_and_levels_is_valid(mdl):
    im = mdl.intensity_measure_types_and_levels

    valid = True
    errors = []

    # SA intensity measure configs need special handling
    imts = list(set(imt.__all__) - set(['SA']))

    for im_type, imls in im.iteritems():
        if im_type in imts:
            if not isinstance(imls, list):
                valid = False
                errors.append(
                    '%s: IMLs must be specified as a list of floats' % im_type
                )
            else:
                if len(imls) == 0:
                    valid = False
                    errors.append(
                        '%s: IML lists must have at least 1 value' % im_type
                    )
                elif not all([x > 0 for x in imls]):
                    valid = False
                    errors.append('%s: IMLs must be > 0' % im_type)
        elif 'SA' in im_type:
            match = re.match(r'^SA\(([^)]+?)\)$', im_type)
            if match is None:
                # SA key is not formatted properly
                valid = False
                errors.append(
                    '%s: SA must be specified with a period value, in the form'
                    ' `SA(N)`, where N is a value >= 0' % im_type
                )
            else:
                # there's a match; make sure the period value is valid
                sa_period = match.groups()[0]
                try:
                    if float(sa_period) < 0:
                        valid = False
                        errors.append(
                            '%s: SA period values must be >= 0' % im_type
                        )
                except ValueError:
                    valid = False
                    errors.append(
                        '%s: SA period value should be a float >= 0' % im_type
                    )
        else:
            valid = False
            errors.append('%s: Invalid intensity measure type' % im_type)

    return valid, errors

def truncation_level_is_valid(mdl):
    if not mdl.truncation_level >= 0:
        return False, ['Truncation level must be >= 0']
    return True, []

def maximum_distance_is_valid(mdl):
    if not mdl.maximum_distance > 0:
        return False, ['Maximum distance must be > 0']
    return True, []

def mean_hazard_curves_is_valid(_mdl):
    # The validation form should normalize the type to a boolean.
    # We don't need to check anything here.
    return True, []

def quantile_hazard_curves_is_valid(mdl):
    qhc = mdl.quantile_hazard_curves

    if qhc is not None:
        if not all([0.0 <= x <= 1.0 for x in qhc]):
            return False, ['Quantile hazard curve values must in the range '
                           '[0, 1]']
    return True, []

def poes_hazard_maps_is_valid(mdl):
    phm = mdl.poes_hazard_maps

    if phm is not None:
        if not all([0.0 <= x <= 1.0 for x in phm]):
            return False, ['PoEs for hazard maps must be in the range [0, 1]']
    return True, []

class BaseOQModelForm(ModelForm):
    """This class is based on :class:`django.forms.ModelForm`. Constructor
    arguments are the same.

    Since we're using forms (at the moment) purely for model validation, it's
    worth noting how we're using forms and what sort of inputs should be
    supplied.

    At the very least, an `instance` should be specified, which is expected to
    be a Django model object (perhaps one from :mod:`openquake.db.models`).

    `data` can be specified to populate the form and model. If no `data` is
    specified, the form will take the current data from the `instance`.

    You can also specify `files`. In the Django web form context, this
    represents a `dict` of name-file_object pairs. The file object type can be,
    for example, one of the types in :mod:`django.core.files.uploadedfile`.

    In this case, however, we expect `files` to be a dict of
    :class:`openquake.db.models.Input`, keyed by config file parameter for the
    input. For example::

    {'site_model_file': <Input: 174||site_model||0xdeadbeef||>}
    """

    def __init__(self, *args, **kwargs):
        if not 'data' in kwargs:
            # Because we're not using ModelForms in exactly the
            # originally-intended modus operandi, we need to pass all of the
            # field values from the instance model object as the `data` kwarg
            # (`data` needs to be a dict of fieldname-value pairs).
            # This serves to populate the form (as if a user had done so) and
            # immediately enables validation checking (through `is_valid()`,
            # for example).
            # This is, of course, only applicable if `instance` was supplied to
            # the form. For the purpose of just doing validation (which is why
            # these forms were created), we need to specify the `instance`.
            instance = kwargs.get('instance')
            if instance is not None:
                kwargs['data'] = instance.__dict__
        super(BaseOQModelForm, self).__init__(*args, **kwargs)

class ClassicalHazardCalculationForm(BaseOQModelForm):

    # These fields require more complex validation.
    # The rules for these fields depend on other parameters
    # and files.
    special_fields = (
        'region',
        'region_grid_spacing',
        'sites',
        'reference_vs30_value',
        'reference_vs30_type',
        'reference_depth_to_2pt5km_per_sec',
        'reference_depth_to_1pt0km_per_sec',
    )

    class Meta:
        model = models.HazardCalculation
        fields = (
            'description',
            'calculation_mode',
            'region',
            'region_grid_spacing',
            'sites',
            'random_seed',
            'number_of_logic_tree_samples',
            'rupture_mesh_spacing',
            'width_of_mfd_bin',
            'area_source_discretization',
            'reference_vs30_value',
            'reference_vs30_type',
            'reference_depth_to_2pt5km_per_sec',
            'reference_depth_to_1pt0km_per_sec',
            'investigation_time',
            'intensity_measure_types_and_levels',
            'truncation_level',
            'maximum_distance',
            'mean_hazard_curves',
            'quantile_hazard_curves',
            'poes_hazard_maps',
        )

    def _add_error(self, field_name, error_msg):
        """Add an error to the `errors` dict.

        If errors for the given ``field_name`` already exist append the error
        to that list. Otherwise, a new entry will have to be created for the
        ``field_name`` to hold the ``error_msg``.

        ``error_msg`` can also be a list or tuple of error messages.
        """
        is_list = isinstance(error_msg, (list, tuple))
        if self.errors.get(field_name) is not None:
            if is_list:
                self.errors[field_name].extend(error_msg)
            else:
                self.errors[field_name].append(error_msg)
        else:
            # no errors for this field have been recorded yet
            if is_list:
                if len(error_msg) > 0:
                    self.errors[field_name] = error_msg
            else:
                self.errors[field_name] = [error_msg]

    def is_valid(self):
        """Overrides :meth:`django.forms.ModelForm.is_valid` to perform
        custom validation checks (in addition to superclass validation).

        :returns:
            If valid return `True`, else `False`.
        """
        super_valid = super(ClassicalHazardCalculationForm, self).is_valid()
        all_valid = super_valid

        # HazardCalculation
        hjp = self.instance

        # Exclude special fields that require contextual validation.
        for field in sorted(set(self.fields) - set(self.special_fields)):
            valid, errs = eval('%s_is_valid' % field)(hjp)
            all_valid = all_valid and valid

            self._add_error(field, errs)

        # Now do checks which require more context.

        # Cannot specify region AND sites
        if (hjp.region is not None
            and hjp.sites is not None):
            all_valid = False
            err = 'Cannot specify `region` and `sites`. Choose one.'
            self._add_error('region', err)
        # At least one must be specified (region OR sites)
        elif not (hjp.region is not None or hjp.sites is not None):
            all_valid = False
            err = 'Must specify either `region` or `sites`.'
            self._add_error('region', err)
            self._add_error('sites', err)
        # Only region is specified
        elif hjp.region is not None:
            if hjp.region_grid_spacing is not None:
                valid, errs = region_grid_spacing_is_valid(hjp)
                all_valid = all_valid and valid

                self._add_error('region_grid_spacing', errs)
            else:
                all_valid = False
                err = '`region` requires `region_grid_spacing`'
                self._add_error('region', err)

            # validate the region
            valid, errs = region_is_valid(hjp)
            all_valid = all_valid and valid
            self._add_error('region', errs)
        # Only sites was specified
        else:
            valid, errs = sites_is_valid(hjp)
            all_valid = all_valid and valid
            self._add_error('sites', errs)

        if 'site_model_file' not in self.files:
            # make sure the reference parameters are defined and valid

            for field in (
                'reference_vs30_value',
                'reference_vs30_type',
                'reference_depth_to_2pt5km_per_sec',
                'reference_depth_to_1pt0km_per_sec',
            ):
                valid, errs = eval('%s_is_valid' % field)(hjp)
                all_valid = all_valid and valid
                self._add_error(field, errs)

        return all_valid
<|endoftext|>


<commit_before>
"""Contains the functions for each rcv command-line command."""

import logging
import os
from textwrap import dedent
import sys

import yaml

from openrcv import counting
from openrcv import jcmanage
from openrcv.formats import jscase
from openrcv.formats.internal import parse_internal_ballot
from openrcv import jcmodels, jsonlib, models
from openrcv.models import BallotStreamResource, ContestInput
from openrcv.utils import logged_open, PathInfo, StringInfo

log = logging.getLogger(__name__)

# TODO: finish removing references to ns in this module.
#  This will decouple the argparse definitions from these functions.
# TODO: unit-test this.
def count(ns, stdout=None):
    input_path = ns.input_path
    with logged_open(input_path) as f:
        config = yaml.load(f)
    # TODO: use a common pattern for accessing config values.
    base_dir = os.path.dirname(input_path)
    config = config['openrcv']
    contests = config['contests']
    contest = contests[0]
    blt_path = os.path.join(base_dir, contest['file'])
    results = counting.count_irv(blt_path)
    json_results = jcmodels.JsonTestCaseOutput.from_contest_results(results)
    print(json_results.to_json())

def make_random_contest(ballot_count, candidate_count, format_cls,
                        json_contests_path, output_dir,
                        normalize=False, stdout=None):
    """Generate a random contest."""
    if stdout is None:
        stdout = sys.stdout
    format = format_cls()

    creator_cls = (jcmanage.NormalizedContestCreator if normalize else
                   jcmanage.ContestCreator)
    creator = creator_cls()
    with models.temp_ballots_resource() as ballots_resource:
        contest = creator.create_random(ballots_resource, ballot_count=ballot_count,
            candidate_count=candidate_count)
        output_paths = format.write_contest(contest, output_dir=output_dir, stdout=stdout)

        # TODO: refactor this out into jcmanage.
        if json_contests_path:
            jc_contest = jscase.JsonCaseContestInput.from_object(contest)
            jsobj_contest = jc_contest.to_jsobj()
            data = jsonlib.read_json_path(json_contests_path)
            data["contests"].append(jsobj_contest)
            jsonlib.write_json(data, path=json_contests_path)

    return "\n".join(output_paths) + "\n" if output_paths else None

# TODO: refactor code out into jcmanage.
# TODO: log normalization conversions (e.g. if they are unequal).
def clean_contests(json_path):
    jsobj = jsonlib.read_json_path(json_path)
    test_file = jcmodels.JsonCaseContestsFile.from_jsobj(jsobj)
    jc_contests = []
    for id_, jc_contest in enumerate(test_file.contests, start=1):
        contest = jc_contest.to_model()
        contest.id = id_
        contest.normalize()
    #jsonlib.write_json(test_file, path=json_path)
<commit_msg>Remove unneeded import from commands.py.<commit_after>
"""Contains the functions for each rcv command-line command."""

import logging
import os
from textwrap import dedent
import sys

import yaml

from openrcv import counting
from openrcv import jcmanage
from openrcv.formats import jscase
from openrcv.formats.internal import parse_internal_ballot
from openrcv import jcmodels, jsonlib, models
from openrcv.models import ContestInput
from openrcv.utils import logged_open, PathInfo, StringInfo

log = logging.getLogger(__name__)

# TODO: finish removing references to ns in this module.
#  This will decouple the argparse definitions from these functions.
# TODO: unit-test this.
def count(ns, stdout=None):
    input_path = ns.input_path
    with logged_open(input_path) as f:
        config = yaml.load(f)
    # TODO: use a common pattern for accessing config values.
    base_dir = os.path.dirname(input_path)
    config = config['openrcv']
    contests = config['contests']
    contest = contests[0]
    blt_path = os.path.join(base_dir, contest['file'])
    results = counting.count_irv(blt_path)
    json_results = jcmodels.JsonTestCaseOutput.from_contest_results(results)
    print(json_results.to_json())

def make_random_contest(ballot_count, candidate_count, format_cls,
                        json_contests_path, output_dir,
                        normalize=False, stdout=None):
    """Generate a random contest."""
    if stdout is None:
        stdout = sys.stdout
    format = format_cls()

    creator_cls = (jcmanage.NormalizedContestCreator if normalize else
                   jcmanage.ContestCreator)
    creator = creator_cls()
    with models.temp_ballots_resource() as ballots_resource:
        contest = creator.create_random(ballots_resource, ballot_count=ballot_count,
            candidate_count=candidate_count)
        output_paths = format.write_contest(contest, output_dir=output_dir, stdout=stdout)

        # TODO: refactor this out into jcmanage.
        if json_contests_path:
            jc_contest = jscase.JsonCaseContestInput.from_object(contest)
            jsobj_contest = jc_contest.to_jsobj()
            data = jsonlib.read_json_path(json_contests_path)
            data["contests"].append(jsobj_contest)
            jsonlib.write_json(data, path=json_contests_path)

    return "\n".join(output_paths) + "\n" if output_paths else None

# TODO: refactor code out into jcmanage.
# TODO: log normalization conversions (e.g. if they are unequal).
def clean_contests(json_path):
    jsobj = jsonlib.read_json_path(json_path)
    test_file = jcmodels.JsonCaseContestsFile.from_jsobj(jsobj)
    jc_contests = []
    for id_, jc_contest in enumerate(test_file.contests, start=1):
        contest = jc_contest.to_model()
        contest.id = id_
        contest.normalize()
    #jsonlib.write_json(test_file, path=json_path)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.conf import settings
from django.contrib.syndication.views import Feed
from django.contrib.sites.models import get_current_site
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.utils import feedgenerator
from opps.containers.models import Container
from opps.channels.models import Channel

class ItemFeed(Feed):

    feed_type = feedgenerator.Rss201rev2Feed
    description_template = 'articles/feed_item_description.html'
    item_enclosure_length = 1
    item_enclosure_mime_type = "image/jpeg"

    def item_categories(self, obj):
        cats = []
        if obj.channel:
            cats.append(obj.channel.name)
        if getattr(obj, 'tags', None) is not None:
            cats.extend(obj.get_tags())
        return cats

    def item_title(self, item):
        return item.title

    def item_pubdate(self, item):
        return item.date_available

    def item_updateddate(self, item):
        return item.date_update

    def item_link(self, item):
        return item.get_absolute_url()

    def item_enclosure_url(self, item):
        if item.main_image:
            i_url = item.main_image.image_url()
            m_url = getattr(settings, 'MEDIA_URL', '')
            if not m_url.startswith('http') and not i_url.startswith('http'):
                i_url = "http://" + self.site.domain + i_url
            return i_url

class ContainerFeed(ItemFeed):

    link = "/rss"

    def __init__(self, child_class=False):
        self.child_class = child_class

    def __call__(self, request, *args, **kwargs):
        self.site = get_current_site(request)
        return super(ContainerFeed, self).__call__(request, *args, **kwargs)

    def title(self):
        return _("{0}'s news".format(self.site.name))

    def description(self):
        return _("Latest news on {0}'s".format(self.site.name))

    def items(self):
        container = Container.objects.filter(
            site=self.site,
            date_available__lte=timezone.now(),
            published=True,
            channel__include_in_main_rss=True,
            channel__published=True
        )

        if self.child_class:
            container = container.filter(child_class=self.child_class)

        container = container.exclude(child_class__in=['Mirror', 'Entry'])

        return container.order_by('-date_available')[:40]

class ChannelFeed(ItemFeed):

    def get_object(self, request, long_slug):
        self.site = get_current_site(request)
        return get_object_or_404(Channel,
                                 site=self.site,
                                 long_slug=long_slug)

    def link(self, obj):
        return _("{0}RSS".format(obj.get_absolute_url()))

    def title(self, obj):
        return _(u"{0}'s news on channel {1}".format(self.site.name,
                                                     obj.name))

    def description(self, obj):
        return _(u"Latest news on {0}'s channel {1}".format(self.site.name,
                                                            obj.name))

    def items(self, obj):
        return Container.objects.filter(
            site=self.site,
            channel_long_slug=obj.long_slug,
            date_available__lte=timezone.now(),
            published=True,
        ).exclude(
            child_class__in=['Mirror', 'Entry']
        ).order_by(
            '-date_available'
        ).select_related('publisher')[:40]

class ContainerAtomFeed(ContainerFeed):
    link = "/atom"
    feed_type = feedgenerator.Atom1Feed

class ChannelAtomFeed(ChannelFeed):
    feed_type = feedgenerator.Atom1Feed
<commit_msg>Fix TypeError when get_tags is None<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.conf import settings
from django.contrib.syndication.views import Feed
from django.contrib.sites.models import get_current_site
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.utils import feedgenerator
from opps.containers.models import Container
from opps.channels.models import Channel

class ItemFeed(Feed):

    feed_type = feedgenerator.Rss201rev2Feed
    description_template = 'articles/feed_item_description.html'
    item_enclosure_length = 1
    item_enclosure_mime_type = "image/jpeg"

    def item_categories(self, obj):
        cats = []
        if obj.channel:
            cats.append(obj.channel.name)
        if getattr(obj, 'tags', None) is not None:
            cats.extend(obj.get_tags() or [])
        return cats

    def item_title(self, item):
        return item.title

    def item_pubdate(self, item):
        return item.date_available

    def item_updateddate(self, item):
        return item.date_update

    def item_link(self, item):
        return item.get_absolute_url()

    def item_enclosure_url(self, item):
        if item.main_image:
            i_url = item.main_image.image_url()
            m_url = getattr(settings, 'MEDIA_URL', '')
            if not m_url.startswith('http') and not i_url.startswith('http'):
                i_url = "http://" + self.site.domain + i_url
            return i_url

class ContainerFeed(ItemFeed):

    link = "/rss"

    def __init__(self, child_class=False):
        self.child_class = child_class

    def __call__(self, request, *args, **kwargs):
        self.site = get_current_site(request)
        return super(ContainerFeed, self).__call__(request, *args, **kwargs)

    def title(self):
        return _("{0}'s news".format(self.site.name))

    def description(self):
        return _("Latest news on {0}'s".format(self.site.name))

    def items(self):
        container = Container.objects.filter(
            site=self.site,
            date_available__lte=timezone.now(),
            published=True,
            channel__include_in_main_rss=True,
            channel__published=True
        )

        if self.child_class:
            container = container.filter(child_class=self.child_class)

        container = container.exclude(child_class__in=['Mirror', 'Entry'])

        return container.order_by('-date_available')[:40]

class ChannelFeed(ItemFeed):

    def get_object(self, request, long_slug):
        self.site = get_current_site(request)
        return get_object_or_404(Channel,
                                 site=self.site,
                                 long_slug=long_slug)

    def link(self, obj):
        return _("{0}RSS".format(obj.get_absolute_url()))

    def title(self, obj):
        return _(u"{0}'s news on channel {1}".format(self.site.name,
                                                     obj.name))

    def description(self, obj):
        return _(u"Latest news on {0}'s channel {1}".format(self.site.name,
                                                            obj.name))

    def items(self, obj):
        return Container.objects.filter(
            site=self.site,
            channel_long_slug=obj.long_slug,
            date_available__lte=timezone.now(),
            published=True,
        ).exclude(
            child_class__in=['Mirror', 'Entry']
        ).order_by(
            '-date_available'
        ).select_related('publisher')[:40]

class ContainerAtomFeed(ContainerFeed):
    link = "/atom"
    feed_type = feedgenerator.Atom1Feed

class ChannelAtomFeed(ChannelFeed):
    feed_type = feedgenerator.Atom1Feed
<|endoftext|>


<commit_before>#!/usr/bin/python

# gtfsrdb.py: load gtfs-realtime data to a database
# recommended to have the (static) GTFS data for the agency you are connecting
# to already loaded.

# Copyright 2011, 2013 Matt Conway

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#   http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Authors:
# Matt Conway: main code

'''
  https://github.com/mattwigway/gtfsrdb
  http://trimet.org/transweb/ws/V1/FeedSpecAlerts/appId/3819A6A38C72223198B560DF0/includeFuture/true

  bin/python ott/controller/database/gtfsrdb/gtfsrdb.py -d sqlite:///gtfsdb.db -a http://trimet.org/transweb/ws/V1/FeedSpecAlerts/appId/3819A6A38C72223198B560DF0/includeFuture/true -o -w 300

'''

from optparse import OptionParser
import time
import sys
import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from urllib2 import urlopen
from . import gtfs_realtime_pb2
from . import alerts
from . import model
from utils import getTrans
from .model import *

p = OptionParser()
p.add_option('-t', '--trip-updates', dest='tripUpdates', default=None,
             help='The trip updates URL', metavar='URL')

p.add_option('-a', '--alerts', default=None, dest='alerts',
             help='The alerts URL', metavar='URL')

p.add_option('-p', '--vehicle-positions', dest='vehiclePositions', default=None,
             help='The vehicle positions URL', metavar='URL')

p.add_option('-d', '--database', default=None, dest='dsn',
             help='Database connection string', metavar='DSN')

p.add_option('-o', '--discard-old', default=False, dest='deleteOld',
             action='store_true',
             help='Discard old updates, so the database is always current')

p.add_option('-c', '--create-tables', default=False, dest='create',
             action='store_true', help="Create tables if they aren't found")

p.add_option('-w', '--wait', default=30, type='int', metavar='SECS',
             dest='timeout', help='Time to wait between requests (in seconds)')

p.add_option('-v', '--verbose', default=False, dest='verbose',
             action='store_true', help='Print generated SQL')

p.add_option('-l', '--language', default='en', dest='lang', metavar='LANG',
             help='When multiple translations are available, prefer this language')

p.add_option('-s', '--schema',   default=None, dest='schema', help='Database schema')

p.add_option('-1', '--once',   default=None, dest='once', help='only run the loader one time')

opts, args = p.parse_args()

if opts.dsn == None:
    print 'No database specified!'
    exit(1)

if opts.alerts == None and opts.tripUpdates == None and opts.vehiclePositions == None:
    print 'No trip updates, alerts, or vehicle positions URLs were specified!'
    exit(1)

if opts.alerts == None:
    print 'Warning: no alert URL specified, proceeding without alerts'

if opts.tripUpdates == None:
    print 'Warning: no trip update URL specified, proceeding without trip updates'

if opts.vehiclePositions == None:
    print 'Warning: no vehicle positions URL specified, proceeding without vehicle positions'

# add DB schema definitions the table class meta data...
if opts.schema:
    model.add_schema(opts.schema)

# Connect to the database (and implicitly handle any non-default schema)
engine = create_engine(opts.dsn, echo=opts.verbose)

# sessionmaker returns a class
session = sessionmaker(bind=engine)()

# Check if it has the tables
# Base from model.py
for table in Base.metadata.tables.keys():
    if not engine.has_table(table, opts.schema):
        if opts.create:
            print 'Creating table %s' % table
            Base.metadata.tables[table].create(engine)
        else:
            print 'Missing table %s! Use -c to create it.' % table
            exit(1)

def main():
    try:
        keep_running = True
        while keep_running:
            success = True
            try:
            #if True:
                if opts.deleteOld:
                    # Go through all of the tables that we create, clear them
                    # Don't mess with other tables (i.e., tables from static GTFS)
                    for theClass in AllClasses:
                        for obj in session.query(theClass):
                            session.delete(obj)

                if opts.tripUpdates:
                    fm = gtfs_realtime_pb2.FeedMessage()
                    fm.ParseFromString(
                        urlopen(opts.tripUpdates).read()
                        )

                    # Convert this a Python object, and save it to be placed into each
                    # trip_update
                    timestamp = datetime.datetime.utcfromtimestamp(fm.header.timestamp)

                    # Check the feed version
                    if fm.header.gtfs_realtime_version != u'1.0':
                        print 'Warning: feed version has changed: found %s, expected 1.0' % fm.header.gtfs_realtime_version

                    print 'Adding %s trip updates' % len(fm.entity)
                    for entity in fm.entity:
                        tu = entity.trip_update
                        dbtu = TripUpdate(
                            trip_id = tu.trip.trip_id,
                            route_id = tu.trip.route_id,
                            trip_start_time = tu.trip.start_time,
                            trip_start_date = tu.trip.start_date,

                            # get the schedule relationship
                            # This is somewhat undocumented, but by referencing the
                            # DESCRIPTOR.enum_types_by_name, you get a dict of enum types
                            # as described at http://code.google.com/apis/protocolbuffers/docs/reference/python/google.protobuf.descriptor.EnumDescriptor-class.html
                            schedule_relationship = tu.trip.DESCRIPTOR.enum_types_by_name['ScheduleRelationship'].values_by_number[tu.trip.schedule_relationship].name,

                            vehicle_id = tu.vehicle.id,
                            vehicle_label = tu.vehicle.label,
                            vehicle_license_plate = tu.vehicle.license_plate,
                            timestamp = timestamp)

                        for stu in tu.stop_time_update:
                            dbstu = StopTimeUpdate(
                                stop_sequence = stu.stop_sequence,
                                stop_id = stu.stop_id,
                                arrival_delay = stu.arrival.delay,
                                arrival_time = stu.arrival.time,
                                arrival_uncertainty = stu.arrival.uncertainty,
                                departure_delay = stu.departure.delay,
                                departure_time = stu.departure.time,
                                departure_uncertainty = stu.departure.uncertainty,
                                schedule_relationship = tu.trip.DESCRIPTOR.enum_types_by_name['ScheduleRelationship'].values_by_number[tu.trip.schedule_relationship].name
                            )
                            session.add(dbstu)
                            dbtu.StopTimeUpdates.append(dbstu)

                        session.add(dbtu)

                if opts.alerts:
                    alerts.make_alert(session, gtfs_realtime_pb2, opts)

                if opts.vehiclePositions:
                    fm = gtfs_realtime_pb2.FeedMessage()
                    fm.ParseFromString(
                        urlopen(opts.vehiclePositions).read()
                        )

                    # Convert this a Python object, and save it to be placed into each
                    # vehicle_position
                    timestamp = datetime.datetime.utcfromtimestamp(fm.header.timestamp)

                    # Check the feed version
                    if fm.header.gtfs_realtime_version != u'1.0':
                        print 'Warning: feed version has changed: found %s, expected 1.0' % fm.header.gtfs_realtime_version

                    print 'Adding %s vehicle_positions' % len(fm.entity)
                    for entity in fm.entity:
                        vp = entity.vehicle
                        dbvp = VehiclePosition(
                            trip_id = vp.trip.trip_id,
                            route_id = vp.trip.route_id,
                            trip_start_time = vp.trip.start_time,
                            trip_start_date = vp.trip.start_date,
                            vehicle_id = vp.vehicle.id,
                            vehicle_label = vp.vehicle.label,
                            vehicle_license_plate = vp.vehicle.license_plate,
                            position_latitude = vp.position.latitude,
                            position_longitude = vp.position.longitude,
                            position_bearing = vp.position.bearing,
                            position_speed = vp.position.speed,
                            timestamp = timestamp)

                        session.add(dbvp)

                # This does deletes and adds, since it's atomic it never leaves us
                # without data
                session.commit()
            except:
            #else:
                print 'Exception occurred in iteration'
                print sys.exc_info()
                success = False

            # put this outside the try...except so it won't be skipped when something
            # fails
            # also, makes it easier to end the process with ctrl-c, b/c a
            # KeyboardInterrupt here will end the program (cleanly)
            if opts.once and success:
                print "Executed the load ONCE ... going to stop now..."
                keep_running = False
            else:
                time.sleep(opts.timeout)
    finally:
        print "Closing session . . ."
        session.close()

if __name__ == "__main__":
    main()

<commit_msg>more script fixup<commit_after>#!/usr/bin/python

# gtfsrdb.py: load gtfs-realtime data to a database
# recommended to have the (static) GTFS data for the agency you are connecting
# to already loaded.

# Copyright 2011, 2013 Matt Conway

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#   http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Authors:
# Matt Conway: main code

'''
  https://github.com/mattwigway/gtfsrdb
  http://trimet.org/transweb/ws/V1/FeedSpecAlerts/appId/3819A6A38C72223198B560DF0/includeFuture/true

  bin/python ott/controller/database/gtfsrdb/gtfsrdb.py -d sqlite:///gtfsdb.db -a http://trimet.org/transweb/ws/V1/FeedSpecAlerts/appId/3819A6A38C72223198B560DF0/includeFuture/true -o -w 300

'''

from optparse import OptionParser
import time
import sys
import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from urllib2 import urlopen
from . import gtfs_realtime_pb2
from . import alerts
from . import model
from utils import getTrans
from .model import *

p = OptionParser()
p.add_option('-t', '--trip-updates', dest='tripUpdates', default=None,
             help='The trip updates URL', metavar='URL')

p.add_option('-a', '--alerts', default=None, dest='alerts',
             help='The alerts URL', metavar='URL')

p.add_option('-p', '--vehicle-positions', dest='vehiclePositions', default=None,
             help='The vehicle positions URL', metavar='URL')

p.add_option('-d', '--database', default=None, dest='dsn',
             help='Database connection string', metavar='DSN')

p.add_option('-o', '--discard-old', default=False, dest='deleteOld',
             action='store_true',
             help='Discard old updates, so the database is always current')

p.add_option('-c', '--create-tables', default=False, dest='create',
             action='store_true', help="Create tables if they aren't found")

p.add_option('-w', '--wait', default=30, type='int', metavar='SECS',
             dest='timeout', help='Time to wait between requests (in seconds)')

p.add_option('-v', '--verbose', default=False, dest='verbose',
             action='store_true', help='Print generated SQL')

p.add_option('-l', '--language', default='en', dest='lang', metavar='LANG',
             help='When multiple translations are available, prefer this language')

p.add_option('-s', '--schema',   default=None, dest='schema', help='Database schema')

p.add_option('-1', '--once',   default=None, dest='once', help='only run the loader one time')

opts, args = p.parse_args()

if opts.dsn == None:
    print 'No database specified!'
    exit(1)

if opts.alerts == None and opts.tripUpdates == None and opts.vehiclePositions == None:
    print 'No trip updates, alerts, or vehicle positions URLs were specified!'
    exit(1)

if opts.alerts == None:
    print 'Warning: no alert URL specified, proceeding without alerts'

if opts.tripUpdates == None:
    print 'Warning: no trip update URL specified, proceeding without trip updates'

if opts.vehiclePositions == None:
    print 'Warning: no vehicle positions URL specified, proceeding without vehicle positions'

# add DB schema definitions the table class meta data...
if opts.schema:
    model.add_schema(opts.schema)

# Connect to the database (and implicitly handle any non-default schema)
engine = create_engine(opts.dsn, echo=opts.verbose)

# sessionmaker returns a class
session = sessionmaker(bind=engine)()

def main():
    # Check if it has the tables
    # Base from model.py
    for table in Base.metadata.tables.keys():
        if not engine.has_table(table, opts.schema):
            if opts.create:
                print 'Creating table %s' % table
                Base.metadata.tables[table].create(engine)
            else:
                print 'Missing table %s! Use -c to create it.' % table
                exit(1)

    try:
        keep_running = True
        while keep_running:
            success = True
            try:
            #if True:
                if opts.deleteOld:
                    # Go through all of the tables that we create, clear them
                    # Don't mess with other tables (i.e., tables from static GTFS)
                    for theClass in AllClasses:
                        for obj in session.query(theClass):
                            session.delete(obj)

                if opts.tripUpdates:
                    fm = gtfs_realtime_pb2.FeedMessage()
                    fm.ParseFromString(
                        urlopen(opts.tripUpdates).read()
                        )

                    # Convert this a Python object, and save it to be placed into each
                    # trip_update
                    timestamp = datetime.datetime.utcfromtimestamp(fm.header.timestamp)

                    # Check the feed version
                    if fm.header.gtfs_realtime_version != u'1.0':
                        print 'Warning: feed version has changed: found %s, expected 1.0' % fm.header.gtfs_realtime_version

                    print 'Adding %s trip updates' % len(fm.entity)
                    for entity in fm.entity:
                        tu = entity.trip_update
                        dbtu = TripUpdate(
                            trip_id = tu.trip.trip_id,
                            route_id = tu.trip.route_id,
                            trip_start_time = tu.trip.start_time,
                            trip_start_date = tu.trip.start_date,

                            # get the schedule relationship
                            # This is somewhat undocumented, but by referencing the
                            # DESCRIPTOR.enum_types_by_name, you get a dict of enum types
                            # as described at http://code.google.com/apis/protocolbuffers/docs/reference/python/google.protobuf.descriptor.EnumDescriptor-class.html
                            schedule_relationship = tu.trip.DESCRIPTOR.enum_types_by_name['ScheduleRelationship'].values_by_number[tu.trip.schedule_relationship].name,

                            vehicle_id = tu.vehicle.id,
                            vehicle_label = tu.vehicle.label,
                            vehicle_license_plate = tu.vehicle.license_plate,
                            timestamp = timestamp)

                        for stu in tu.stop_time_update:
                            dbstu = StopTimeUpdate(
                                stop_sequence = stu.stop_sequence,
                                stop_id = stu.stop_id,
                                arrival_delay = stu.arrival.delay,
                                arrival_time = stu.arrival.time,
                                arrival_uncertainty = stu.arrival.uncertainty,
                                departure_delay = stu.departure.delay,
                                departure_time = stu.departure.time,
                                departure_uncertainty = stu.departure.uncertainty,
                                schedule_relationship = tu.trip.DESCRIPTOR.enum_types_by_name['ScheduleRelationship'].values_by_number[tu.trip.schedule_relationship].name
                            )
                            session.add(dbstu)
                            dbtu.StopTimeUpdates.append(dbstu)

                        session.add(dbtu)

                if opts.alerts:
                    alerts.make_alert(session, gtfs_realtime_pb2, opts)

                if opts.vehiclePositions:
                    fm = gtfs_realtime_pb2.FeedMessage()
                    fm.ParseFromString(
                        urlopen(opts.vehiclePositions).read()
                        )

                    # Convert this a Python object, and save it to be placed into each
                    # vehicle_position
                    timestamp = datetime.datetime.utcfromtimestamp(fm.header.timestamp)

                    # Check the feed version
                    if fm.header.gtfs_realtime_version != u'1.0':
                        print 'Warning: feed version has changed: found %s, expected 1.0' % fm.header.gtfs_realtime_version

                    print 'Adding %s vehicle_positions' % len(fm.entity)
                    for entity in fm.entity:
                        vp = entity.vehicle
                        dbvp = VehiclePosition(
                            trip_id = vp.trip.trip_id,
                            route_id = vp.trip.route_id,
                            trip_start_time = vp.trip.start_time,
                            trip_start_date = vp.trip.start_date,
                            vehicle_id = vp.vehicle.id,
                            vehicle_label = vp.vehicle.label,
                            vehicle_license_plate = vp.vehicle.license_plate,
                            position_latitude = vp.position.latitude,
                            position_longitude = vp.position.longitude,
                            position_bearing = vp.position.bearing,
                            position_speed = vp.position.speed,
                            timestamp = timestamp)

                        session.add(dbvp)

                # This does deletes and adds, since it's atomic it never leaves us
                # without data
                session.commit()
            except:
            #else:
                print 'Exception occurred in iteration'
                print sys.exc_info()
                success = False

            # put this outside the try...except so it won't be skipped when something
            # fails
            # also, makes it easier to end the process with ctrl-c, b/c a
            # KeyboardInterrupt here will end the program (cleanly)
            if opts.once and success:
                print "Executed the load ONCE ... going to stop now..."
                keep_running = False
            else:
                time.sleep(opts.timeout)
    finally:
        print "Closing session . . ."
        session.close()

if __name__ == "__main__":
    main()

<|endoftext|>


<commit_before>from django.contrib import admin

import datetime

from mptt.admin import MPTTModelAdmin
from fsm_admin.mixins import FSMTransitionMixin

from .inlines import (
    ArrangerInline,
    AwardInline,
    CertificationInline,
    ContestantInline,
    SessionInline,
    PerformerInline,
    PerformerStackedInline,
    DirectorInline,
    GroupInline,
    JudgeInline,
    PerformanceInline,
    ScoreInline,
    RoundInline,
    RankingInline,
    SingerInline,
    SongStackedInline,
    OrganizationInline,
)

from .models import (
    Arranger,
    Award,
    Catalog,
    Chapter,
    Contestant,
    Session,
    Performer,
    Convention,
    Group,
    Judge,
    Organization,
    Performance,
    Person,
    Score,
    Round,
    Song,
    Tune,
    User,
)

import arrow

# from grappelli.forms import GrappelliSortableHiddenMixin
from super_inlines.admin import SuperModelAdmin

@admin.register(Arranger)
class ArrangerAdmin(admin.ModelAdmin):
    list_display = [
        'name',
        'catalog',
        'person',
    ]

@admin.register(Award)
class AwardAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    list_display = [
        'name',
        'organization',
        'get_long_name',
        'get_level',
        'kind',
        'long_name',
        'stix_name',
    ]

    list_filter = [
        'status',
        'kind',
        'organization',
    ]

    def get_long_name(self, obj):
        return obj.organization.long_name
    get_long_name.short_description = 'Org Name'
    get_long_name.admin_order_field = 'organization__long_name'

    def get_level(self, obj):
        return obj.organization.get_kind_display()
    get_level.short_description = 'Org Level'
    get_level.admin_order_field = 'organization__level'

@admin.register(Catalog)
class CatalogAdmin(admin.ModelAdmin):
    list_display = [
        'song_name',
        'tune',
        'bhs_id',
        'bhs_songname',
        'bhs_arranger',
    ]

@admin.register(Chapter)
class ChapterAdmin(admin.ModelAdmin):
    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'organization',
        'code',
        # 'status',
        # 'status_monitor',
        # 'location',
        # 'website',
        # 'facebook',
        # 'twitter',
        # 'email',
        # 'phone',
        # 'picture',
    )

    fields = (
        'name',
        # ('status', 'status_monitor',),
        'organization',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        'code',
        'notes',
    )

    inlines = [
        GroupInline,
    ]

    readonly_fields = (
        # 'status_monitor',
    )
    save_on_top = True

@admin.register(Contestant)
class ContestantAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    search_fields = [
        'name',
    ]
    fields = [
        'name',
        ('status', 'status_monitor',),
        'performer',
        'place',
        'total_score',
    ]
    list_filter = (
        'status',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'performer',
        ]
    }
    readonly_fields = [
        'name',
        'status_monitor',
        'place',
        'total_score',
    ]

    raw_id_fields = [
        'performer',
    ]

@admin.register(Convention)
class ConventionAdmin(FSMTransitionMixin, admin.ModelAdmin):

    def human_range(self, obj):
        if obj.date:
            if obj.date.upper - obj.date.lower == datetime.timedelta(1):
                dates = "{0}".format(
                    arrow.get(obj.date.lower).format('MMMM D, YYYY'),
                )
            else:
                dates = "{0} - {1}".format(
                    arrow.get(obj.date.lower).format('MMMM D'),
                    arrow.get(obj.date.upper).format('MMMM D, YYYY'),
                )
        else:
            dates = obj.date
        return dates
    human_range.short_description = "Dates22"

    fsm_field = [
        'status',
    ]

    change_list_template = "admin/change_list_filter_sidebar.html"
    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'status',
        'organization',
        'kind',
        'division',
        # 'date',
        'human_range',
        # 'location',
    )

    fields = (
        'name',
        ('status', 'status_monitor',),
        # 'stix_name',
        # 'stix_div',
        ('location', 'timezone',),
        # 'dates',
        'date',
        'organization',
        'drcj',
        'kind',
        # 'stix_file',
        'division',
    )

    list_filter = (
        'status',
        'kind',
        'year',
        'organization__level',
        'division',
        'organization',
    )

    inlines = [
        SessionInline,
        # OrganizationInline,
    ]

    readonly_fields = (
        'name',
        'status_monitor',
        'year',
    )

    raw_id_fields = [
        'drcj',
    ]

    save_on_top = True

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    search_fields = (
        'name',
        'chapter__name',
    )

    list_display = (
        'name',
        'chapter',
        'status',
        'status_monitor',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
    )

    fields = (
        'name',
        ('status', 'status_monitor',),
        'kind',
        'organization',
        'chapter',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        'notes',
    )

    list_filter = (
        'kind',
    )

    inlines = [
        PerformerInline,
    ]

    readonly_fields = (
        'status_monitor',
    )

    raw_id_fields = [
        'chapter',
    ]

    autocomplete_lookup_fields = {
        'fk': [
            'chapter',
        ]
    }

    save_on_top = True

@admin.register(Judge)
class JudgeAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    save_on_top = True
    fields = [
        'name',
        ('status', 'status_monitor',),
        'session',
        'person',
        'organization',
        ('category', 'slot',),
    ]

    list_display = [
        'name',
        'status',
        'person',
        'organization',
    ]

    list_filter = (
        'status',
    )

    list_select_related = [
        'organization',
        'session',
        'person',
    ]

    raw_id_fields = (
        'session',
        'person',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'session',
            'person',
        ]
    }

    readonly_fields = [
        'name',
    ]

    inlines = [
        ScoreInline,
    ]

@admin.register(Organization)
class OrganizationAdmin(MPTTModelAdmin):
    fields = [
        'name',
        ('status', 'status_monitor',),
        'parent',
        'kind',
        'short_name',
        'long_name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        'notes',
    ]

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

    readonly_fields = [
        'status_monitor',
    ]

    list_display = [
        'name',
        'short_name',
        'long_name',
        'status',
        'kind',
    ]

    inlines = [
        AwardInline,
    ]

    # ordering = [
    #     'tree_id',
    # ]

@admin.register(Performance)
class PerformanceAdmin(FSMTransitionMixin, SuperModelAdmin):
    fsm_field = [
        'status',
    ]

    save_on_top = True
    change_list_template = "admin/change_list_filter_sidebar.html"

    inlines = [
        SongStackedInline,
        # RoundInline,
    ]
    list_display = [
        'name',
        'status',
        'draw',
        'total_score',
    ]
    list_filter = [
        'status',
        'round',
    ]

    fields = [
        'name',
        ('status', 'status_monitor',),
        'performer',
        ('draw', 'scheduled'),
        ('mus_points', 'prs_points', 'sng_points', 'total_points',),
        ('mus_score', 'prs_score', 'sng_score', 'total_score',),
    ]

    readonly_fields = [
        'name',
        'status_monitor',
        'mus_points',
        'prs_points',
        'sng_points',
        'total_points',
        'mus_score',
        'prs_score',
        'sng_score',
        'total_score',
        'draw',
    ]

    raw_id_fields = (
        'performer',
    )
    autocomplete_lookup_fields = {
        'fk': [
            'performer',
        ]
    }

    search_fields = (
        'name',
    )

@admin.register(Performer)
class PerformerAdmin(FSMTransitionMixin, admin.ModelAdmin):
    fsm_field = [
        'status',
    ]

    change_list_template = "admin/change_list_filter_sidebar.html"

    inlines = [
        SingerInline,
        DirectorInline,
        PerformanceInline,
        ContestantInline,
    ]

    list_display = (
        'name',
        'status',
        'seed',
        'prelim',
        # 'mus_score',
        # 'prs_score',
        # 'sng_score',
        # 'total_score',
        'men',
    )

    search_fields = (
        'name',
    )

    list_filter = (
        'status',
    )

    raw_id_fields = (
        'session',
        'group',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'session',
            'group',
        ]
    }
    fields = (
        'name',
        ('status', 'status_monitor',),
        'session',
        ('group', 'organization',),
        ('seed', 'prelim',),
        ('men',),
        # ('mus_points', 'prs_points', 'sng_points', 'total_points',),
        # ('mus_score', 'prs_score', 'sng_score', 'total_score',),
    )

    readonly_fields = (
        'organization',
        'name',
        'status_monitor',
        'mus_points',
        'prs_points',
        'sng_points',
        'total_points',
        'mus_score',
        'prs_score',
        'sng_score',
        'total_score',
    )

    save_on_top = True

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    search_fields = (
        'name',
    )

    save_on_top = True
    list_display = (
        'name',
        'status',
        'status_monitor',
        'member',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'bhs_member_id',
        'bhs_name',
        'bhs_city',
        'bhs_state',
        'bhs_phone',
        'bhs_email',
    )

    fields = (
        'name',
        ('status', 'status_monitor',),
        'organization',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'member',
        'picture',
        'description',
        'bhs_city',
        'notes',
    )

    list_filter = [
        'status',
    ]

    readonly_fields = (
        'status_monitor',
    )
    inlines = [
        DirectorInline,
        SingerInline,
        ArrangerInline,
        CertificationInline,
    ]

@admin.register(Round)
class RoundAdmin(FSMTransitionMixin, admin.ModelAdmin):
    fsm_field = [
        'status',
    ]

    save_on_top = True
    change_list_template = "admin/change_list_filter_sidebar.html"
    list_display = [
        'name',
        'status',
        'slots',
    ]
    fields = [
        'name',
        ('status', 'status_monitor',),
        ('session', 'kind',),
        ('slots',),
    ]

    readonly_fields = [
        'name',
        'status_monitor',
        'session',
        'kind',
    ]

    list_filter = (
        'status',
    )

    raw_id_fields = (
        'session',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'session',
        ]
    }

    inlines = [
        PerformanceInline,
    ]

    search_fields = [
        'name',
    ]

@admin.register(Score)
class ScoreAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    save_on_top = True
    fields = [
        'name',
        # ('status', 'status_monitor',),
        'song',
        'judge',
        'points',
    ]

    readonly_fields = [
        'name',
        # 'status_monitor',
        'song',
        'judge',
    ]

    list_display = [
        'name',
        # 'status',
        'points',
    ]

    # list_filter = [
    #     'status',
    # ]

    raw_id_fields = [
        'song',
        'judge',
    ]

    autocomplete_lookup_fields = {
        'fk': [
            'song',
            'judge',
        ]
    }

    ordering = [
        'song',
        'judge',
    ]

@admin.register(Session)
class SessionAdmin(FSMTransitionMixin, SuperModelAdmin):
    fsm_field = [
        'status',
    ]

    change_list_template = "admin/change_list_filter_sidebar.html"
    save_on_top = True
    fields = [
        'name',
        ('status', 'status_monitor',),
        ('history', 'history_monitor',),
        'convention',
        'kind',
        'administrator',
        # 'organization',
        # 'year',
        # # 'size',
        # 'num_rounds',
    ]

    list_display = [
        'name',
        'status',
        'convention',
        'kind',
        'administrator',
        # 'size',
        # 'num_rounds',
    ]

    list_filter = (
        'status',
        'history',
        'kind',
        'convention__year',
        'organization',
        'administrator',
    )

    raw_id_fields = (
        'convention',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'convention',
        ]
    }

    readonly_fields = [
        'name',
        'status_monitor',
        'history_monitor',
        'organization',
    ]

    inlines = [
        RoundInline,
        PerformerInline,
        JudgeInline,
    ]

@admin.register(Song)
class SongAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    save_on_top = True
    list_display = (
        'name',
        # 'status',
        'title',
        # 'tune',
        'mus_points',
        'prs_points',
        'sng_points',
        'total_points'
    )

    search_fields = (
        'name',
        'title',
    )

    fields = [
        'name',
        # ('status', 'status_monitor',),
        'order',
        ('mus_points', 'prs_points', 'sng_points', 'total_points',),
        ('mus_score', 'prs_score', 'sng_score', 'total_score',),
        'title',
        # 'tune',
        # 'catalog',
    ]

    inlines = [
        # ArrangersInline,
        ScoreInline,
    ]

    # list_filter = (
    #     'status',
    # )

    readonly_fields = (
        'name',
        'order',
        'mus_points',
        'prs_points',
        'sng_points',
        'total_points',
        'mus_score',
        'prs_score',
        'sng_score',
        'total_score',
        # 'status_monitor',
    )
    raw_id_fields = (
        'performance',
        # 'tune',
        # 'catalog',
    )
    autocomplete_lookup_fields = {
        'fk': [
            'performance',
            # 'tune',
        ]
    }

    ordering = (
        'name',
        'order',
    )

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

    search_fields = (
        'name',
    )

@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    model = User
    save_on_top = True
<commit_msg>Add code to organization<commit_after>from django.contrib import admin

import datetime

from mptt.admin import MPTTModelAdmin
from fsm_admin.mixins import FSMTransitionMixin

from .inlines import (
    ArrangerInline,
    AwardInline,
    CertificationInline,
    ContestantInline,
    SessionInline,
    PerformerInline,
    PerformerStackedInline,
    DirectorInline,
    GroupInline,
    JudgeInline,
    PerformanceInline,
    ScoreInline,
    RoundInline,
    RankingInline,
    SingerInline,
    SongStackedInline,
    OrganizationInline,
)

from .models import (
    Arranger,
    Award,
    Catalog,
    Chapter,
    Contestant,
    Session,
    Performer,
    Convention,
    Group,
    Judge,
    Organization,
    Performance,
    Person,
    Score,
    Round,
    Song,
    Tune,
    User,
)

import arrow

# from grappelli.forms import GrappelliSortableHiddenMixin
from super_inlines.admin import SuperModelAdmin

@admin.register(Arranger)
class ArrangerAdmin(admin.ModelAdmin):
    list_display = [
        'name',
        'catalog',
        'person',
    ]

@admin.register(Award)
class AwardAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    list_display = [
        'name',
        'organization',
        'get_long_name',
        'get_level',
        'kind',
        'long_name',
        'stix_name',
    ]

    list_filter = [
        'status',
        'kind',
        'organization',
    ]

    def get_long_name(self, obj):
        return obj.organization.long_name
    get_long_name.short_description = 'Org Name'
    get_long_name.admin_order_field = 'organization__long_name'

    def get_level(self, obj):
        return obj.organization.get_kind_display()
    get_level.short_description = 'Org Level'
    get_level.admin_order_field = 'organization__level'

@admin.register(Catalog)
class CatalogAdmin(admin.ModelAdmin):
    list_display = [
        'song_name',
        'tune',
        'bhs_id',
        'bhs_songname',
        'bhs_arranger',
    ]

@admin.register(Chapter)
class ChapterAdmin(admin.ModelAdmin):
    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'organization',
        'code',
        # 'status',
        # 'status_monitor',
        # 'location',
        # 'website',
        # 'facebook',
        # 'twitter',
        # 'email',
        # 'phone',
        # 'picture',
    )

    fields = (
        'name',
        # ('status', 'status_monitor',),
        'organization',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        'code',
        'notes',
    )

    inlines = [
        GroupInline,
    ]

    readonly_fields = (
        # 'status_monitor',
    )
    save_on_top = True

@admin.register(Contestant)
class ContestantAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    search_fields = [
        'name',
    ]
    fields = [
        'name',
        ('status', 'status_monitor',),
        'performer',
        'place',
        'total_score',
    ]
    list_filter = (
        'status',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'performer',
        ]
    }
    readonly_fields = [
        'name',
        'status_monitor',
        'place',
        'total_score',
    ]

    raw_id_fields = [
        'performer',
    ]

@admin.register(Convention)
class ConventionAdmin(FSMTransitionMixin, admin.ModelAdmin):

    def human_range(self, obj):
        if obj.date:
            if obj.date.upper - obj.date.lower == datetime.timedelta(1):
                dates = "{0}".format(
                    arrow.get(obj.date.lower).format('MMMM D, YYYY'),
                )
            else:
                dates = "{0} - {1}".format(
                    arrow.get(obj.date.lower).format('MMMM D'),
                    arrow.get(obj.date.upper).format('MMMM D, YYYY'),
                )
        else:
            dates = obj.date
        return dates
    human_range.short_description = "Dates22"

    fsm_field = [
        'status',
    ]

    change_list_template = "admin/change_list_filter_sidebar.html"
    search_fields = (
        'name',
    )

    list_display = (
        'name',
        'status',
        'organization',
        'kind',
        'division',
        # 'date',
        'human_range',
        # 'location',
    )

    fields = (
        'name',
        ('status', 'status_monitor',),
        # 'stix_name',
        # 'stix_div',
        ('location', 'timezone',),
        # 'dates',
        'date',
        'organization',
        'drcj',
        'kind',
        # 'stix_file',
        'division',
    )

    list_filter = (
        'status',
        'kind',
        'year',
        'organization__level',
        'division',
        'organization',
    )

    inlines = [
        SessionInline,
        # OrganizationInline,
    ]

    readonly_fields = (
        'name',
        'status_monitor',
        'year',
    )

    raw_id_fields = [
        'drcj',
    ]

    save_on_top = True

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    search_fields = (
        'name',
        'chapter__name',
    )

    list_display = (
        'name',
        'chapter',
        'status',
        'status_monitor',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
    )

    fields = (
        'name',
        ('status', 'status_monitor',),
        'kind',
        'organization',
        'chapter',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        'notes',
    )

    list_filter = (
        'kind',
    )

    inlines = [
        PerformerInline,
    ]

    readonly_fields = (
        'status_monitor',
    )

    raw_id_fields = [
        'chapter',
    ]

    autocomplete_lookup_fields = {
        'fk': [
            'chapter',
        ]
    }

    save_on_top = True

@admin.register(Judge)
class JudgeAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    save_on_top = True
    fields = [
        'name',
        ('status', 'status_monitor',),
        'session',
        'person',
        'organization',
        ('category', 'slot',),
    ]

    list_display = [
        'name',
        'status',
        'person',
        'organization',
    ]

    list_filter = (
        'status',
    )

    list_select_related = [
        'organization',
        'session',
        'person',
    ]

    raw_id_fields = (
        'session',
        'person',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'session',
            'person',
        ]
    }

    readonly_fields = [
        'name',
    ]

    inlines = [
        ScoreInline,
    ]

@admin.register(Organization)
class OrganizationAdmin(MPTTModelAdmin):
    fields = [
        'name',
        ('status', 'status_monitor',),
        'parent',
        'kind',
        'code',
        'short_name',
        'long_name',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'description',
        'notes',
    ]

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

    readonly_fields = [
        'status_monitor',
    ]

    list_display = [
        'name',
        'short_name',
        'long_name',
        'status',
        'kind',
    ]

    inlines = [
        AwardInline,
    ]

    # ordering = [
    #     'tree_id',
    # ]

@admin.register(Performance)
class PerformanceAdmin(FSMTransitionMixin, SuperModelAdmin):
    fsm_field = [
        'status',
    ]

    save_on_top = True
    change_list_template = "admin/change_list_filter_sidebar.html"

    inlines = [
        SongStackedInline,
        # RoundInline,
    ]
    list_display = [
        'name',
        'status',
        'draw',
        'total_score',
    ]
    list_filter = [
        'status',
        'round',
    ]

    fields = [
        'name',
        ('status', 'status_monitor',),
        'performer',
        ('draw', 'scheduled'),
        ('mus_points', 'prs_points', 'sng_points', 'total_points',),
        ('mus_score', 'prs_score', 'sng_score', 'total_score',),
    ]

    readonly_fields = [
        'name',
        'status_monitor',
        'mus_points',
        'prs_points',
        'sng_points',
        'total_points',
        'mus_score',
        'prs_score',
        'sng_score',
        'total_score',
        'draw',
    ]

    raw_id_fields = (
        'performer',
    )
    autocomplete_lookup_fields = {
        'fk': [
            'performer',
        ]
    }

    search_fields = (
        'name',
    )

@admin.register(Performer)
class PerformerAdmin(FSMTransitionMixin, admin.ModelAdmin):
    fsm_field = [
        'status',
    ]

    change_list_template = "admin/change_list_filter_sidebar.html"

    inlines = [
        SingerInline,
        DirectorInline,
        PerformanceInline,
        ContestantInline,
    ]

    list_display = (
        'name',
        'status',
        'seed',
        'prelim',
        # 'mus_score',
        # 'prs_score',
        # 'sng_score',
        # 'total_score',
        'men',
    )

    search_fields = (
        'name',
    )

    list_filter = (
        'status',
    )

    raw_id_fields = (
        'session',
        'group',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'session',
            'group',
        ]
    }
    fields = (
        'name',
        ('status', 'status_monitor',),
        'session',
        ('group', 'organization',),
        ('seed', 'prelim',),
        ('men',),
        # ('mus_points', 'prs_points', 'sng_points', 'total_points',),
        # ('mus_score', 'prs_score', 'sng_score', 'total_score',),
    )

    readonly_fields = (
        'organization',
        'name',
        'status_monitor',
        'mus_points',
        'prs_points',
        'sng_points',
        'total_points',
        'mus_score',
        'prs_score',
        'sng_score',
        'total_score',
    )

    save_on_top = True

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    search_fields = (
        'name',
    )

    save_on_top = True
    list_display = (
        'name',
        'status',
        'status_monitor',
        'member',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'picture',
        'bhs_member_id',
        'bhs_name',
        'bhs_city',
        'bhs_state',
        'bhs_phone',
        'bhs_email',
    )

    fields = (
        'name',
        ('status', 'status_monitor',),
        'organization',
        'location',
        'website',
        'facebook',
        'twitter',
        'email',
        'phone',
        'member',
        'picture',
        'description',
        'bhs_city',
        'notes',
    )

    list_filter = [
        'status',
    ]

    readonly_fields = (
        'status_monitor',
    )
    inlines = [
        DirectorInline,
        SingerInline,
        ArrangerInline,
        CertificationInline,
    ]

@admin.register(Round)
class RoundAdmin(FSMTransitionMixin, admin.ModelAdmin):
    fsm_field = [
        'status',
    ]

    save_on_top = True
    change_list_template = "admin/change_list_filter_sidebar.html"
    list_display = [
        'name',
        'status',
        'slots',
    ]
    fields = [
        'name',
        ('status', 'status_monitor',),
        ('session', 'kind',),
        ('slots',),
    ]

    readonly_fields = [
        'name',
        'status_monitor',
        'session',
        'kind',
    ]

    list_filter = (
        'status',
    )

    raw_id_fields = (
        'session',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'session',
        ]
    }

    inlines = [
        PerformanceInline,
    ]

    search_fields = [
        'name',
    ]

@admin.register(Score)
class ScoreAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    save_on_top = True
    fields = [
        'name',
        # ('status', 'status_monitor',),
        'song',
        'judge',
        'points',
    ]

    readonly_fields = [
        'name',
        # 'status_monitor',
        'song',
        'judge',
    ]

    list_display = [
        'name',
        # 'status',
        'points',
    ]

    # list_filter = [
    #     'status',
    # ]

    raw_id_fields = [
        'song',
        'judge',
    ]

    autocomplete_lookup_fields = {
        'fk': [
            'song',
            'judge',
        ]
    }

    ordering = [
        'song',
        'judge',
    ]

@admin.register(Session)
class SessionAdmin(FSMTransitionMixin, SuperModelAdmin):
    fsm_field = [
        'status',
    ]

    change_list_template = "admin/change_list_filter_sidebar.html"
    save_on_top = True
    fields = [
        'name',
        ('status', 'status_monitor',),
        ('history', 'history_monitor',),
        'convention',
        'kind',
        'administrator',
        # 'organization',
        # 'year',
        # # 'size',
        # 'num_rounds',
    ]

    list_display = [
        'name',
        'status',
        'convention',
        'kind',
        'administrator',
        # 'size',
        # 'num_rounds',
    ]

    list_filter = (
        'status',
        'history',
        'kind',
        'convention__year',
        'organization',
        'administrator',
    )

    raw_id_fields = (
        'convention',
    )

    autocomplete_lookup_fields = {
        'fk': [
            'convention',
        ]
    }

    readonly_fields = [
        'name',
        'status_monitor',
        'history_monitor',
        'organization',
    ]

    inlines = [
        RoundInline,
        PerformerInline,
        JudgeInline,
    ]

@admin.register(Song)
class SongAdmin(admin.ModelAdmin):
    change_list_template = "admin/change_list_filter_sidebar.html"
    save_on_top = True
    list_display = (
        'name',
        # 'status',
        'title',
        # 'tune',
        'mus_points',
        'prs_points',
        'sng_points',
        'total_points'
    )

    search_fields = (
        'name',
        'title',
    )

    fields = [
        'name',
        # ('status', 'status_monitor',),
        'order',
        ('mus_points', 'prs_points', 'sng_points', 'total_points',),
        ('mus_score', 'prs_score', 'sng_score', 'total_score',),
        'title',
        # 'tune',
        # 'catalog',
    ]

    inlines = [
        # ArrangersInline,
        ScoreInline,
    ]

    # list_filter = (
    #     'status',
    # )

    readonly_fields = (
        'name',
        'order',
        'mus_points',
        'prs_points',
        'sng_points',
        'total_points',
        'mus_score',
        'prs_score',
        'sng_score',
        'total_score',
        # 'status_monitor',
    )
    raw_id_fields = (
        'performance',
        # 'tune',
        # 'catalog',
    )
    autocomplete_lookup_fields = {
        'fk': [
            'performance',
            # 'tune',
        ]
    }

    ordering = (
        'name',
        'order',
    )

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

    search_fields = (
        'name',
    )

@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    model = User
    save_on_top = True
<|endoftext|>