<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/>.

import getpass
import os
import StringIO
import subprocess
import sys
import unittest

from django.core import exceptions

from openquake import engine2
from openquake.db import models

from tests.utils import helpers

class PrepareJobTestCase(unittest.TestCase):

    def test_prepare_job_default_user(self):
        job = engine2.prepare_job()

        self.assertEqual('openquake', job.owner.user_name)
        self.assertEqual('pending', job.status)

        # Check the make sure it's in the database.
        try:
            models.OqJob.objects.get(id=job.id)
        except exceptions.ObjectDoesNotExist:
            self.fail('Job was not found in the database')

    def test_prepare_job_specified_user(self):
        user_name = helpers.random_string()
        job = engine2.prepare_job(user_name=user_name)

        self.assertEqual(user_name, job.owner.user_name)
        self.assertEqual('pending', job.status)

        try:
            models.OqJob.objects.get(id=job.id)
        except exceptions.ObjectDoesNotExist:
            self.fail('Job was not found in the database')

class PrepareUserTestCase(unittest.TestCase):

    def test_prepare_user_exists(self):
        user_name = helpers.random_string()
        existing_user = models.OqUser(
            user_name=user_name, full_name=user_name, organization_id=1
        )
        existing_user.save()

        user = engine2.prepare_user(user_name)
        self.assertEqual(existing_user.id, user.id)

    def test_prepare_user_does_not_exist(self):
        user_name = helpers.random_string()

        # Sanity check; make sure the user doesn't exist yet.
        self.assertEqual(
            0, len(models.OqUser.objects.filter(user_name=user_name))
        )

        user = engine2.prepare_user(user_name)

        # Now the user should exist.
        self.assertEqual(
            1, len(models.OqUser.objects.filter(user_name=user_name))
        )

class ParseConfigTestCase(unittest.TestCase):

    def test_parse_config_no_files(self):
        # sections are there just for documentation
        # when we parse the file, we ignore these
        source = StringIO.StringIO("""
[general]
CALCULATION_MODE = classical
region = 1 1 2 2 3 3
[foo]
bar = baz
""")
        expected_params = {
            'calculation_mode': 'classical',
            'region': '1 1 2 2 3 3',
            'bar': 'baz',
        }

        params, _ = engine2.parse_config(source)

        self.assertEqual(expected_params, params)

    def test_parse_config_with_files_force_inputs(self):
        site_model_input = helpers.touch(content="foo")

        source = StringIO.StringIO("""
[general]
calculation_mode = classical
[site]
site_model_file = %s
not_a_valid_file = foo.xml
""" % site_model_input)

        expected_params = {
            'calculation_mode': 'classical',
            'not_a_valid_file': 'foo.xml',
        }

        params, files = engine2.parse_config(source, force_inputs=True)

        expected_files = {
            'site_model_file': models.Input.objects.latest('id'),
        }

        self.assertEqual(expected_params, params)

        self.assertEqual(len(expected_files), len(files))
        for key in expected_files:
            self.assertEqual(expected_files[key].id, files[key].id)

    def test_parse_config_with_files_no_force_inputs(self):
        site_model_input = helpers.touch(content="foo")

        source = StringIO.StringIO("""
[general]
calculation_mode = classical
[site]
site_model_file = %s
not_a_valid_file = foo.xml
""" % site_model_input)

        expected_params = {
            'calculation_mode': 'classical',
            'not_a_valid_file': 'foo.xml',
        }

        # Run first with force_inputs=True to create the new Input.
        _, expected_files = engine2.parse_config(source, force_inputs=True)

        # In order for us to reuse the existing input, we need to associate
        # each input with a successful job.
        job = engine2.prepare_job(getpass.getuser())
        job.status = 'succeeded'
        job.save()
        for inp in expected_files.values():
            i2j = models.Input2job(input=inp, oq_job=job)
            i2j.save()

        # Now run twice with force_inputs=False (the default).
        source.seek(0)
        params1, files1 = engine2.parse_config(source)
        source.seek(0)
        params2, files2 = engine2.parse_config(source)

        # Check the params just for sanity.
        self.assertEqual(expected_params, params1)
        self.assertEqual(expected_params, params2)

        # Finally, check that the Input returned by the latest 2 calls matches
        # the input we created above.
        self.assertEqual(len(expected_files), len(files1))
        self.assertEqual(len(expected_files), len(files2))

        for key in expected_files:
            self.assertEqual(expected_files[key].id, files1[key].id)
            self.assertEqual(expected_files[key].id, files2[key].id)

class FileDigestTestCase(unittest.TestCase):
    """Test the _file_digest() function."""

    PATH = helpers.get_data_path("src_model1.dat")

    def test__file_digest(self):
        # Make sure the digest returned by the function matches the one
        # obtained via /usr/bin/md5sum
        if sys.platform == 'darwin':
            expected = subprocess.check_output(["md5", self.PATH]).split()[-1]
        else:
            expected = subprocess.check_output(
                ["md5sum", self.PATH]).split()[0]
        actual = engine2._file_digest(self.PATH)
        self.assertEqual(expected, actual)

class GetContentTypeTestCase(unittest.TestCase):

    def test__get_content_type(self):
        # no file extension
        self.assertEqual('unknown', engine2._get_content_type('/foo/bar/baz'))
        # xml file extension
        self.assertEqual('xml', engine2._get_content_type('/foo/bar/baz.xml'))
        # hdf5 file extension
        self.assertEqual(
            'HDF5', engine2._get_content_type('/foo/bar/baz.HDF5')
        )

class IdenticalInputTestCase(unittest.TestCase, helpers.DbTestCase):
    """Test the _identical_input() function."""

    EXPOM = helpers.get_data_path("exposure.xml")
    FRAGM = os.path.join(helpers.SCHEMA_DIR, "examples/fragm_d.xml")

    # some jobs (first succeeded) ran by user "u2"
    jobs_u2 = []
    # some jobs (first failed) ran by user "u1"
    failed_jobs_u1 = []
    # some jobs (first succeeded) ran by user "u1"
    jobs_u1 = []
    # "New" job for user "u1", _identical_input() will be run for it.
    job = None
    # Fragility model md5sum digest
    fragm_digest = None

    @classmethod
    def setUpClass(cls):
        # In 'failed_jobs_u1' we want the first job to have status "failed".
        # In 'jobs_u1' the first job is fine/"succeeded".
        # In 'jobs_u2' the first job is fine but all the jobs are owned by a
        # different user "u2".
        jdata = (("u1", cls.failed_jobs_u1, 0),
                 ("u1", cls.jobs_u1, 1), ("u2", cls.jobs_u2, 2))
        for ji, (user_name, jl, fidx) in enumerate(jdata):
            num_jobs = ji + 2
            for jj in range(num_jobs):
                job = cls.setup_classic_job(omit_profile=True,
                                            user_name=user_name)
                job.status = "failed" if jj == fidx else "succeeded"
                job.save()
                jl.append(job)
        cls.job = cls.setup_classic_job(user_name="u1")
        cls.job.status = "succeeded"
        cls.job.save()
        if sys.platform == 'darwin':
            cls.fragm_digest = subprocess.check_output(
                ["md5", cls.FRAGM]).split()[-1]
        else:
            cls.fragm_digest = subprocess.check_output(
                ["md5sum", cls.FRAGM]).split()[0]

    @classmethod
    def tearDownClass(cls):
        cls.teardown_job(cls.job)

    def _setup_input(self, input_type, size, path, digest, jobs):
        """Create a model input and associate it with the given jobs.

        Its owner will be the same as the owner of the first job.
        """
        # In order for the tests in this class to work we need to disable any
        # other model inputs that might still be in the database.
        models.Input2job.objects.all().delete()

        mdl = models.Input(input_type=input_type, size=size, path=path,
                           owner=jobs[0].owner, digest=digest)
        mdl.save()
        for job in jobs:
            i2j = models.Input2job(input=mdl, oq_job=job)
            i2j.save()
        return mdl

    def test__identical_input(self):
        # The matching fragility model input is found
        expected = self._setup_input(
            input_type="fragility", size=123, path=self.FRAGM,
            digest=self.fragm_digest, jobs=self.jobs_u1)
        actual = engine2._identical_input("fragility", self.fragm_digest,
                                         self.job.owner.id)
        self.assertEqual(expected.id, actual.id)

    def test__identical_input_and_non_matching_digest(self):
        # The exposure model input is not found since the md5sum digest does
        # not match.
        self._setup_input(
            input_type="exposure", size=123, path=self.EXPOM, digest="0" * 32,
            jobs=self.jobs_u1)
        actual = engine2._identical_input("exposure", "x" * 32,
                                         self.job.owner.id)
        self.assertIs(None, actual)

    def test__identical_input_and_failed_first_job(self):
        # The exposure model input is not found since the first job to have
        # used it has failed.
        self._setup_input(
            input_type="exposure", size=123, path=self.EXPOM, digest="0" * 32,
            jobs=self.failed_jobs_u1)
        actual = engine2._identical_input("exposure", "0" * 32,
                                         self.job.owner.id)
        self.assertIs(None, actual)

    def test__identical_input_and_owner_differing_from_user(self):
        # The exposure model input is not found since its owner is not the user
        # that is running the current job.
        self._setup_input(
            input_type="exposure", size=123, path=self.EXPOM, digest="0" * 32,
            jobs=self.jobs_u2)
        actual = engine2._identical_input("exposure", "0" * 32,
                                         self.job.owner.id)
        self.assertIs(None, actual)

class CreateHazardJobProfileTestCase(unittest.TestCase):

    def test_create_hazard_job_profile(self):
        # Just the bare minimum set of params to satisfy not null constraints
        # in the db.
        params = {
            'calculation_mode': 'classical',
            'region': '1 1 2 2 3 3',
            'width_of_mfd_bin': '1',
            'rupture_mesh_spacing': '1',
            'area_source_discretization': '2',
            'investigation_time': 50,
            'truncation_level': 0,
            'maximum_distance': 200,
        }

        owner = helpers.default_user()
        hjp = engine2.create_hazard_job_profile(params, owner)
        # Normalize/clean fields by fetching a fresh copy from the db.
        hjp = models.HazardJobProfile.objects.get(id=hjp.id)

        self.assertEqual(hjp.calculation_mode, 'classical')
        self.assertEqual(hjp.width_of_mfd_bin, 1.0)
        self.assertEqual(hjp.rupture_mesh_spacing, 1.0)
        self.assertEqual(hjp.area_source_discretization, 2.0)
        self.assertEqual(hjp.investigation_time, 50.0)
        self.assertEqual(hjp.truncation_level, 0.0)
        self.assertEqual(hjp.maximum_distance, 200.0)
<commit_msg>Added an integration test for loading and validating a job profile (read from a config file)<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/>.

import getpass
import os
import StringIO
import subprocess
import sys
import unittest

from django.core import exceptions

from openquake import engine2
from openquake.db import models
from openquake.job import validation

from tests.utils import helpers

class PrepareJobTestCase(unittest.TestCase):

    def test_prepare_job_default_user(self):
        job = engine2.prepare_job()

        self.assertEqual('openquake', job.owner.user_name)
        self.assertEqual('pending', job.status)

        # Check the make sure it's in the database.
        try:
            models.OqJob.objects.get(id=job.id)
        except exceptions.ObjectDoesNotExist:
            self.fail('Job was not found in the database')

    def test_prepare_job_specified_user(self):
        user_name = helpers.random_string()
        job = engine2.prepare_job(user_name=user_name)

        self.assertEqual(user_name, job.owner.user_name)
        self.assertEqual('pending', job.status)

        try:
            models.OqJob.objects.get(id=job.id)
        except exceptions.ObjectDoesNotExist:
            self.fail('Job was not found in the database')

class PrepareUserTestCase(unittest.TestCase):

    def test_prepare_user_exists(self):
        user_name = helpers.random_string()
        existing_user = models.OqUser(
            user_name=user_name, full_name=user_name, organization_id=1
        )
        existing_user.save()

        user = engine2.prepare_user(user_name)
        self.assertEqual(existing_user.id, user.id)

    def test_prepare_user_does_not_exist(self):
        user_name = helpers.random_string()

        # Sanity check; make sure the user doesn't exist yet.
        self.assertEqual(
            0, len(models.OqUser.objects.filter(user_name=user_name))
        )

        user = engine2.prepare_user(user_name)

        # Now the user should exist.
        self.assertEqual(
            1, len(models.OqUser.objects.filter(user_name=user_name))
        )

class ParseConfigTestCase(unittest.TestCase):

    def test_parse_config_no_files(self):
        # sections are there just for documentation
        # when we parse the file, we ignore these
        source = StringIO.StringIO("""
[general]
CALCULATION_MODE = classical
region = 1 1 2 2 3 3
[foo]
bar = baz
""")
        expected_params = {
            'calculation_mode': 'classical',
            'region': '1 1 2 2 3 3',
            'bar': 'baz',
        }

        params, _ = engine2.parse_config(source)

        self.assertEqual(expected_params, params)

    def test_parse_config_with_files_force_inputs(self):
        site_model_input = helpers.touch(content="foo")

        source = StringIO.StringIO("""
[general]
calculation_mode = classical
[site]
site_model_file = %s
not_a_valid_file = foo.xml
""" % site_model_input)

        expected_params = {
            'calculation_mode': 'classical',
            'not_a_valid_file': 'foo.xml',
        }

        params, files = engine2.parse_config(source, force_inputs=True)

        expected_files = {
            'site_model_file': models.Input.objects.latest('id'),
        }

        self.assertEqual(expected_params, params)

        self.assertEqual(len(expected_files), len(files))
        for key in expected_files:
            self.assertEqual(expected_files[key].id, files[key].id)

    def test_parse_config_with_files_no_force_inputs(self):
        site_model_input = helpers.touch(content="foo")

        source = StringIO.StringIO("""
[general]
calculation_mode = classical
[site]
site_model_file = %s
not_a_valid_file = foo.xml
""" % site_model_input)

        expected_params = {
            'calculation_mode': 'classical',
            'not_a_valid_file': 'foo.xml',
        }

        # Run first with force_inputs=True to create the new Input.
        _, expected_files = engine2.parse_config(source, force_inputs=True)

        # In order for us to reuse the existing input, we need to associate
        # each input with a successful job.
        job = engine2.prepare_job(getpass.getuser())
        job.status = 'succeeded'
        job.save()
        for inp in expected_files.values():
            i2j = models.Input2job(input=inp, oq_job=job)
            i2j.save()

        # Now run twice with force_inputs=False (the default).
        source.seek(0)
        params1, files1 = engine2.parse_config(source)
        source.seek(0)
        params2, files2 = engine2.parse_config(source)

        # Check the params just for sanity.
        self.assertEqual(expected_params, params1)
        self.assertEqual(expected_params, params2)

        # Finally, check that the Input returned by the latest 2 calls matches
        # the input we created above.
        self.assertEqual(len(expected_files), len(files1))
        self.assertEqual(len(expected_files), len(files2))

        for key in expected_files:
            self.assertEqual(expected_files[key].id, files1[key].id)
            self.assertEqual(expected_files[key].id, files2[key].id)

class FileDigestTestCase(unittest.TestCase):
    """Test the _file_digest() function."""

    PATH = helpers.get_data_path("src_model1.dat")

    def test__file_digest(self):
        # Make sure the digest returned by the function matches the one
        # obtained via /usr/bin/md5sum
        if sys.platform == 'darwin':
            expected = subprocess.check_output(["md5", self.PATH]).split()[-1]
        else:
            expected = subprocess.check_output(
                ["md5sum", self.PATH]).split()[0]
        actual = engine2._file_digest(self.PATH)
        self.assertEqual(expected, actual)

class GetContentTypeTestCase(unittest.TestCase):

    def test__get_content_type(self):
        # no file extension
        self.assertEqual('unknown', engine2._get_content_type('/foo/bar/baz'))
        # xml file extension
        self.assertEqual('xml', engine2._get_content_type('/foo/bar/baz.xml'))
        # hdf5 file extension
        self.assertEqual(
            'HDF5', engine2._get_content_type('/foo/bar/baz.HDF5')
        )

class IdenticalInputTestCase(unittest.TestCase, helpers.DbTestCase):
    """Test the _identical_input() function."""

    EXPOM = helpers.get_data_path("exposure.xml")
    FRAGM = os.path.join(helpers.SCHEMA_DIR, "examples/fragm_d.xml")

    # some jobs (first succeeded) ran by user "u2"
    jobs_u2 = []
    # some jobs (first failed) ran by user "u1"
    failed_jobs_u1 = []
    # some jobs (first succeeded) ran by user "u1"
    jobs_u1 = []
    # "New" job for user "u1", _identical_input() will be run for it.
    job = None
    # Fragility model md5sum digest
    fragm_digest = None

    @classmethod
    def setUpClass(cls):
        # In 'failed_jobs_u1' we want the first job to have status "failed".
        # In 'jobs_u1' the first job is fine/"succeeded".
        # In 'jobs_u2' the first job is fine but all the jobs are owned by a
        # different user "u2".
        jdata = (("u1", cls.failed_jobs_u1, 0),
                 ("u1", cls.jobs_u1, 1), ("u2", cls.jobs_u2, 2))
        for ji, (user_name, jl, fidx) in enumerate(jdata):
            num_jobs = ji + 2
            for jj in range(num_jobs):
                job = cls.setup_classic_job(omit_profile=True,
                                            user_name=user_name)
                job.status = "failed" if jj == fidx else "succeeded"
                job.save()
                jl.append(job)
        cls.job = cls.setup_classic_job(user_name="u1")
        cls.job.status = "succeeded"
        cls.job.save()
        if sys.platform == 'darwin':
            cls.fragm_digest = subprocess.check_output(
                ["md5", cls.FRAGM]).split()[-1]
        else:
            cls.fragm_digest = subprocess.check_output(
                ["md5sum", cls.FRAGM]).split()[0]

    @classmethod
    def tearDownClass(cls):
        cls.teardown_job(cls.job)

    def _setup_input(self, input_type, size, path, digest, jobs):
        """Create a model input and associate it with the given jobs.

        Its owner will be the same as the owner of the first job.
        """
        # In order for the tests in this class to work we need to disable any
        # other model inputs that might still be in the database.
        models.Input2job.objects.all().delete()

        mdl = models.Input(input_type=input_type, size=size, path=path,
                           owner=jobs[0].owner, digest=digest)
        mdl.save()
        for job in jobs:
            i2j = models.Input2job(input=mdl, oq_job=job)
            i2j.save()
        return mdl

    def test__identical_input(self):
        # The matching fragility model input is found
        expected = self._setup_input(
            input_type="fragility", size=123, path=self.FRAGM,
            digest=self.fragm_digest, jobs=self.jobs_u1)
        actual = engine2._identical_input("fragility", self.fragm_digest,
                                         self.job.owner.id)
        self.assertEqual(expected.id, actual.id)

    def test__identical_input_and_non_matching_digest(self):
        # The exposure model input is not found since the md5sum digest does
        # not match.
        self._setup_input(
            input_type="exposure", size=123, path=self.EXPOM, digest="0" * 32,
            jobs=self.jobs_u1)
        actual = engine2._identical_input("exposure", "x" * 32,
                                         self.job.owner.id)
        self.assertIs(None, actual)

    def test__identical_input_and_failed_first_job(self):
        # The exposure model input is not found since the first job to have
        # used it has failed.
        self._setup_input(
            input_type="exposure", size=123, path=self.EXPOM, digest="0" * 32,
            jobs=self.failed_jobs_u1)
        actual = engine2._identical_input("exposure", "0" * 32,
                                         self.job.owner.id)
        self.assertIs(None, actual)

    def test__identical_input_and_owner_differing_from_user(self):
        # The exposure model input is not found since its owner is not the user
        # that is running the current job.
        self._setup_input(
            input_type="exposure", size=123, path=self.EXPOM, digest="0" * 32,
            jobs=self.jobs_u2)
        actual = engine2._identical_input("exposure", "0" * 32,
                                         self.job.owner.id)
        self.assertIs(None, actual)

class CreateHazardJobProfileTestCase(unittest.TestCase):

    def test_create_hazard_job_profile(self):
        # Just the bare minimum set of params to satisfy not null constraints
        # in the db.
        params = {
            'calculation_mode': 'classical',
            'region': '1 1 2 2 3 3',
            'width_of_mfd_bin': '1',
            'rupture_mesh_spacing': '1',
            'area_source_discretization': '2',
            'investigation_time': 50,
            'truncation_level': 0,
            'maximum_distance': 200,
        }

        owner = helpers.default_user()
        hjp = engine2.create_hazard_job_profile(params, owner)
        # Normalize/clean fields by fetching a fresh copy from the db.
        hjp = models.HazardJobProfile.objects.get(id=hjp.id)

        self.assertEqual(hjp.calculation_mode, 'classical')
        self.assertEqual(hjp.width_of_mfd_bin, 1.0)
        self.assertEqual(hjp.rupture_mesh_spacing, 1.0)
        self.assertEqual(hjp.area_source_discretization, 2.0)
        self.assertEqual(hjp.investigation_time, 50.0)
        self.assertEqual(hjp.truncation_level, 0.0)
        self.assertEqual(hjp.maximum_distance, 200.0)

class ReadJobProfileFromConfigFileTestCase(unittest.TestCase):
    """Integration test for basic engine functions.

    Test reading/generating a hazard job profile from a config file, then run
    through the validation form.
    """

    def test_read_and_validate_hazard_config(self):
        cfg = helpers.demo_file('simple_fault_demo_hazard/job.ini')
        job = engine2.prepare_job(getpass.getuser())
        params, files = engine2.parse_config(open(cfg, 'r'))
        profile = engine2.create_hazard_job_profile(params, job.owner)

        form = validation.ClassicalHazardJobForm(instance=profile, files=files)
        self.assertTrue(form.is_valid())
<|endoftext|>


<commit_before>from django.conf import settings
from django.contrib.comments.models import Comment
from django.contrib.sites.models import Site
from django.utils.translation import ugettext_lazy as _
from satchmo.caching import cache_get, cache_set, NotCachedError
from satchmo.configuration import config_value
from satchmo.product.models import Product
import logging
import math
import operator

log = logging.getLogger('product.comments')

def average(ratings):
    """ Average a list of numbers, return None if it fails """
    total = reduce(operator.add, ratings)
    if(total == None):
        return None
    return float(total)/len(ratings)

def get_product_rating(product, site=None):
    """Get the average product rating"""
    if site is None:
        site = Site.objects.get_current()

    site = site.id

    manager = Comment.objects
    comments = manager.filter(object_pk__exact=product.id,
                               content_type__app_label__exact='product',
                               content_type__model__exact='product',
                               site__id__exact=site,
                               is_public__exact=True)
    ratings = []
    for comment in comments:
        if hasattr(comment,'productrating'):
            rating = comment.productrating.rating
            if rating > 0:
                ratings.append(rating)

    log.debug("Ratings: %s", ratings)
    if ratings:
        return average(ratings)

    else:
        return None

def get_product_rating_string(product, site=None):
    """Get the average product rating as a string, for use in templates"""
    rating = get_product_rating(product, site=site)

    if rating is not None:
        rating = "%0.1f" % rating
        if rating.endswith('.0'):
            rating = rating[0]
        rating = rating + "/5"
    else:
        rating = _('Not Rated')

    return rating

def highest_rated(num=None, site=None):
    """Get the most highly rated products"""
    if site is None:
        site = Site.objects.get_current()

    site = site.id

    try:
        pks = cache_get("BESTRATED", site=site, num=num)
        pks = [pk for pk in pks.split(',')]
        log.debug('retrieved highest rated products from cache')

    except NotCachedError, nce:
        # here were are going to do just one lookup for all product comments

        comments = Comment.objects.filter(content_type__app_label__exact='product',
            content_type__model__exact='product',
            site__id__exact=site.id,
            productrating__rating__gt=0,
            is_public__exact=True).order_by('object_pk')

        # then make lists of ratings for each
        commentdict = {}
        for comment in comments:
            if hasattr(comment, 'productrating'):
                rating = comment.productrating.rating
                if rating>0:
                    commentdict.setdefault(comment.object_id, []).append(rating)

        # now take the average of each, and make a nice list suitable for sorting
        ratelist = [(average(ratings), pk) for pk, ratings in commentdict.items()]
        ratelist.sort()
        #log.debug(ratelist)

        # chop off the highest and reverse so highest is the first
        if num is None:
            num = config_value('SHOP', 'NUM_DISPLAY')
        ratelist = ratelist[-num:]
        ratelist.reverse()

        pks = ["%i" % p[1] for p in ratelist]
        pkstring = ",".join(pks)
        log.debug('calculated highest rated products, set to cache: %s', pkstring)
        cache_set(nce.key, value=pkstring)

    if pks:
        pks = [pk for pk in pks if _is_int(pk)]
        productdict = Product.objects.in_bulk(pks)
        products = []
        for pk in pks:
            try:
                if (int(pk)) in productdict:
                    key = int(pk)
                elif long(pk) in productdict:
                    key = long(pk)
                else:
                    continue
                products.append(productdict[key])
            except ValueError:
                pass
    else:
        products = []

    return products

def _is_int(v):
    try:
        v = int(v)
        return True
    except ValueError:
        return False
<commit_msg>Fix some type cast issues with product ratings. Some of these only show up on postgresql.<commit_after>from django.conf import settings
from django.contrib.comments.models import Comment
from django.contrib.sites.models import Site
from django.utils.translation import ugettext_lazy as _
from satchmo.caching import cache_get, cache_set, NotCachedError
from satchmo.configuration import config_value
from satchmo.product.models import Product
import logging
import math
import operator

log = logging.getLogger('product.comments')

def average(ratings):
    """ Average a list of numbers, return None if it fails """
    total = reduce(operator.add, ratings)
    if(total == None):
        return None
    return float(total)/len(ratings)

def get_product_rating(product, site=None):
    """Get the average product rating"""
    if site is None:
        site = Site.objects.get_current()

    site = site.id
    manager = Comment.objects
    comments = manager.filter(object_pk__exact=str(product.id),
                               content_type__app_label__exact='product',
                               content_type__model__exact='product',
                               site__id__exact=site,
                               is_public__exact=True)
    ratings = []
    for comment in comments:
        if hasattr(comment,'productrating'):
            rating = comment.productrating.rating
            if rating > 0:
                ratings.append(rating)

    log.debug("Ratings: %s", ratings)
    if ratings:
        return average(ratings)

    else:
        return None

def get_product_rating_string(product, site=None):
    """Get the average product rating as a string, for use in templates"""
    rating = get_product_rating(product, site=site)

    if rating is not None:
        rating = "%0.1f" % rating
        if rating.endswith('.0'):
            rating = rating[0]
        rating = rating + "/5"
    else:
        rating = _('Not Rated')

    return rating

def highest_rated(num=None, site=None):
    """Get the most highly rated products"""
    if site is None:
        site = Site.objects.get_current()

    site_id = site.id

    try:
        pks = cache_get("BESTRATED", site=site_id, num=num)
        pks = [pk for pk in pks.split(',')]
        log.debug('retrieved highest rated products from cache')

    except NotCachedError, nce:
        # here were are going to do just one lookup for all product comments

        comments = Comment.objects.filter(content_type__app_label__exact='product',
            content_type__model__exact='product',
            site__id__exact=site_id,
            productrating__rating__gt=0,
            is_public__exact=True).order_by('object_pk')

        # then make lists of ratings for each
        commentdict = {}
        for comment in comments:
            if hasattr(comment, 'productrating'):
                rating = comment.productrating.rating
                if rating>0:
                    commentdict.setdefault(comment.object_pk, []).append(rating)

        # now take the average of each, and make a nice list suitable for sorting
        ratelist = [(average(ratings), int(pk)) for pk, ratings in commentdict.items()]
        ratelist.sort()
        #log.debug(ratelist)

        # chop off the highest and reverse so highest is the first
        if num is None:
            num = config_value('SHOP', 'NUM_DISPLAY')
        ratelist = ratelist[-num:]
        ratelist.reverse()

        pks = ["%i" % p[1] for p in ratelist]
        pkstring = ",".join(pks)
        log.debug('calculated highest rated products, set to cache: %s', pkstring)
        cache_set(nce.key, value=pkstring)

    if pks:
        pks = [pk for pk in pks if _is_int(pk)]
        productdict = Product.objects.in_bulk(pks)
        products = []
        for pk in pks:
            try:
                if (int(pk)) in productdict:
                    key = int(pk)
                elif long(pk) in productdict:
                    key = long(pk)
                else:
                    continue
                products.append(productdict[key])
            except ValueError:
                pass
    else:
        products = []

    return products

def _is_int(v):
    try:
        v = int(v)
        return True
    except ValueError:
        return False
<|endoftext|>


<commit_before>from Module import Module
from Location import Location
import datetime

class Project():
    def __init__(self, name, method, date):
        self.name = name
        self.development_method = method
        self.delivery_date = date
        self.modules = list()
        self.locations = list()
        self.current_time = datetime.datetime(2014,1,1,0,0,0)<commit_msg>added in start_time to project for endgame screen<commit_after>from Module import Module
from Location import Location
import datetime

class Project():
    def __init__(self, name, method, date):
        self.name = name
        self.development_method = method
        self.delivery_date = date
        self.modules = list()
        self.locations = list()
        self.start_time = datetime.datetime(2014,1,1,0,0,0)
        self.current_time = datetime.datetime(2014,1,1,0,0,0)<|endoftext|>


<commit_before>"""
 Something that will create dataspreadsheets based on entries in the table
"""
import gdata.spreadsheets.client
import gdata.spreadsheets.data
import gdata.docs.data
import gdata.docs.client
import gdata.gauth
import re
import ConfigParser
import sys
import util
import copy

config = ConfigParser.ConfigParser()
config.read('mytokens.cfg')

token = gdata.gauth.OAuth2Token(client_id=config.get('appauth','client_id'),
                                client_secret=config.get('appauth', 'app_secret'),
                                user_agent='daryl.testing',
                                scope=config.get('googleauth', 'scopes'),
                                refresh_token=config.get('googleauth', 'refresh_token'))

spr_client = gdata.spreadsheets.client.SpreadsheetsClient()
token.authorize(spr_client)

docs_client = gdata.docs.client.DocsClient()
token.authorize(docs_client)

meta_feed = spr_client.get_list_feed(config.get('cscap', 'metamaster'), 'od6')
sdc_feed = spr_client.get_list_feed(config.get('cscap', 'sdckey'), 'od6')
treat_feed = spr_client.get_list_feed(config.get('cscap', 'treatkey'), 'od6')

treatments, treatment_names = util.build_treatments(treat_feed)
sdc, sdc_names = util.build_sdc(sdc_feed)

"""
Okay, we are cross product TIL x ROT x DWN x NIT
"""

baseheaders = ['UniqueID', 'Rep', 'Tillage', 'Rotation', 'Drainage', 'Nitrogen',
               'Landscape', 'PlotID', 'ROW', 'COLUMN']
basecolumns = {'A': 'UniqueID', 'B': 'Rep', 'C': 'Tillage', 'D': 'Rotation',
           'E': 'Drainage', 'F': 'Nitrogen', 'G': 'Landscape','H': 'Plot ID',
           'I': 'ROW', 'J': 'COLUMN'}

letters = ['K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
           'AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL']

DONE = ['bradford.c',]

for entry in meta_feed.entry:
    data = entry.to_dict()
    sitekey = data.get('uniqueid').lower()
    if sitekey not in DONE:
        print 'skip', sitekey
        continue
    # This is the folder where synced data is stored
    colfolder = data.get('colfolder')
    collect = docs_client.get_resource_by_id(colfolder)
    leadpi = data.get('leadpi')

    # Lets go find the Plot Identifiers Table
    keyspread = data.get('keyspread')
    # loop over rows
    plots = []
    feed = spr_client.get_list_feed(keyspread, 'od6')
    for feed_entry in feed.entry:
        cols = feed_entry.to_dict()
        if cols.get('plotid', '') not in ['', None]:
            plots.append( cols )

    if len(plots) == 0:
        print 'No plot IDs found for: %s %s' % (leadpi, sitekey.upper())
        continue

    # Figure out our columns
    columns = basecolumns.copy()
    headers = copy.deepcopy(baseheaders)
    i = 0
    datavars = []
    for datavar in sdc[sitekey]:
        if datavar.find("AGR") > -1:
            #hid = '%s %s %s' % (datavar, sdc_names[datavar]['name'], sdc_names[datavar]['units'])
            hid = datavar
            columns[ letters[i] ] = hid
            datavars.append( hid )
            headers.append( hid )
            i += 1

    if len(datavars) == 0:
        print 'NO AGR DATA! %s' % (sitekey,)
        continue

    # Figure out how many
    rows = []
    # Do row 2
    entry = gdata.spreadsheets.data.ListEntry()
    for dv in datavars:
        hid = '%s' % (sdc_names[dv]['name'], )
        entry.set_value(dv.lower(), hid)
    for col in plots[0].keys():
        entry.set_value(col, '')
    rows.append(entry)
    # Do row 3
    entry = gdata.spreadsheets.data.ListEntry()
    for dv in datavars:
        hid = '%s' % (sdc_names[dv]['units'],)
        entry.set_value(dv.lower(), hid)
    for col in plots[0].keys():
        entry.set_value(col, '')
    rows.append(entry)
    for plot in plots:
        entry = gdata.spreadsheets.data.ListEntry()
        for dv in datavars:
            entry.set_value(dv.lower(), '')
        for col in plot.keys():
            entry.set_value(col, plot[col])
        rows.append(entry)

    # Okay, now we are ready to create a spreadsheet
    title = '%s %s Agronomic Data' % (sitekey.upper(), leadpi)
    print 'Adding: %s ROWS: %s' %  (title, len(rows))
    doc = gdata.docs.data.Resource(type='spreadsheet', title=title)
    doc = docs_client.CreateResource(doc, collection=collect)
    spreadkey= doc.resource_id.text.split(':')[1]
    for yr in ['2011','2012','2013','2014','2015']:
        print 'Adding worksheet for year: %s' % (yr,)
        sheet = spr_client.add_worksheet(spreadkey, yr, 10, len(columns))
        for colnum in range(len(headers)):
            cell = spr_client.get_cell(spreadkey, sheet.get_worksheet_id(),1, colnum+1)
            cell.cell.input_value = headers[colnum]
            spr_client.update(cell)
        for row in rows:
            spr_client.add_list_entry(row, spreadkey, sheet.get_worksheet_id())
    #TODO remove first worksheet
    sheet = spr_client.get_worksheet(spreadkey,'od6')
    spr_client.delete(sheet)
<commit_msg>sdc changed slightly by year<commit_after>"""
 Something that will create dataspreadsheets based on entries in the table
"""
import gdata.spreadsheets.client
import gdata.spreadsheets.data
import gdata.docs.data
import gdata.docs.client
import gdata.gauth
import re
import ConfigParser
import sys
import util
import copy

config = ConfigParser.ConfigParser()
config.read('mytokens.cfg')

token = gdata.gauth.OAuth2Token(client_id=config.get('appauth','client_id'),
                                client_secret=config.get('appauth', 'app_secret'),
                                user_agent='daryl.testing',
                                scope=config.get('googleauth', 'scopes'),
                                refresh_token=config.get('googleauth', 'refresh_token'))

spr_client = gdata.spreadsheets.client.SpreadsheetsClient()
token.authorize(spr_client)

docs_client = gdata.docs.client.DocsClient()
token.authorize(docs_client)

meta_feed = spr_client.get_list_feed(config.get('cscap', 'metamaster'), 'od6')
sdc_feed = spr_client.get_list_feed(config.get('cscap', 'sdckey'), 'od6')
treat_feed = spr_client.get_list_feed(config.get('cscap', 'treatkey'), 'od6')

treatments, treatment_names = util.build_treatments(treat_feed)
sdc, sdc_names = util.build_sdc(sdc_feed)

"""
Okay, we are cross product TIL x ROT x DWN x NIT
"""

baseheaders = ['UniqueID', 'Rep', 'Tillage', 'Rotation', 'Drainage', 'Nitrogen',
               'Landscape', 'PlotID', 'ROW', 'COLUMN']
basecolumns = {'A': 'UniqueID', 'B': 'Rep', 'C': 'Tillage', 'D': 'Rotation',
           'E': 'Drainage', 'F': 'Nitrogen', 'G': 'Landscape','H': 'Plot ID',
           'I': 'ROW', 'J': 'COLUMN'}

letters = ['K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
           'AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL']

TODO = ['swroc.p','naew.y102', 'naew.y103']

for entry in meta_feed.entry:
    data = entry.to_dict()
    sitekey = data.get('uniqueid').lower()
    if sitekey not in TODO:
        print 'skip', sitekey
        continue
    # This is the folder where synced data is stored
    colfolder = data.get('colfolder')
    collect = docs_client.get_resource_by_id(colfolder)
    leadpi = data.get('leadpi')

    # Lets go find the Plot Identifiers Table
    keyspread = data.get('keyspread')
    # loop over rows
    plots = []
    feed = spr_client.get_list_feed(keyspread, 'od6')
    for feed_entry in feed.entry:
        cols = feed_entry.to_dict()
        if cols.get('plotid', '') not in ['', None]:
            plots.append( cols )

    if len(plots) == 0:
        print 'No plot IDs found for: %s %s' % (leadpi, sitekey.upper())
        continue

    # Figure out our columns
    columns = basecolumns.copy()
    headers = copy.deepcopy(baseheaders)
    i = 0
    datavars = []
    for datavar in sdc['2011'][sitekey]:
        if datavar.find("AGR") > -1:
            #hid = '%s %s %s' % (datavar, sdc_names[datavar]['name'], sdc_names[datavar]['units'])
            hid = datavar
            columns[ letters[i] ] = hid
            datavars.append( hid )
            headers.append( hid )
            i += 1

    if len(datavars) == 0:
        print 'NO AGR DATA! %s' % (sitekey,)
        continue

    # Figure out how many
    rows = []
    # Do row 2
    entry = gdata.spreadsheets.data.ListEntry()
    for dv in datavars:
        hid = '%s' % (sdc_names[dv]['name'], )
        entry.set_value(dv.lower(), hid)
    for col in plots[0].keys():
        entry.set_value(col, '')
    rows.append(entry)
    # Do row 3
    entry = gdata.spreadsheets.data.ListEntry()
    for dv in datavars:
        hid = '%s' % (sdc_names[dv]['units'],)
        entry.set_value(dv.lower(), hid)
    for col in plots[0].keys():
        entry.set_value(col, '')
    rows.append(entry)
    for plot in plots:
        entry = gdata.spreadsheets.data.ListEntry()
        for dv in datavars:
            entry.set_value(dv.lower(), '')
        for col in plot.keys():
            entry.set_value(col, plot[col])
        rows.append(entry)

    # Okay, now we are ready to create a spreadsheet
    title = '%s %s Agronomic Data' % (sitekey.upper(), leadpi)
    print 'Adding: %s ROWS: %s' %  (title, len(rows))
    doc = gdata.docs.data.Resource(type='spreadsheet', title=title)
    doc = docs_client.CreateResource(doc, collection=collect)
    spreadkey= doc.resource_id.text.split(':')[1]
    for yr in ['2011','2012','2013','2014','2015']:
        print 'Adding worksheet for year: %s' % (yr,)
        sheet = spr_client.add_worksheet(spreadkey, yr, 10, len(columns))
        for colnum in range(len(headers)):
            cell = spr_client.get_cell(spreadkey, sheet.get_worksheet_id(),1, colnum+1)
            cell.cell.input_value = headers[colnum]
            spr_client.update(cell)
        for row in rows:
            spr_client.add_list_entry(row, spreadkey, sheet.get_worksheet_id())
    #TODO remove first worksheet
    sheet = spr_client.get_worksheet(spreadkey,'od6')
    spr_client.delete(sheet)
<|endoftext|>


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

"""Unit tests for gclient.py.

See gclient_smoketest.py for integration tests.
"""

from __future__ import with_statement
import Queue
import logging
import os
import sys
import unittest

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.dirname(BASE_DIR))

import gclient
from tests import trial_dir

def write(filename, content):
  """Writes the content of a file and create the directories as needed."""
  filename = os.path.abspath(filename)
  dirname = os.path.dirname(filename)
  if not os.path.isdir(dirname):
    os.makedirs(dirname)
  with open(filename, 'w') as f:
    f.write(content)

class SCMMock(object):
  def __init__(self, unit_test, url):
    self.unit_test = unit_test
    self.url = url

  def RunCommand(self, command, options, args, file_list):
    self.unit_test.assertEquals('None', command)
    self.unit_test.processed.put(self.url)

  def FullUrlForRelativeUrl(self, url):
    return self.url + url

class GclientTest(trial_dir.TestCase):
  def setUp(self):
    super(GclientTest, self).setUp()
    self.processed = Queue.Queue()
    self.previous_dir = os.getcwd()
    os.chdir(self.root_dir)
    # Manual mocks.
    self._old_createscm = gclient.gclient_scm.CreateSCM
    gclient.gclient_scm.CreateSCM = self._createscm
    self._old_sys_stdout = sys.stdout
    sys.stdout = gclient.gclient_utils.MakeFileAutoFlush(sys.stdout)
    sys.stdout = gclient.gclient_utils.MakeFileAnnotated(sys.stdout)

  def tearDown(self):
    self.assertEquals([], self._get_processed())
    gclient.gclient_scm.CreateSCM = self._old_createscm
    sys.stdout = self._old_sys_stdout
    os.chdir(self.previous_dir)
    super(GclientTest, self).tearDown()

  def _createscm(self, parsed_url, root_dir, name):
    self.assertTrue(parsed_url.startswith('svn://example.com/'), parsed_url)
    self.assertTrue(root_dir.startswith(self.root_dir), root_dir)
    return SCMMock(self, parsed_url)

  def testDependencies(self):
    self._dependencies('1', False)

  def testDependenciesReverse(self):
    self._dependencies('1', True)

  def testDependenciesJobs(self):
    self._dependencies('1000', False)

  def testDependenciesJobsReverse(self):
    self._dependencies('1000', True)

  def _dependencies(self, jobs, reverse):
    # Verify that dependencies are processed in the right order, e.g. if there
    # is a dependency 'src' and another 'src/third_party/bar', that bar isn't
    # fetched until 'src' is done.
    # jobs is the number of parallel jobs simulated. reverse is to reshuffle the
    # list to see if it is still processed in order correctly.
    # Also test that a From() dependency that should not be processed is listed
    # as a requirement.
    parser = gclient.Parser()
    options, args = parser.parse_args(['--jobs', jobs])
    write(
        '.gclient',
        'solutions = [\n'
        '  { "name": "foo", "url": "svn://example.com/foo" },\n'
        '  { "name": "bar", "url": "svn://example.com/bar" },\n'
        '  { "name": "bar/empty", "url": "svn://example.com/bar_empty" },\n'
        ']')
    write(
        os.path.join('foo', 'DEPS'),
        'deps = {\n'
        '  "foo/dir1": "/dir1",\n'
        # This one will depend on dir1/dir2 in bar.
        '  "foo/dir1/dir2/dir3": "/dir1/dir2/dir3",\n'
        '  "foo/dir1/dir2/dir3/dir4": "/dir1/dir2/dir3/dir4",\n'
        '  "foo/dir1/dir2/dir5/dir6":\n'
        '    From("foo/dir1/dir2/dir3/dir4", "foo/dir1/dir2"),\n'
        '}')
    write(
        os.path.join('bar', 'DEPS'),
        'deps = {\n'
        '  "foo/dir1/dir2": "/dir1/dir2",\n'
        '}')
    write(
        os.path.join('bar/empty', 'DEPS'),
        'deps = {\n'
        '}')
    # Test From()
    write(
        os.path.join('foo/dir1/dir2/dir3/dir4', 'DEPS'),
        'deps = {\n'
        # This one should not be fetched or set as a requirement.
        '  "foo/dir1/dir2/dir5": "svn://example.com/x",\n'
        '  "foo/dir1/dir2": "/dir1/another",\n'
        '}')

    obj = gclient.GClient.LoadCurrentConfig(options)
    self._check_requirements(obj.dependencies[0], {})
    self._check_requirements(obj.dependencies[1], {})
    obj.RunOnDeps('None', args)
    # The trick here is to manually process the list to make sure it's out of
    # order.
    for i in obj.dependencies:
      # pylint: disable=W0212
      i._dependencies.sort(key=lambda x: x.name, reverse=reverse)
    actual = self._get_processed()
    # We don't care of the ordering of these items:
    self.assertEquals(
        ['svn://example.com/bar', 'svn://example.com/foo'], sorted(actual[0:2]))
    actual = actual[2:]
    # Ordering may not be exact in case of parallel jobs.
    self.assertTrue(
        actual.index('svn://example.com/bar/dir1/dir2') >
        actual.index('svn://example.com/foo/dir1'))
    actual.remove('svn://example.com/bar/dir1/dir2')

    # Ordering may not be exact in case of parallel jobs.
    actual.remove('svn://example.com/bar_empty')
    self.assertEquals(
        [
          'svn://example.com/foo/dir1',
          'svn://example.com/foo/dir1/dir2/dir3',
          'svn://example.com/foo/dir1/dir2/dir3/dir4',
          # TODO(maruel): This is probably wrong.
          'svn://example.com/foo/dir1/dir2/dir3/dir4/dir1/another',
        ],
        actual)
    self._check_requirements(
        obj.dependencies[0],
        {
          'foo/dir1': ['foo'],
          'foo/dir1/dir2/dir3': ['foo', 'foo/dir1', 'foo/dir1/dir2'],
          'foo/dir1/dir2/dir3/dir4':
              ['foo', 'foo/dir1', 'foo/dir1/dir2', 'foo/dir1/dir2/dir3'],
          'foo/dir1/dir2/dir5/dir6':
              ['foo', 'foo/dir1', 'foo/dir1/dir2', 'foo/dir1/dir2/dir3/dir4'],
        })
    self._check_requirements(
        obj.dependencies[1],
        {
          'foo/dir1/dir2': ['bar', 'foo', 'foo/dir1'],
        })
    self._check_requirements(
        obj,
        {
          'foo': [],
          'bar': [],
          'bar/empty': ['bar'],
        })

  def _check_requirements(self, solution, expected):
    for dependency in solution.dependencies:
      self.assertEquals(
          expected.pop(dependency.name), sorted(dependency.requirements))
    self.assertEquals({}, expected)

  def _get_processed(self):
    items = []
    try:
      while True:
        items.append(self.processed.get_nowait())
    except Queue.Empty:
      pass
    return items

  def testAutofix(self):
    # Invalid urls causes pain when specifying requirements. Make sure it's
    # auto-fixed.
    d = gclient.Dependency(
        None, 'name', 'proto://host/path/@revision', None, None, None,
        None, '', True)
    self.assertEquals('proto://host/path@revision', d.url)

  def testStr(self):
    # Make sure __str__() works fine.
    # pylint: disable=W0212
    parser = gclient.Parser()
    options, _ = parser.parse_args([])
    obj = gclient.GClient('foo', options)
    obj._dependencies.append(
        gclient.Dependency(obj, 'foo', 'url', None, None, None, None, 'DEPS',
                           True))
    obj._dependencies.append(
        gclient.Dependency(obj, 'bar', 'url', None, None, None, None, 'DEPS',
                           True))
    obj.dependencies[0]._dependencies.append(
        gclient.Dependency(
          obj.dependencies[0], 'foo/dir1', 'url', None, None, None, None,
          'DEPS', True))
    obj.dependencies[0]._dependencies.append(
        gclient.Dependency(
          obj.dependencies[0], 'foo/dir2',
          gclient.GClientKeywords.FromImpl('bar'), None, None, None, None,
          'DEPS', True))
    obj.dependencies[0]._dependencies.append(
        gclient.Dependency(
          obj.dependencies[0], 'foo/dir3',
          gclient.GClientKeywords.FileImpl('url'), None, None, None, None,
          'DEPS', True))
    obj.dependencies[0]._file_list.append('foo')
    self.assertEquals(434, len(str(obj)), '%d\n%s' % (len(str(obj)), str(obj)))

if __name__ == '__main__':
  logging.basicConfig(
      level=[logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG][
        min(sys.argv.count('-v'), 3)],
      format='%(asctime).19s %(levelname)s %(filename)s:'
              '%(lineno)s %(message)s')
  unittest.main()
<commit_msg>Disable --jobs 1000 tests since it's not stable yet.<commit_after>#!/usr/bin/env python
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Unit tests for gclient.py.

See gclient_smoketest.py for integration tests.
"""

from __future__ import with_statement
import Queue
import logging
import os
import sys
import unittest

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.dirname(BASE_DIR))

import gclient
from tests import trial_dir

def write(filename, content):
  """Writes the content of a file and create the directories as needed."""
  filename = os.path.abspath(filename)
  dirname = os.path.dirname(filename)
  if not os.path.isdir(dirname):
    os.makedirs(dirname)
  with open(filename, 'w') as f:
    f.write(content)

class SCMMock(object):
  def __init__(self, unit_test, url):
    self.unit_test = unit_test
    self.url = url

  def RunCommand(self, command, options, args, file_list):
    self.unit_test.assertEquals('None', command)
    self.unit_test.processed.put(self.url)

  def FullUrlForRelativeUrl(self, url):
    return self.url + url

class GclientTest(trial_dir.TestCase):
  def setUp(self):
    super(GclientTest, self).setUp()
    self.processed = Queue.Queue()
    self.previous_dir = os.getcwd()
    os.chdir(self.root_dir)
    # Manual mocks.
    self._old_createscm = gclient.gclient_scm.CreateSCM
    gclient.gclient_scm.CreateSCM = self._createscm
    self._old_sys_stdout = sys.stdout
    sys.stdout = gclient.gclient_utils.MakeFileAutoFlush(sys.stdout)
    sys.stdout = gclient.gclient_utils.MakeFileAnnotated(sys.stdout)

  def tearDown(self):
    self.assertEquals([], self._get_processed())
    gclient.gclient_scm.CreateSCM = self._old_createscm
    sys.stdout = self._old_sys_stdout
    os.chdir(self.previous_dir)
    super(GclientTest, self).tearDown()

  def _createscm(self, parsed_url, root_dir, name):
    self.assertTrue(parsed_url.startswith('svn://example.com/'), parsed_url)
    self.assertTrue(root_dir.startswith(self.root_dir), root_dir)
    return SCMMock(self, parsed_url)

  def testDependencies(self):
    self._dependencies('1', False)

  def testDependenciesReverse(self):
    self._dependencies('1', True)

  def testDependenciesJobs(self):
    # TODO(maruel): Reenable once parallel processing works.
    #self._dependencies('1000', False)
    pass

  def testDependenciesJobsReverse(self):
    # TODO(maruel): Reenable once parallel processing works.
    #self._dependencies('1000', True)
    pass

  def _dependencies(self, jobs, reverse):
    # Verify that dependencies are processed in the right order, e.g. if there
    # is a dependency 'src' and another 'src/third_party/bar', that bar isn't
    # fetched until 'src' is done.
    # jobs is the number of parallel jobs simulated. reverse is to reshuffle the
    # list to see if it is still processed in order correctly.
    # Also test that a From() dependency that should not be processed is listed
    # as a requirement.
    parser = gclient.Parser()
    options, args = parser.parse_args(['--jobs', jobs])
    write(
        '.gclient',
        'solutions = [\n'
        '  { "name": "foo", "url": "svn://example.com/foo" },\n'
        '  { "name": "bar", "url": "svn://example.com/bar" },\n'
        '  { "name": "bar/empty", "url": "svn://example.com/bar_empty" },\n'
        ']')
    write(
        os.path.join('foo', 'DEPS'),
        'deps = {\n'
        '  "foo/dir1": "/dir1",\n'
        # This one will depend on dir1/dir2 in bar.
        '  "foo/dir1/dir2/dir3": "/dir1/dir2/dir3",\n'
        '  "foo/dir1/dir2/dir3/dir4": "/dir1/dir2/dir3/dir4",\n'
        '  "foo/dir1/dir2/dir5/dir6":\n'
        '    From("foo/dir1/dir2/dir3/dir4", "foo/dir1/dir2"),\n'
        '}')
    write(
        os.path.join('bar', 'DEPS'),
        'deps = {\n'
        '  "foo/dir1/dir2": "/dir1/dir2",\n'
        '}')
    write(
        os.path.join('bar/empty', 'DEPS'),
        'deps = {\n'
        '}')
    # Test From()
    write(
        os.path.join('foo/dir1/dir2/dir3/dir4', 'DEPS'),
        'deps = {\n'
        # This one should not be fetched or set as a requirement.
        '  "foo/dir1/dir2/dir5": "svn://example.com/x",\n'
        '  "foo/dir1/dir2": "/dir1/another",\n'
        '}')

    obj = gclient.GClient.LoadCurrentConfig(options)
    self._check_requirements(obj.dependencies[0], {})
    self._check_requirements(obj.dependencies[1], {})
    obj.RunOnDeps('None', args)
    # The trick here is to manually process the list to make sure it's out of
    # order.
    for i in obj.dependencies:
      # pylint: disable=W0212
      i._dependencies.sort(key=lambda x: x.name, reverse=reverse)
    actual = self._get_processed()
    # We don't care of the ordering of these items:
    self.assertEquals(
        ['svn://example.com/bar', 'svn://example.com/foo'], sorted(actual[0:2]))
    actual = actual[2:]
    # Ordering may not be exact in case of parallel jobs.
    self.assertTrue(
        actual.index('svn://example.com/bar/dir1/dir2') >
        actual.index('svn://example.com/foo/dir1'))
    actual.remove('svn://example.com/bar/dir1/dir2')

    # Ordering may not be exact in case of parallel jobs.
    actual.remove('svn://example.com/bar_empty')
    self.assertEquals(
        [
          'svn://example.com/foo/dir1',
          'svn://example.com/foo/dir1/dir2/dir3',
          'svn://example.com/foo/dir1/dir2/dir3/dir4',
          # TODO(maruel): This is probably wrong.
          'svn://example.com/foo/dir1/dir2/dir3/dir4/dir1/another',
        ],
        actual)
    self._check_requirements(
        obj.dependencies[0],
        {
          'foo/dir1': ['foo'],
          'foo/dir1/dir2/dir3': ['foo', 'foo/dir1', 'foo/dir1/dir2'],
          'foo/dir1/dir2/dir3/dir4':
              ['foo', 'foo/dir1', 'foo/dir1/dir2', 'foo/dir1/dir2/dir3'],
          'foo/dir1/dir2/dir5/dir6':
              ['foo', 'foo/dir1', 'foo/dir1/dir2', 'foo/dir1/dir2/dir3/dir4'],
        })
    self._check_requirements(
        obj.dependencies[1],
        {
          'foo/dir1/dir2': ['bar', 'foo', 'foo/dir1'],
        })
    self._check_requirements(
        obj,
        {
          'foo': [],
          'bar': [],
          'bar/empty': ['bar'],
        })

  def _check_requirements(self, solution, expected):
    for dependency in solution.dependencies:
      self.assertEquals(
          expected.pop(dependency.name), sorted(dependency.requirements))
    self.assertEquals({}, expected)

  def _get_processed(self):
    items = []
    try:
      while True:
        items.append(self.processed.get_nowait())
    except Queue.Empty:
      pass
    return items

  def testAutofix(self):
    # Invalid urls causes pain when specifying requirements. Make sure it's
    # auto-fixed.
    d = gclient.Dependency(
        None, 'name', 'proto://host/path/@revision', None, None, None,
        None, '', True)
    self.assertEquals('proto://host/path@revision', d.url)

  def testStr(self):
    # Make sure __str__() works fine.
    # pylint: disable=W0212
    parser = gclient.Parser()
    options, _ = parser.parse_args([])
    obj = gclient.GClient('foo', options)
    obj._dependencies.append(
        gclient.Dependency(obj, 'foo', 'url', None, None, None, None, 'DEPS',
                           True))
    obj._dependencies.append(
        gclient.Dependency(obj, 'bar', 'url', None, None, None, None, 'DEPS',
                           True))
    obj.dependencies[0]._dependencies.append(
        gclient.Dependency(
          obj.dependencies[0], 'foo/dir1', 'url', None, None, None, None,
          'DEPS', True))
    obj.dependencies[0]._dependencies.append(
        gclient.Dependency(
          obj.dependencies[0], 'foo/dir2',
          gclient.GClientKeywords.FromImpl('bar'), None, None, None, None,
          'DEPS', True))
    obj.dependencies[0]._dependencies.append(
        gclient.Dependency(
          obj.dependencies[0], 'foo/dir3',
          gclient.GClientKeywords.FileImpl('url'), None, None, None, None,
          'DEPS', True))
    obj.dependencies[0]._file_list.append('foo')
    self.assertEquals(434, len(str(obj)), '%d\n%s' % (len(str(obj)), str(obj)))

if __name__ == '__main__':
  logging.basicConfig(
      level=[logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG][
        min(sys.argv.count('-v'), 3)],
      format='%(asctime).19s %(levelname)s %(filename)s:'
              '%(lineno)s %(message)s')
  unittest.main()
<|endoftext|>


<commit_before># coding=utf-8

from django.contrib import admin
from django import forms
from django.http.response import HttpResponseRedirect
from django.shortcuts import render

from feedback.models import Person, Veranstaltung, Semester, Einstellung, \
    Mailvorlage, Kommentar, Tutor, BarcodeScanner, BarcodeScannEvent, BarcodeAllowedState
from feedback.models.base import Log, Fachgebiet, FachgebietEmail

class PersonAdmin(admin.ModelAdmin):
    """Admin View für Personen"""
    list_display = ('__str__', 'email', 'fachgebiet')
    search_fields = ['vorname', 'nachname', 'email', ]
    list_filter = ('fachgebiet',)

    class FachgebietZuweisenForm(forms.Form):
        """Form für die Zuweisung von einem Fachgebiet für eine Person."""
        _selected_action = forms.CharField(widget=forms.MultipleHiddenInput)

    def assign_fachgebiet_action(self, request, queryset):
        """Definiert eine Admin-Action für die Fachgebietzuweisung."""
        form = None
        suggestion_list = []

        for p in queryset:
            proposed_fachgebiet = FachgebietEmail.get_fachgebiet_from_email(p.email)
            suggestion_list.append((p, proposed_fachgebiet))

        if any(s in request.POST for s in ('apply', 'save')):
            form = self.FachgebietZuweisenForm(request.POST)

            if form.is_valid():
                selected_persons = request.POST.getlist("selectedPerson")
                for person in queryset:
                    person_id_str = str(person.id)
                    if person_id_str in selected_persons:
                        proposed_fachgebiet_id = request.POST.get("fachgebiet_" + person_id_str, 0)
                        if int(proposed_fachgebiet_id) > 0:
                            proposed_fachgebiet = Fachgebiet.objects.get(id=proposed_fachgebiet_id)
                            person.fachgebiet = proposed_fachgebiet
                            person.save()
                            suggestion_list = [(x, y) for x, y in suggestion_list if x is not person]

                self.message_user(request, "Fachgebiete erfolgreich zugewiesen.")

                if ('save' in request.POST) or not suggestion_list:
                    return HttpResponseRedirect(request.get_full_path())

        if not form:
            form = self.FachgebietZuweisenForm(initial={
                '_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
            })

        return render(request, 'admin/fachgebiet.html', {'data': suggestion_list, 'fachgebiet': form, })

    assign_fachgebiet_action.short_description = "Einem Fachgebiet zuweisen"
    actions = [assign_fachgebiet_action]

class LogInline(admin.TabularInline):
    """Admin View für Log"""
    model = Log
    readonly_fields = ('veranstaltung', 'user', 'scanner', 'timestamp', 'status', 'interface')
    can_delete = False

    def has_add_permission(self, request, obj=None):
        return False

class VeranstaltungAdmin(admin.ModelAdmin):
    """Admin View für Veranstaltung"""
    fieldsets = [
        ('Stammdaten', {'fields':
                            ['typ', 'name', 'semester', 'status', 'lv_nr', 'grundstudium', 'evaluieren',
                             'veranstalter', 'link_veranstalter',
                             ]}),
        ('Bestellung', {'fields': ['sprache', 'anzahl', 'verantwortlich', 'ergebnis_empfaenger', 'primaerdozent',
                                   'auswertungstermin', 'freiefrage1', 'freiefrage2', 'kleingruppen', ]}),
    ]
    list_display = ('typ', 'name', 'semester', 'grundstudium', 'evaluieren', 'anzahl',
                    'sprache', 'status', 'veranstalter_list')
    list_display_links = ['name']
    list_filter = ('typ', 'semester', 'status', 'grundstudium', 'evaluieren', 'sprache')
    search_fields = ['name']
    filter_horizontal = ('veranstalter', 'ergebnis_empfaenger')  # @see http://stackoverflow.com/a/5386871
    readonly_fields = ('link_veranstalter',)
    inlines = [LogInline, ]

    def save_model(self, request, obj, form, change):
        """Definiert eine Post-Save Operation."""
        super(VeranstaltungAdmin, self).save_model(request, obj, form, change)
        for changed_att in form.changed_data:
            # Wenn sich der Status ändert, wird es geloggt.
            if changed_att == "status":
                obj.log(request.user)

    class StatusAendernForm(forms.Form):
        """Definiert eine Form für Änderung einen Status'."""
        _selected_action = forms.CharField(widget=forms.MultipleHiddenInput)
        status = forms.ChoiceField(choices=Veranstaltung.STATUS_CHOICES)

    def status_aendern_action(self, request, queryset):
        """Beschreibt eine Admin-Action für die Statusänderung."""
        form = None
        if 'apply' in request.POST:
            form = self.StatusAendernForm(request.POST)
            if form.is_valid():
                status = form.cleaned_data['status']

                queryset.update(status=status)
                for veranstaltung in queryset:
                    veranstaltung.log(request.user)

                self.message_user(request, "Status erfolgreich geändert.")
                return HttpResponseRedirect(request.get_full_path())

        if not form:
            form = self.StatusAendernForm(initial={'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME)})
        return render(request, 'admin/status_aendern.html', {'veranstaltungen': queryset, 'status': form, })

    status_aendern_action.short_description = "Ändere den Status einer Veranstaltung"

    class KeineEvaluationForm(forms.Form):
        _selected_action = None

    def keine_evaluation_action(self, request, queryset):
        """Beschreibt eine Admin-Action für die Option keine Evaluation."""
        form = None
        print(request.POST)
        if 'apply' not in request.POST:     #Dieser Teil reicht bereits zum ändern aus. In diesem Fall können auch Zeile 146-149 gelöscht werden (Kein Bestätigungsfenster erscheint.
            form = self.KeineEvaluationForm(request.POST)
            if form.is_valid():
                queryset.update(status=Veranstaltung.STATUS_KEINE_EVALUATION_FINAL)
                queryset.update(evaluieren=False)
                for veranstaltung in queryset:
                    veranstaltung.log(request.user)

                self.message_user(request, "Keine Evaluation erfolgreich gesetzt.")
                return HttpResponseRedirect(request.get_full_path())

        if not form:
            form = self.KeineEvaluationForm(initial={'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME)})
            #nach dem return landet Python in status_aendern_action
        return render(request, 'admin/keine_evaluation.html', {'veranstaltungen': queryset, 'status': form, })

    keine_evaluation_action.short_description = "Keine Evaluation für diese Veranstaltung"

    actions = [status_aendern_action,keine_evaluation_action]

class SemesterAdmin(admin.ModelAdmin):
    """Admin View für Semester"""
    list_display = ('__str__', 'sichtbarkeit', 'fragebogen')
    list_filter = ('sichtbarkeit', 'fragebogen')
    ordering = ('-semester',)

class EinstellungAdmin(admin.ModelAdmin):
    """Admin View für Einstellung"""
    list_display = ('name', 'wert')
    list_editable = ('wert',)

class MailvorlageAdmin(admin.ModelAdmin):
    """Admin View für Mailvorlage"""
    list_display = ('subject',)

class KommentarAdmin(admin.ModelAdmin):
    """Admin View für Kommentar"""
    list_display = ('typ', 'name', 'semester', 'autor')
    list_display_links = ('name',)

class TutorAdmin(admin.ModelAdmin):
    """Admin View für Tutor"""
    fieldsets = [
        ('Stammdaten', {'fields':
                            ['vorname', 'nachname', 'email',
                             ]}),
        ('Lehrveranstaltung', {'fields':
                                   ['veranstaltung', 'nummer', 'anmerkung'
                                    ]}),
    ]
    list_display = ('vorname', 'nachname', 'nummer', 'veranstaltung')
    search_fields = ('vorname', 'nachname')
    ordering = ('veranstaltung', 'nummer')

    def render_change_form(self, request, context, *args, **kwargs):
        # Limit Auswahl zum aktuellen Semester
        context['adminform'].form.fields['veranstaltung'].queryset = Veranstaltung.objects.filter(
            semester=Semester.current())
        return super(TutorAdmin, self).render_change_form(request, context, args, kwargs)

class BarcodeScannEventAdmin(admin.ModelAdmin):
    """Admin View für BarcodeScannEvent"""
    list_display = ('veranstaltung', 'timestamp',)
    readonly_fields = ('veranstaltung', 'timestamp',)

class BarcodeAllowedStateInline(admin.TabularInline):
    """Admin View für BarcodeAllowedState"""
    model = BarcodeAllowedState

class BarcodeScannerAdmin(admin.ModelAdmin):
    """Admin View für BarcodeScanner"""
    inlines = [
        BarcodeAllowedStateInline,
    ]
    list_display = ('token', 'description')

class FachgebietEmailAdminInline(admin.TabularInline):
    """Admin View für FachgebietEmail"""
    model = FachgebietEmail
    extra = 1

class FachgebietAdmin(admin.ModelAdmin):
    """Admin View für Fachgebiet"""
    list_display = ('name', 'kuerzel')
    list_display_links = ('name',)
    inlines = (FachgebietEmailAdminInline,)

    def save_related(self, request, form, formsets, change):
        super(FachgebietAdmin, self).save_related(request, form, formsets, change)

        if change:
            count_added = 0
            for formset in formsets:
                if formset.extra_forms:
                    for new_forms in formset.extra_forms:
                        if new_forms.instance:
                            fachgebiet_email_instance = new_forms.instance
                            fachgebiet_suffix = fachgebiet_email_instance.email_suffix
                            if fachgebiet_suffix:
                                persons = Person.objects.filter(fachgebiet=None)

                                for person in persons:
                                    proposed_fachgebiet = FachgebietEmail.get_fachgebiet_from_email(person.email)
                                    if proposed_fachgebiet \
                                            and proposed_fachgebiet.id == fachgebiet_email_instance.fachgebiet_id:
                                        person.fachgebiet = proposed_fachgebiet
                                        person.save()
                                        count_added += 1

            if count_added > 0:
                self.message_user(request, "Dieser Fachgebiet wurde {0} Personen zugeordnet".format(count_added))

admin.site.register(Person, PersonAdmin)
admin.site.register(Veranstaltung, VeranstaltungAdmin)
admin.site.register(Semester, SemesterAdmin)
admin.site.register(Einstellung, EinstellungAdmin)
admin.site.register(Mailvorlage, MailvorlageAdmin)
admin.site.register(Kommentar, KommentarAdmin)
admin.site.register(Tutor, TutorAdmin)
admin.site.register(BarcodeScannEvent, BarcodeScannEventAdmin)
admin.site.register(BarcodeScanner, BarcodeScannerAdmin)
admin.site.register(Fachgebiet, FachgebietAdmin)
<commit_msg>Changes at admin.py to fix issue 112, not working right now<commit_after># coding=utf-8

from django.contrib import admin
from django import forms
from django.http.response import HttpResponseRedirect
from django.shortcuts import render

from feedback.models import Person, Veranstaltung, Semester, Einstellung, \
    Mailvorlage, Kommentar, Tutor, BarcodeScanner, BarcodeScannEvent, BarcodeAllowedState
from feedback.models.base import Log, Fachgebiet, FachgebietEmail

class PersonAdmin(admin.ModelAdmin):
    """Admin View für Personen"""
    list_display = ('__str__', 'email', 'fachgebiet')
    search_fields = ['vorname', 'nachname', 'email', ]
    list_filter = ('fachgebiet',)

    class FachgebietZuweisenForm(forms.Form):
        """Form für die Zuweisung von einem Fachgebiet für eine Person."""
        _selected_action = forms.CharField(widget=forms.MultipleHiddenInput)

    def assign_fachgebiet_action(self, request, queryset):
        """Definiert eine Admin-Action für die Fachgebietzuweisung."""
        form = None
        suggestion_list = []

        for p in queryset:
            proposed_fachgebiet = FachgebietEmail.get_fachgebiet_from_email(p.email)
            suggestion_list.append((p, proposed_fachgebiet))

        if any(s in request.POST for s in ('apply', 'save')):
            form = self.FachgebietZuweisenForm(request.POST)

            if form.is_valid():
                selected_persons = request.POST.getlist("selectedPerson")
                for person in queryset:
                    person_id_str = str(person.id)
                    if person_id_str in selected_persons:
                        proposed_fachgebiet_id = request.POST.get("fachgebiet_" + person_id_str, 0)
                        if int(proposed_fachgebiet_id) > 0:
                            proposed_fachgebiet = Fachgebiet.objects.get(id=proposed_fachgebiet_id)
                            person.fachgebiet = proposed_fachgebiet
                            person.save()
                            suggestion_list = [(x, y) for x, y in suggestion_list if x is not person]

                self.message_user(request, "Fachgebiete erfolgreich zugewiesen.")

                if ('save' in request.POST) or not suggestion_list:
                    return HttpResponseRedirect(request.get_full_path())

        if not form:
            form = self.FachgebietZuweisenForm(initial={
                '_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
            })

        return render(request, 'admin/fachgebiet.html', {'data': suggestion_list, 'fachgebiet': form, })

    assign_fachgebiet_action.short_description = "Einem Fachgebiet zuweisen"
    actions = [assign_fachgebiet_action]

class LogInline(admin.TabularInline):
    """Admin View für Log"""
    model = Log
    readonly_fields = ('veranstaltung', 'user', 'scanner', 'timestamp', 'status', 'interface')
    can_delete = False

    def has_add_permission(self, request, obj=None):
        return False

class VeranstaltungAdmin(admin.ModelAdmin):
    """Admin View für Veranstaltung"""
    fieldsets = [
        ('Stammdaten', {'fields':
                            ['typ', 'name', 'semester', 'status', 'lv_nr', 'grundstudium', 'evaluieren',
                             'veranstalter', 'link_veranstalter',
                             ]}),
        ('Bestellung', {'fields': ['sprache', 'anzahl', 'verantwortlich', 'ergebnis_empfaenger', 'primaerdozent',
                                   'auswertungstermin', 'freiefrage1', 'freiefrage2', 'kleingruppen', ]}),
    ]
    list_display = ('typ', 'name', 'semester', 'grundstudium', 'evaluieren', 'anzahl',
                    'sprache', 'status', 'veranstalter_list')
    list_display_links = ['name']
    list_filter = ('typ', 'semester', 'status', 'grundstudium', 'evaluieren', 'sprache')
    search_fields = ['name']
    filter_horizontal = ('veranstalter', 'ergebnis_empfaenger')  # @see http://stackoverflow.com/a/5386871
    readonly_fields = ('link_veranstalter',)
    inlines = [LogInline, ]

    def save_model(self, request, obj, form, change):
        """Definiert eine Post-Save Operation."""
        super(VeranstaltungAdmin, self).save_model(request, obj, form, change)
        for changed_att in form.changed_data:
            # Wenn sich der Status ändert, wird es geloggt.
            if changed_att == "status":
                obj.log(request.user)

    class StatusAendernForm(forms.Form):
        """Definiert eine Form für Änderung einen Status'."""
        _selected_action = forms.CharField(widget=forms.MultipleHiddenInput)
        status = forms.ChoiceField(choices=Veranstaltung.STATUS_CHOICES)

    def status_aendern_action(self, request, queryset):
        """Beschreibt eine Admin-Action für die Statusänderung."""
        form = None

        if 'apply' in request.POST:
            form = self.StatusAendernForm(request.POST)

            if form.is_valid():
                status = form.cleaned_data['status']

                queryset.update(status=status)
                for veranstaltung in queryset:
                    veranstaltung.log(request.user)

                self.message_user(request, "Status erfolgreich geändert.")
                return HttpResponseRedirect(request.get_full_path())

        if not form:
            form = self.StatusAendernForm(initial={'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME)})

        return render(request, 'admin/status_aendern.html', {'veranstaltungen': queryset, 'status': form, })

    status_aendern_action.short_description = "Ändere den Status einer Veranstaltung"

    class KeineEvaluationForm(forms.Form):
        _selected_action = None

    def keine_evaluation_action(self, request, queryset):
        """Beschreibt eine Admin-Action für die Option keine Evaluation."""
        form = None
        print(request.POST)
        if 'apply' not in request.POST:     #Dieser Teil reicht bereits zum ändern aus. In diesem Fall können auch Zeile 146-149 gelöscht werden (Kein Bestätigungsfenster erscheint.
            form = self.KeineEvaluationForm(request.POST)
            if form.is_valid():
                queryset.update(status=Veranstaltung.STATUS_KEINE_EVALUATION_FINAL)
                queryset.update(evaluieren=False)
                for veranstaltung in queryset:
                    veranstaltung.log(request.user)

                self.message_user(request, "Keine Evaluation erfolgreich gesetzt.")
                return HttpResponseRedirect(request.get_full_path())

        if not form:
            form = self.KeineEvaluationForm(initial={'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME)})
            #nach dem return landet Python in status_aendern_action
        return render(request, 'admin/keine_evaluation.html', {'veranstaltungen': queryset, 'status': form, })

    keine_evaluation_action.short_description = "Keine Evaluation für diese Veranstaltung"

    actions = [status_aendern_action,keine_evaluation_action]

class SemesterAdmin(admin.ModelAdmin):
    """Admin View für Semester"""
    list_display = ('__str__', 'sichtbarkeit', 'fragebogen')
    list_filter = ('sichtbarkeit', 'fragebogen')
    ordering = ('-semester',)

class EinstellungAdmin(admin.ModelAdmin):
    """Admin View für Einstellung"""
    list_display = ('name', 'wert')
    list_editable = ('wert',)

class MailvorlageAdmin(admin.ModelAdmin):
    """Admin View für Mailvorlage"""
    list_display = ('subject',)

class KommentarAdmin(admin.ModelAdmin):
    """Admin View für Kommentar"""
    list_display = ('typ', 'name', 'semester', 'autor')
    list_display_links = ('name',)

class TutorAdmin(admin.ModelAdmin):
    """Admin View für Tutor"""
    fieldsets = [
        ('Stammdaten', {'fields':
                            ['vorname', 'nachname', 'email',
                             ]}),
        ('Lehrveranstaltung', {'fields':
                                   ['veranstaltung', 'nummer', 'anmerkung'
                                    ]}),
    ]
    list_display = ('vorname', 'nachname', 'nummer', 'veranstaltung')
    search_fields = ('vorname', 'nachname')
    ordering = ('veranstaltung', 'nummer')

    def render_change_form(self, request, context, *args, **kwargs):
        # Limit Auswahl zum aktuellen Semester
        context['adminform'].form.fields['veranstaltung'].queryset = Veranstaltung.objects.filter(
            semester=Semester.current())
        return super(TutorAdmin, self).render_change_form(request, context, args, kwargs)

class BarcodeScannEventAdmin(admin.ModelAdmin):
    """Admin View für BarcodeScannEvent"""
    list_display = ('veranstaltung', 'timestamp',)
    readonly_fields = ('veranstaltung', 'timestamp',)

class BarcodeAllowedStateInline(admin.TabularInline):
    """Admin View für BarcodeAllowedState"""
    model = BarcodeAllowedState

class BarcodeScannerAdmin(admin.ModelAdmin):
    """Admin View für BarcodeScanner"""
    inlines = [
        BarcodeAllowedStateInline,
    ]
    list_display = ('token', 'description')

class FachgebietEmailAdminInline(admin.TabularInline):
    """Admin View für FachgebietEmail"""
    model = FachgebietEmail
    extra = 1

class FachgebietAdmin(admin.ModelAdmin):
    """Admin View für Fachgebiet"""
    list_display = ('name', 'kuerzel')
    list_display_links = ('name',)
    inlines = (FachgebietEmailAdminInline,)

    def save_related(self, request, form, formsets, change):
        super(FachgebietAdmin, self).save_related(request, form, formsets, change)

        if change:
            count_added = 0
            for formset in formsets:
                if formset.extra_forms:
                    for new_forms in formset.extra_forms:
                        if new_forms.instance:
                            fachgebiet_email_instance = new_forms.instance
                            fachgebiet_suffix = fachgebiet_email_instance.email_suffix
                            if fachgebiet_suffix:
                                persons = Person.objects.filter(fachgebiet=None)

                                for person in persons:
                                    proposed_fachgebiet = FachgebietEmail.get_fachgebiet_from_email(person.email)
                                    if proposed_fachgebiet \
                                            and proposed_fachgebiet.id == fachgebiet_email_instance.fachgebiet_id:
                                        person.fachgebiet = proposed_fachgebiet
                                        person.save()
                                        count_added += 1

            if count_added > 0:
                self.message_user(request, "Dieser Fachgebiet wurde {0} Personen zugeordnet".format(count_added))

admin.site.register(Person, PersonAdmin)
admin.site.register(Veranstaltung, VeranstaltungAdmin)
admin.site.register(Semester, SemesterAdmin)
admin.site.register(Einstellung, EinstellungAdmin)
admin.site.register(Mailvorlage, MailvorlageAdmin)
admin.site.register(Kommentar, KommentarAdmin)
admin.site.register(Tutor, TutorAdmin)
admin.site.register(BarcodeScannEvent, BarcodeScannEventAdmin)
admin.site.register(BarcodeScanner, BarcodeScannerAdmin)
admin.site.register(Fachgebiet, FachgebietAdmin)
<|endoftext|>


<commit_before># coding=utf-8

from django import forms
from django.forms import widgets

from feedback.models import Person, Veranstaltung, Kommentar, BarcodeScannEvent
from django.core.exceptions import ValidationError

class VeranstaltungEvaluationForm(forms.ModelForm):
    """Definiert die Form für den 1. Schritt des Wizards"""
    required_css_class = 'required'

    class Meta:
        model = Veranstaltung
        fields = ('evaluieren',)
        widgets = {'evaluieren': forms.RadioSelect}

    def __init__(self, *args, **kwargs):
        super(VeranstaltungEvaluationForm, self).__init__(*args, **kwargs)

        for k, field in list(self.fields.items()):
            field.required = True

class VeranstaltungBasisdatenForm(forms.ModelForm):
    """Definiert die Form für den 2. Schritt des Wizards."""
    required_css_class = 'required'

    def __init__(self, *args, **kwargs):
        veranstalter_queryset = kwargs.pop('all_veranstalter', None)

        super(VeranstaltungBasisdatenForm, self).__init__(*args, **kwargs)

        # Schränke QuerySet nur auf den Veranstalter ein
        self.fields['verantwortlich'].queryset = veranstalter_queryset
        self.fields['ergebnis_empfaenger'].queryset = veranstalter_queryset

        # Keine negative Anzahl möglich
        self.fields['anzahl'] = forms.IntegerField(min_value=1)

        # Nutze ein Widget bei dem nur das jahr des letzten Auswertungstermins angegeben werden kann
        years_tuple = kwargs['instance'].semester.auswertungstermin_years()
        self.fields['auswertungstermin'].widget = widgets.SelectDateWidget(years=years_tuple)

        # Auswertungstermin kann nur gewählt werden wenn es ein Seminar oder Praktikum ist
        if kwargs['instance'].typ not in ['se', 'pr']:
            del self.fields['auswertungstermin']

        # Lösche die Auswahl ob es eine Übung gibt wenn es keine Vorlesung ist
        vltypes = ['vu', 'v']
        if kwargs['instance'].typ not in vltypes:
            del self.fields['typ']
        else:
            choices = []
            for cur in self.fields['typ'].choices:
                if cur[0] in vltypes:
                    choices.append(cur)

            self.fields['typ'].choices = choices

        # Wenn Evaluation oder Vollerhebung, dann sind alle anderen Felder notwendig
        for k, field in list(self.fields.items()):
            field.required = True

    class Meta:
        model = Veranstaltung
        fields = ('typ', 'anzahl', 'sprache', 'verantwortlich', 'ergebnis_empfaenger', 'auswertungstermin')
        widgets = {'ergebnis_empfaenger': forms.CheckboxSelectMultiple,
                   'auswertungstermin': forms.SelectDateWidget}

class VeranstaltungPrimaerDozentForm(forms.ModelForm):
    """Definiert die Form für den 3. Schritt des Wizards."""
    required_css_class = 'required'

    def __init__(self, *args, **kwargs):
        if kwargs.pop("is_dynamic_form", False):
            super(VeranstaltungPrimaerDozentForm, self).__init__(*args, **kwargs)
        else:
            previous_step_data = kwargs.pop('basisdaten', None)
            super(VeranstaltungPrimaerDozentForm, self).__init__(*args, **kwargs)
            if previous_step_data is not None:
                self.fields['primaerdozent'].queryset = previous_step_data.get('ergebnis_empfaenger', None)
                self.fields['primaerdozent'].required = True

    class Meta:
        model = Veranstaltung
        fields = ('primaerdozent',)

class VeranstaltungDozentDatenForm(forms.ModelForm):
    """Definiert die Form für den 4. Schritt des Wizards."""
    required_css_class = 'required'

    def __init__(self, *args, **kwargs):
        super(VeranstaltungDozentDatenForm, self).__init__(*args, **kwargs)

        for k, field in list(self.fields.items()):
            field.required = True

    class Meta:
        model = Person
        fields = ('anschrift', 'email')

class VeranstaltungFreieFragen(forms.ModelForm):
    """Definiert die Form für den 5. Schritt des Wizards."""
    required_css_class = 'required'

    class Meta:
        model = Veranstaltung
        fields = ('freiefrage1', 'freiefrage2')

class VeranstaltungTutorenForm(forms.Form):
    """Definiert die Form für den 6. Schritt des Wizards."""
    required_css_class = 'required'

    csv_tutoren = forms.CharField(label='CSV', widget=forms.Textarea, required=False)

    def __init__(self, *args, **kwargs):
        preset_csv = kwargs.pop("preset_csv", None)
        super(VeranstaltungTutorenForm, self).__init__(*args, **kwargs)
        self.fields["csv_tutoren"].initial = preset_csv

class VeranstaltungVeroeffentlichung(forms.ModelForm):
    """Definiert die Form für den 7. Schritt des Wizards."""
    required_css_class = 'required'

    class Meta:
        model = Veranstaltung
        fields = ('veroeffentlichen',)

class UploadFileForm(forms.Form):
    """Definiert die Form für den XML Import."""
    file = forms.FileField(label='Datei')

class PersonForm(forms.ModelForm):
    """Definiert die Form für die Bearbeitung von Personen."""
    class Meta:
        model = Person
        fields = ('geschlecht', 'email')

    def clean(self):
        geschlecht = self.cleaned_data.get('geschlecht')
        email = self.cleaned_data.get('email')

        if not geschlecht or not email:
            raise forms.ValidationError('Das Feld für die Anrede oder Email ist leer.')

class PersonUpdateForm(forms.ModelForm):
    """Definiert die Form für die Nachpflege von Personendaten"""
    class Meta:
        model = Person
        fields = ('anschrift', 'fachgebiet')

class KommentarModelForm(forms.ModelForm):
    """Definiert die Form für Kommentare."""
    def __init__(self, *args, **kwargs):
        veranst = kwargs.pop('veranstaltung', None)

        if veranst is None:
            raise KeyError('This form needs an veranstaltung=... parameter to function properly.')

        super(KommentarModelForm, self).__init__(*args, **kwargs)
        self.fields['autor'].queryset = veranst.veranstalter.all()

    class Meta:
        model = Kommentar
        exclude = ('veranstaltung',)

CLOSE_ORDER_CHOICES = (
    ('ja', 'Ja'),
    ('nein', 'Nein')
)

class CloseOrderForm(forms.Form):
    """Definiert die Form für das Beenden der Bestellphase"""
    auswahl = forms.ChoiceField(choices=CLOSE_ORDER_CHOICES)

class CreateBarcodeScannEventForm(forms.ModelForm):
    """Definiert die Form für einen Barcodescan-Event"""
    scanner_token = forms.CharField()

    class Meta:
        model = BarcodeScannEvent
        fields = ['barcode', 'scanner']

    def clean(self):
        super(CreateBarcodeScannEventForm, self).clean()
        cd = self.cleaned_data

        if cd['scanner'].token != cd['scanner_token']:
            raise ValidationError(ValidationError('Token dose not match', code='tokendmatch'))
        else:
            barcode_decoded = Veranstaltung.decode_barcode(cd['barcode'])
            cd['veranstaltung'] = barcode_decoded['veranstaltung']

            if barcode_decoded['tutorgroup'] >= 1:
                cd['tutorgroup'] = barcode_decoded['tutorgroup']

        return cd

<commit_msg>refactor wizard forms<commit_after># coding=utf-8

from django import forms
from django.forms import widgets

from feedback.models import Person, Veranstaltung, Kommentar, BarcodeScannEvent
from django.core.exceptions import ValidationError

class BestellWizardForm(forms.ModelForm):
    required_css_class = 'required'

class VeranstaltungEvaluationForm(BestellWizardForm):
    """Definiert die Form für den 1. Schritt des Wizards"""

    class Meta:
        model = Veranstaltung
        fields = ('evaluieren',)
        widgets = {'evaluieren': forms.RadioSelect}

    def __init__(self, *args, **kwargs):
        super(VeranstaltungEvaluationForm, self).__init__(*args, **kwargs)

        for k, field in list(self.fields.items()):
            field.required = True

class VeranstaltungBasisdatenForm(BestellWizardForm):
    """Definiert die Form für den 2. Schritt des Wizards."""

    def __init__(self, *args, **kwargs):
        veranstalter_queryset = kwargs.pop('all_veranstalter', None)

        super(VeranstaltungBasisdatenForm, self).__init__(*args, **kwargs)

        # Schränke QuerySet nur auf den Veranstalter ein
        self.fields['verantwortlich'].queryset = veranstalter_queryset
        self.fields['ergebnis_empfaenger'].queryset = veranstalter_queryset

        # Keine negative Anzahl möglich
        self.fields['anzahl'] = forms.IntegerField(min_value=1)

        # Nutze ein Widget bei dem nur das jahr des letzten Auswertungstermins angegeben werden kann
        years_tuple = kwargs['instance'].semester.auswertungstermin_years()
        self.fields['auswertungstermin'].widget = widgets.SelectDateWidget(years=years_tuple)

        # Auswertungstermin kann nur gewählt werden wenn es ein Seminar oder Praktikum ist
        if kwargs['instance'].typ not in ['se', 'pr']:
            del self.fields['auswertungstermin']

        # Lösche die Auswahl ob es eine Übung gibt wenn es keine Vorlesung ist
        vltypes = ['vu', 'v']
        if kwargs['instance'].typ not in vltypes:
            del self.fields['typ']
        else:
            choices = []
            for cur in self.fields['typ'].choices:
                if cur[0] in vltypes:
                    choices.append(cur)

            self.fields['typ'].choices = choices

        # Wenn Evaluation oder Vollerhebung, dann sind alle anderen Felder notwendig
        for k, field in list(self.fields.items()):
            field.required = True

    class Meta:
        model = Veranstaltung
        fields = ('typ', 'anzahl', 'sprache', 'verantwortlich', 'ergebnis_empfaenger', 'auswertungstermin')
        widgets = {'ergebnis_empfaenger': forms.CheckboxSelectMultiple,
                   'auswertungstermin': forms.SelectDateWidget}

class VeranstaltungPrimaerDozentForm(BestellWizardForm):
    """Definiert die Form für den 3. Schritt des Wizards."""

    def __init__(self, *args, **kwargs):
        if kwargs.pop("is_dynamic_form", False):
            super(VeranstaltungPrimaerDozentForm, self).__init__(*args, **kwargs)
        else:
            previous_step_data = kwargs.pop('basisdaten', None)
            super(VeranstaltungPrimaerDozentForm, self).__init__(*args, **kwargs)
            if previous_step_data is not None:
                self.fields['primaerdozent'].queryset = previous_step_data.get('ergebnis_empfaenger', None)
                self.fields['primaerdozent'].required = True

    class Meta:
        model = Veranstaltung
        fields = ('primaerdozent',)

class VeranstaltungDozentDatenForm(BestellWizardForm):
    """Definiert die Form für den 4. Schritt des Wizards."""

    def __init__(self, *args, **kwargs):
        super(VeranstaltungDozentDatenForm, self).__init__(*args, **kwargs)

        for k, field in list(self.fields.items()):
            field.required = True

    class Meta:
        model = Person
        fields = ('anschrift', 'email')

class VeranstaltungFreieFragen(BestellWizardForm):
    """Definiert die Form für den 5. Schritt des Wizards."""

    class Meta:
        model = Veranstaltung
        fields = ('freiefrage1', 'freiefrage2')

class VeranstaltungTutorenForm(forms.Form):
    """Definiert die Form für den 6. Schritt des Wizards."""
    required_css_class = 'required'

    csv_tutoren = forms.CharField(label='CSV', widget=forms.Textarea, required=False)

    def __init__(self, *args, **kwargs):
        preset_csv = kwargs.pop("preset_csv", None)
        super(VeranstaltungTutorenForm, self).__init__(*args, **kwargs)
        self.fields["csv_tutoren"].initial = preset_csv

class VeranstaltungVeroeffentlichung(BestellWizardForm):
    """Definiert die Form für den 7. Schritt des Wizards."""

    class Meta:
        model = Veranstaltung
        fields = ('veroeffentlichen',)

class UploadFileForm(forms.Form):
    """Definiert die Form für den XML Import."""
    file = forms.FileField(label='Datei')

class PersonForm(forms.ModelForm):
    """Definiert die Form für die Bearbeitung von Personen."""
    class Meta:
        model = Person
        fields = ('geschlecht', 'email')

    def clean(self):
        geschlecht = self.cleaned_data.get('geschlecht')
        email = self.cleaned_data.get('email')

        if not geschlecht or not email:
            raise forms.ValidationError('Das Feld für die Anrede oder Email ist leer.')

class PersonUpdateForm(forms.ModelForm):
    """Definiert die Form für die Nachpflege von Personendaten"""
    class Meta:
        model = Person
        fields = ('anschrift', 'fachgebiet')

class KommentarModelForm(forms.ModelForm):
    """Definiert die Form für Kommentare."""
    def __init__(self, *args, **kwargs):
        veranst = kwargs.pop('veranstaltung', None)

        if veranst is None:
            raise KeyError('This form needs an veranstaltung=... parameter to function properly.')

        super(KommentarModelForm, self).__init__(*args, **kwargs)
        self.fields['autor'].queryset = veranst.veranstalter.all()

    class Meta:
        model = Kommentar
        exclude = ('veranstaltung',)

CLOSE_ORDER_CHOICES = (
    ('ja', 'Ja'),
    ('nein', 'Nein')
)

class CloseOrderForm(forms.Form):
    """Definiert die Form für das Beenden der Bestellphase"""
    auswahl = forms.ChoiceField(choices=CLOSE_ORDER_CHOICES)

class CreateBarcodeScannEventForm(forms.ModelForm):
    """Definiert die Form für einen Barcodescan-Event"""
    scanner_token = forms.CharField()

    class Meta:
        model = BarcodeScannEvent
        fields = ['barcode', 'scanner']

    def clean(self):
        super(CreateBarcodeScannEventForm, self).clean()
        cd = self.cleaned_data

        if cd['scanner'].token != cd['scanner_token']:
            raise ValidationError(ValidationError('Token dose not match', code='tokendmatch'))
        else:
            barcode_decoded = Veranstaltung.decode_barcode(cd['barcode'])
            cd['veranstaltung'] = barcode_decoded['veranstaltung']

            if barcode_decoded['tutorgroup'] >= 1:
                cd['tutorgroup'] = barcode_decoded['tutorgroup']

        return cd

<|endoftext|>