Ejemplo n.º 1
0
    def test_mutliple_groups(self):
        """
        https://github.com/betagouv/peps/issues/16
        We need to ensure no practices belonging to the same practice group
        are selected.
        """
        rumex = Weed.objects.filter(display_text='Rumex').first()
        chardon = Weed.objects.filter(
            display_text='Chardon des champs').first()

        answers = {
            "problem":
            "GLYPHOSATE",
            "glyphosate":
            "VIVACES,COUVERTS",
            "weeds":
            "{0},{1}".format(str(chardon.external_id), str(rumex.external_id)),
            "tillage":
            "TRAVAIL_PROFOND",
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        suggestions = engine.get_suggestions(results)
        suggested_groups = []
        for practice in map(lambda x: x.practice, suggestions):
            practice_groups = list(practice.practice_groups.all())
            for group in practice_groups:
                self.assertNotIn(group, suggested_groups)
                suggested_groups.append(group)
Ejemplo n.º 2
0
    def test_pests_problem_type(self):
        """
        If the user says their problem are weeds (adventices), the three suggestions
        must address weeds.
        """
        ble = SimulatorCulture.objects.filter(display_text='Blé dur').first()
        mais = SimulatorCulture.objects.filter(display_text='Maïs').first()
        chanvre = SimulatorCulture.objects.filter(
            display_text='Chanvre').first()

        answers = {
            "problem": "DESHERBAGE",
            "rotation":
            [ble.external_id, chanvre.external_id, mais.external_id]
        }
        engine = Engine(answers, [], [])
        practices = engine.calculate_results()
        response_items = engine.get_suggestions(practices)

        # We ensure there are three suggestions, and all three address weeds
        self.assertEqual(len(response_items), 3)
        for response_item in response_items:
            suggestion = response_item.practice
            self.assertIn(Problem['DESHERBAGE'].value,
                          suggestion.problems_addressed)
Ejemplo n.º 3
0
    def test_weed_fields(self):
        """
        Three form fields can contain weed information: weeds, perennials
        and weedsGlyphosate. All must be treated the same way by the engine.
        """
        practice_title = "Faucher une culture fourragère"
        rumex = Weed.objects.filter(display_text='Rumex').first()
        chardon = Weed.objects.filter(
            display_text='Chardon des champs').first()
        ble = SimulatorCulture.objects.filter(display_text='Blé dur').first()

        answers = {
            "problem":
            "DESHERBAGE",
            "weeds":
            "{0},{1}".format(str(chardon.external_id), str(rumex.external_id)),
            "rotation": [ble.external_id],
            "cattle":
            "Oui"
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result_weeds = next(
            filter(lambda x: x.practice.title == practice_title, results))

        answers = {
            "problem":
            "DESHERBAGE",
            "perennials":
            "{0},{1}".format(str(chardon.external_id), str(rumex.external_id)),
            "rotation": [ble.external_id],
            "cattle":
            "Oui"
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result_perennials = next(
            filter(lambda x: x.practice.title == practice_title, results))

        self.assertEqual(result_weeds.weight, result_perennials.weight)

        answers = {
            "problem":
            "DESHERBAGE",
            "weedsGlyphosate":
            "{0},{1}".format(str(chardon.external_id), str(rumex.external_id)),
            "rotation": [ble.external_id],
            "cattle":
            "Oui"
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result_weeds_glypho = next(
            filter(lambda x: x.practice.title == practice_title, results))

        self.assertEqual(result_weeds.weight, result_weeds_glypho.weight)
Ejemplo n.º 4
0
    def test_suggestion_rankings(self):
        """
        We check that the weight of the proposed suggestions is correct
        """
        answers = {
            "problem": "MALADIES_FONGIQUES",
            "rotation": [],
            "department": "01"
        }
        engine = Engine(answers, [], [])
        practices = engine.calculate_results()
        suggestions = engine.get_suggestions(practices)

        # There should be two practices with weight 1.5
        self.assertEqual(len(suggestions), 3)
        weights = list(map(lambda x: x.weight, suggestions))
        self.assertEqual(len(list(filter(lambda x: x == 1.5, weights))), 2)
Ejemplo n.º 5
0
    def test_culture_multipliers(self):
        """
        A practice can be more (or less) useful to address certain cultures, this
        is specified in the culture_multipliers field of the practice model.
        The practice "Semer l'inter-rang pour réduire la place disponible aux adventices"
        has a multiplier for the culture COLZA, here we check that said
        multiplier is taken into account by the engine.
        """
        practice_title = "Semer l'inter-rang pour réduire la place disponible aux adventices"
        colza = SimulatorCulture.objects.filter(display_text='Colza').first()
        colza_multiplier = 1.2

        # First we check the weignt without using COLZA in the response
        answers = {"problem": "DESHERBAGE", "rotation": []}
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        initial_weight = result.weight

        # Now we add COLZA in the response and get the results
        answers = {"problem": "DESHERBAGE", "rotation": [colza.external_id]}
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        new_weight = result.weight

        # We need to make sure the new weight has taken into account the
        # multiplier for COLZA
        self.assertEqual(new_weight, initial_weight * colza_multiplier)
Ejemplo n.º 6
0
    def test_problem_glyphosate(self):
        """
        If a user chooses glyphosate as their problem, the practices
        that target glyphosate should have a higher score. An exemple of
        these practices is:
        "Défanner les pommes des terre avec un produit de biocontrôle"
        """
        practice_title = 'Défanner les pommes des terre avec un produit de biocontrôle'
        pomme_de_terre = SimulatorCulture.objects.filter(
            display_text='Pomme de terre').first()

        # First we make a request without specifying glyphosate as the main problem
        answers = {
            "problem": "DESHERBAGE",
            "rotation": [pomme_de_terre.external_id]
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        initial_weight = result.weight

        # First we make a request without specifying glyphosate as the main problem
        answers = {
            "problem": "GLYPHOSATE",
            "rotation": [pomme_de_terre.external_id]
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertGreater(result.weight, 0)
        self.assertGreater(result.weight, initial_weight)
Ejemplo n.º 7
0
    def test_cultures_weight(self):
        """
        If the user is already growing a culture, like Chanvre in this example,
        the engine should set practices introducing to that culture to zero.
        """
        practice_title = 'Introduire le chanvre dans la rotation'
        chanvre = SimulatorCulture.objects.filter(
            display_text='Chanvre').first()

        # If we have a problem with weeds we expect to have the chanvre practice
        # with a ranking above 0
        answers = {"problem": "DESHERBAGE", "department": "01"}
        engine = Engine(answers, [], [])
        practices = engine.calculate_results()
        chanvre_practice = next(
            filter(lambda x: x.practice.title == practice_title, practices))

        self.assertGreater(chanvre_practice.weight, 0)

        # However, if the user says they already have chanvre in their rotation,
        # the same practice will now have 0 as weight
        answers = {
            "problem": "DESHERBAGE",
            "rotation": [chanvre.external_id],
            "department": "01"
        }
        engine = Engine(answers, [], [])
        practices = engine.calculate_results()
        chanvre_practice = next(
            filter(lambda x: x.practice.title == practice_title, practices))
        self.assertEqual(chanvre_practice.weight, 0.0)
Ejemplo n.º 8
0
    def test_pest_whitelist(self):
        """
        A practice can have a limited number of whitelisted pests it can be applied
        to. As an example, practice "Lutter contre la pyrale du maïs au moyen de
        lâchers de trichogrammes" can only be relevant for pyrale.
        """
        practice_title = "Lutter contre la pyrale du maïs au moyen de lâchers de trichogrammes"
        pyrales = Pest.objects.filter(display_text='Pyrales').first()
        mais = SimulatorCulture.objects.filter(display_text='Maïs').first()

        # First we check the weignt without using PYRALES. We expect the weight to be
        # zero since the whitelist is not upheld
        answers = {
            "problem": "RAVAGEURS",
            "rotation": [mais.external_id],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertEqual(result.weight, 0)

        # Now we add PYRALES. The same practice should have a non-zero weight
        answers = {
            "problem": "RAVAGEURS",
            "pests": "{0}".format(pyrales.external_id),
            "rotation": [mais.external_id],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertTrue(result.weight > 0)
Ejemplo n.º 9
0
    def test_blacklist_types(self):
        """
        It is possible to blacklist entire practice types. This test
        ensures that practices belonging to blacklisted practice types
        have a score of zero.
        """
        ble = SimulatorCulture.objects.filter(display_text='Blé dur').first()
        mais = SimulatorCulture.objects.filter(display_text='Maïs').first()
        chanvre = SimulatorCulture.objects.filter(
            display_text='Chanvre').first()

        # We make a call to get suggestions
        answers = {
            "problem": "DESHERBAGE",
            "rotation":
            [ble.external_id, chanvre.external_id, mais.external_id]
        }
        engine = Engine(answers, [], [])
        practices = engine.calculate_results()
        response_items = engine.get_suggestions(practices)

        # We get the first suggestion's practice type. We will blacklist it
        # later and ensure no practices of the same type are proposed, and that
        # they are all set to zero.
        blacklisted_practice_type = str(
            list(response_items[0].practice.types.all())[0].id)
        engine = Engine(answers, [], [blacklisted_practice_type])
        practices = engine.calculate_results()
        response_items = engine.get_suggestions(practices)

        # Now let's verify that all the practices belonging to that type
        # have a score of zero.
        for practice_item in practices:
            practice_types_ids = list(
                map(lambda x: str(x.id), practice_item.practice.types.all()))
            if blacklisted_practice_type in practice_types_ids:
                self.assertEqual(practice_item.weight, 0.0)
Ejemplo n.º 10
0
    def test_blacklist_practices(self):
        """
        It is possible to blacklist individual practices, this test
        ensures that blacklisted practices end up with a score of zero.
        """
        ble = SimulatorCulture.objects.filter(display_text='Blé dur').first()
        mais = SimulatorCulture.objects.filter(display_text='Maïs').first()
        chanvre = SimulatorCulture.objects.filter(
            display_text='Chanvre').first()

        # We make a call to get suggestions
        answers = {
            "problem": "DESHERBAGE",
            "rotation":
            [ble.external_id, chanvre.external_id, mais.external_id]
        }
        engine = Engine(answers, [], [])
        practices = engine.calculate_results()
        response_items = engine.get_suggestions(practices)

        # We get the first suggestion - we will blacklist it later and ensure
        # it is no longer proposed.
        blacklisted_suggestion_id = str(response_items[0].practice.id)
        engine = Engine(answers, [blacklisted_suggestion_id], [])
        practices = engine.calculate_results()
        response_items = engine.get_suggestions(practices)

        # Now let's verify that the suggestions no longer include the
        # blacklisted practice
        suggested_ids = list(map(lambda x: str(x.practice.id), response_items))
        self.assertNotIn(blacklisted_suggestion_id, suggested_ids)

        # The blacklisted practice should have a score of zero
        blacklisted_response_item = next(
            filter(lambda x: str(x.practice.id) == blacklisted_suggestion_id,
                   practices))
        self.assertEqual(blacklisted_response_item.weight, 0.0)
Ejemplo n.º 11
0
    def test_balanced_rotation(self):
        """
        Summer cultures should be counted with the automn ones when
        calculating the balance of the rotation.
        """
        # Printemps
        betterave = 'recKDNdfSiV33djzf'
        pomme_de_terre = 'recURJ9JQS9u6OHva'
        orge_printemps = 'recEEz6LZ3MRDdV99'

        # Automne
        ble_hiver = 'recmm8lo1bGXCYSA3'

        # Summer
        colza = 'recZj4cTO0dwcYhbe'

        # This practice balances the sowing period and should be proposed when having an unbalanced rotation
        practice_name = "Favoriser l'alternance de cultures à semis de printemps et d'automne"

        # In this example, all cultures are spring cultures, so it should be unbalanced
        answers = {
            "problem": "DESHERBAGE",
            "tillage": "TRAVAIL_PROFOND",
            "rotation": [betterave, pomme_de_terre, orge_printemps],
        }
        engine = Engine(answers, [], [])
        unbalanced_result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))

        # Now we balance it out, having 3 spring, 1 automn and 1 summer (automn and
        # summer count together)
        answers = {
            "problem":
            "DESHERBAGE",
            "tillage":
            "TRAVAIL_PROFOND",
            "rotation":
            [betterave, pomme_de_terre, orge_printemps, ble_hiver, colza],
        }
        engine = Engine(answers, [], [])
        balanced_result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))

        self.assertGreater(unbalanced_result.weight, balanced_result.weight)
Ejemplo n.º 12
0
    def test_large_rotation(self):
        """
        If there are more than six cultures in the rotation of a user, practices with
        added cultures should be handicaped.
        """
        practice_name = 'Faucher une culture fourragère'

        mais = 'recsPtaEneeYVoEWx'
        tournesol = 'rec5MHmc9xIgAg8ha'
        soja = 'recwHs4aAiZc9okg9'
        ble = 'recuVebqXEqCg8kK0'
        orge = 'recfGVtMZSz05Rfl8'
        ble_hiver = 'recmm8lo1bGXCYSA3'
        colza = 'recZj4cTO0dwcYhbe'
        rumex = 'rec2wnpJOAJzUFe5v'

        # When having 6 or less cultures, there is no handicap
        answers = {
            'problem': 'DESHERBAGE',
            'rotation': [mais, tournesol, ble, orge],
            'weeds': rumex,
            "cattle": "Oui",
        }
        engine = Engine(answers, [], [])
        result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))
        initial_weight = result.weight

        # When having 6 or more, a handicap of 0.9 will be applied to cultures that
        # add a new culture to the rotation
        answers = {
            'problem': 'DESHERBAGE',
            'rotation': [mais, tournesol, ble, orge, soja, ble_hiver, colza],
            'weeds': rumex,
            "cattle": "Oui",
        }
        engine = Engine(answers, [], [])
        result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))
        large_rotation_weight = result.weight

        self.assertEqual(initial_weight * 0.9, large_rotation_weight)
Ejemplo n.º 13
0
    def test_weed_whitelist(self):
        """
        A practice can have a limited number of whitelisted weeds it can be applied
        to. As an example, practice "Faucher une culture fourragère " can only be relevant
        for Rumex.
        """
        practice_title = "Faucher une culture fourragère"
        rumex = Weed.objects.filter(display_text='Rumex').first()
        chardon = Weed.objects.filter(
            display_text='Chardon des champs').first()
        ble = SimulatorCulture.objects.filter(display_text='Blé dur').first()

        # First we check the weignt without using RUMEX. We expect the weight to be
        # zero since the whitelist is not upheld
        answers = {
            "problem": "DESHERBAGE",
            "rotation": [ble.external_id],
            "cattle": "Oui"
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertEqual(result.weight, 0)

        # Now we add RUMEX. The same practice should have a non-zero weight
        answers = {
            "problem":
            "DESHERBAGE",
            "weeds":
            "{0},{1}".format(str(chardon.external_id), str(rumex.external_id)),
            "rotation": [ble.external_id],
            "cattle":
            "Oui"
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertTrue(result.weight > 0)
Ejemplo n.º 14
0
    def test_small_rotation(self):
        """
        If there are three or less cultures in the rotation practices that extend
        the rotation should be boosted by 1.1.
        """
        practice_name = 'Introduire une culture étouffant les adventices'
        mais = 'recsPtaEneeYVoEWx'
        tournesol = 'rec5MHmc9xIgAg8ha'
        soja = 'recwHs4aAiZc9okg9'
        orge = 'recfGVtMZSz05Rfl8'
        raygrass = 'recjzIBqwGkton9Ed'

        # When having more than 3 cultures, there is no bonus
        answers = {
            'problem': 'DESHERBAGE',
            'rotation': [mais, tournesol, soja, orge],
            'weeds': raygrass,
            "cattle": "Oui",
        }
        engine = Engine(answers, [], [])
        result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))
        initial_weight = result.weight

        # When having 3 or less cultures, the bonus is 1.1
        answers = {
            'problem': 'DESHERBAGE',
            'rotation': [mais, tournesol, soja],
            'weeds': raygrass,
            "cattle": "Oui",
        }
        engine = Engine(answers, [], [])
        result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))
        small_rotation_weight = result.weight

        self.assertEqual(initial_weight * 1.1, small_rotation_weight)
Ejemplo n.º 15
0
    def test_glyphosate_multiplier(self):
        """
        Certain practices have a specific multiplier depending on the use
        of glyphosate that the user has. For example, the practice "Déchaumages
        répétés" has a multiplier.
        """
        glyphosate_bonus = 1.4
        practice_title = 'Déchaumages répétés'
        rumex = Weed.objects.filter(display_text='Rumex').first()
        lin_hiver = SimulatorCulture.objects.filter(
            display_text='Lin hiver').first()

        # First we make a request without specifying the use of glyphosate
        answers = {
            "problem": "GLYPHOSATE",
            "weeds": "{0}".format(str(rumex.external_id)),
            "tillage": "TRAVAIL_PROFOND",
            "rotation": [lin_hiver.external_id],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        initial_weight = result.weight

        # First we make a request without specifying glyphosate as the main problem
        answers = {
            "problem": "GLYPHOSATE",
            "glyphosate": "VIVACES",
            "weeds": "{0}".format(str(rumex.external_id)),
            "tillage": "TRAVAIL_PROFOND",
            "rotation": [lin_hiver.external_id],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertGreater(result.weight, 0)
        self.assertEqual(result.weight, initial_weight * glyphosate_bonus)
Ejemplo n.º 16
0
    def test_pest_multipliers(self):
        """
        A practice can be more (or less) useful to address certain pests, this
        is specified in the pest_multipliers field of the practice model.
        The practice "Associer un colza avec un couvert de légumineuses compagnes"
        has a multiplier for the pest Charançon, here we check that said multiplier
        is taken into account by the engine.
        """
        practice_title = "Associer un colza avec un couvert de légumineuses compagnes"
        charancon = Pest.objects.filter(display_text='Charançons').first()
        colza = SimulatorCulture.objects.filter(display_text='Colza').first()
        charancon_multiplier = 1.21

        # First we check the weignt without using CHARANCONS in the response
        answers = {
            "problem": "RAVAGEURS",
            "rotation": [colza.external_id],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        initial_weight = result.weight

        # Now we add CHARANCONS in the response and get the results
        answers = {
            "problem": "RAVAGEURS",
            "pests": "{0}".format(str(charancon.external_id)),
            "rotation": [colza.external_id],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        new_weight = result.weight

        # We need to make sure the new weight has taken into account the
        # multiplier for CHARANCONS
        self.assertEqual(new_weight, initial_weight * charancon_multiplier)
Ejemplo n.º 17
0
    def test_livestock_need(self):
        """
        Certain practices can only be suggested when the user has livestock.
        If they don't, they should be set to zero.
        One example of this is "Faire pâturer les couverts et les repousses"
        """
        practice_title = 'Faire pâturer les couverts et les repousses'
        rumex = Weed.objects.filter(display_text='Rumex').first()
        chardon = Weed.objects.filter(
            display_text='Chardon des champs').first()

        # First we try with livestock, the score should be greater than zero
        answers = {
            "problem":
            "GLYPHOSATE",
            "weeds":
            "{0},{1}".format(str(rumex.external_id), str(chardon.external_id)),
            "cattle":
            "Oui",
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertGreater(result.weight, 0)

        # Now we try without cattle, the score should be zero
        answers = {
            "problem":
            "GLYPHOSATE",
            "weeds":
            "{0},{1}".format(str(chardon.external_id), str(rumex.external_id)),
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertEqual(result.weight, 0)
Ejemplo n.º 18
0
    def test_culture_whitelist(self):
        """
        A practice can have a limited number of whitelisted cultures it can be applied
        to. As an example, practice "Détruire les résidus de cannes de maïs" can only be
        relevant for MAIS.
        """
        practice_title = "Détruire les résidus de cannes de maïs"
        pyrales = Pest.objects.filter(display_text='Pyrales').first()
        ble = SimulatorCulture.objects.filter(display_text='Blé dur').first()
        mais = SimulatorCulture.objects.filter(display_text='Maïs').first()

        # First we check the weight without using MAIS. We expect the weight to be
        # zero since the whitelist is not upheld
        answers = {
            "problem": "RAVAGEURS",
            "pests": "{0}".format(str(pyrales.external_id)),
            "tillage": "TRAVAIL_DU_SOL",
            "rotation": [ble.external_id]
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertEqual(result.weight, 0)

        # Now we add PYRALES. The same practice should have a non-zero weight
        answers = {
            "problem": "RAVAGEURS",
            "pests": "{0}".format(str(pyrales.external_id)),
            "tillage": "TRAVAIL_DU_SOL",
            "rotation": [ble.external_id, mais.external_id]
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        self.assertTrue(result.weight > 0)
Ejemplo n.º 19
0
    def test_weed_multipliers(self):
        """
        A practice can be more (or less) useful to address certain weeds, this
        is specified in the weed_multipliers field of the practice model.
        The practice "Profiter de l'action des auxiliaires sur le puceron de l'épi"
        has a multiplier for the weed Chardon, here we check that said multiplier
        is taken into account by the engine.
        """
        practice_title = "Profiter de l'action des auxiliaires sur le puceron de l'épi"
        chardon_multiplier = 0.6
        chardon = Weed.objects.filter(
            display_text='Chardon des champs').first()
        ble = SimulatorCulture.objects.filter(display_text='Blé dur').first()

        # First we check the weignt without using CHARDON in the response
        answers = {"problem": "DESHERBAGE", "rotation": [ble.external_id]}
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        initial_weight = result.weight

        # Now we add CHARDON in the response and get the results
        answers = {
            "problem": "DESHERBAGE",
            "weeds": "{0}".format(str(chardon.external_id)),
            "rotation": [ble.external_id],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        result = next(
            filter(lambda x: x.practice.title == practice_title, results))
        new_weight = result.weight

        # We need to make sure the new weight has taken into account the
        # multiplier for CHARDON
        self.assertEqual(new_weight, initial_weight * chardon_multiplier)
Ejemplo n.º 20
0
    def test_unbalanced_rotation(self):
        """
        If the user has <=25% of cultures of either spring or fall, they will need
        to balance their rotation. We must propose practices that help balance this
        out.
        """
        # Spring cultures:
        mais = 'recsPtaEneeYVoEWx'
        tournesol = 'rec5MHmc9xIgAg8ha'
        soja = 'recwHs4aAiZc9okg9'

        # Fall cultures:
        ble = 'recuVebqXEqCg8kK0'
        orge = 'recfGVtMZSz05Rfl8'

        # Summer cultures
        colza = 'recZj4cTO0dwcYhbe'

        # This practice balances the sowing period and should be proposed when having an unbalanced rotation
        practice_name = 'Favoriser l\'alternance de cultures à semis de printemps et d\'automne'

        # In this example, we have only 25% of fall cultures: an unbalanced rotation
        answers = {
            "problem": "DESHERBAGE",
            "tillage": "TRAVAIL_PROFOND",
            "rotation": [mais, tournesol, soja, ble],
        }
        engine = Engine(answers, [], [])
        unbalanced_result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))

        # Now we balance it out, having half fall, half spring rotation
        answers = {
            "problem": "DESHERBAGE",
            "tillage": "TRAVAIL_PROFOND",
            "rotation": [mais, tournesol, orge, ble],
        }
        engine = Engine(answers, [], [])
        balanced_result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))

        # The practice weight when the rotation was unbalanced must be higher
        self.assertGreater(unbalanced_result.weight, balanced_result.weight)

        # Note that the threshold is 25%, so having an unbalanced rotation of 2-1 (33%)
        # should count as a balanced rotation. We need to account here for the short-rotation
        # bonis though.
        answers = {
            "problem": "DESHERBAGE",
            "tillage": "TRAVAIL_PROFOND",
            "rotation": [mais, tournesol, ble],
        }
        engine = Engine(answers, [], [])
        short_rotation_bonus = 1.1
        result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))
        self.assertEqual(balanced_result.weight * short_rotation_bonus,
                         result.weight)

        # We should only look at spring and fall cultures when we apply this logic.
        # In this case we have 40% fall, 40% spring, and 20% end-of-summer. Despite
        # having a sowing period of less than 25%, since it is not fall nor spring we
        # still consider this a balanced culture.
        answers = {
            "problem": "DESHERBAGE",
            "tillage": "TRAVAIL_PROFOND",
            "rotation": [mais, tournesol, ble, orge, colza],
        }
        engine = Engine(answers, [], [])
        result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))
        self.assertEqual(balanced_result.weight, result.weight)

        # In this case we have 25% summer, 25% spring and 50% fall. This should
        # be considered an unbalanced rotation because spring is <= 25%
        answers = {
            "problem": "DESHERBAGE",
            "tillage": "TRAVAIL_PROFOND",
            "rotation": [tournesol, ble, orge, colza],
        }
        engine = Engine(answers, [], [])
        result = next(
            filter(lambda x: x.practice.title == practice_name,
                   engine.calculate_results()))
        self.assertEqual(unbalanced_result.weight, result.weight)
Ejemplo n.º 21
0
    def test_tillage_types(self):
        """
        Tillage (travail de sol) can be deep or shallow. The practices that need
        deep tillage should only be proposed if the user can do deep tillage. Same
        goes for shallow tillage.
        """
        deep_tillage_practice_title = 'Positionner un labour stratégiquement'
        shallow_tillage_practice_title = 'Désherbage mécanique en plein en début de saison pour cultures de printemps'

        rumex = Weed.objects.filter(display_text='Rumex').first()
        ble = SimulatorCulture.objects.filter(display_text='Blé dur').first()
        lin_hiver = SimulatorCulture.objects.filter(
            display_text='Lin hiver').first()
        ble_printemps = SimulatorCulture.objects.filter(
            display_text='Blé tendre de printemps').first()

        # If the user can't do any tillage, both practices should be at zero score
        answers = {
            "problem":
            "GLYPHOSATE",
            "weeds":
            "{0}".format(str(rumex.external_id)),
            "tillage":
            None,
            "rotation": [
                ble.external_id, lin_hiver.external_id,
                ble_printemps.external_id
            ],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        deep_tillage_result = next(
            filter(lambda x: x.practice.title == deep_tillage_practice_title,
                   results))
        shallow_tillage_result = next(
            filter(
                lambda x: x.practice.title == shallow_tillage_practice_title,
                results))

        self.assertEqual(deep_tillage_result.weight, 0)
        self.assertEqual(shallow_tillage_result.weight, 0)

        # If the user can do shallow tillage, the shallow tillage practice
        # should be above zero, whereas the deep tillage practice should be at zero
        answers = {
            "problem":
            "GLYPHOSATE",
            "weeds":
            "{0}".format(str(rumex.external_id)),
            "tillage":
            'TRAVAIL_DU_SOL',
            "rotation": [
                ble.external_id, lin_hiver.external_id,
                ble_printemps.external_id
            ],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        deep_tillage_result = next(
            filter(lambda x: x.practice.title == deep_tillage_practice_title,
                   results))
        shallow_tillage_result = next(
            filter(
                lambda x: x.practice.title == shallow_tillage_practice_title,
                results))

        self.assertEqual(deep_tillage_result.weight, 0)
        self.assertGreater(shallow_tillage_result.weight, 0)

        # If the user can do deep tillage, both practices should be above zero
        answers = {
            "problem":
            "GLYPHOSATE",
            "weeds":
            "{0}".format(str(rumex.external_id)),
            "tillage":
            'TRAVAIL_PROFOND',
            "rotation": [
                ble.external_id, lin_hiver.external_id,
                ble_printemps.external_id
            ],
        }
        engine = Engine(answers, [], [])
        results = engine.calculate_results()
        deep_tillage_result = next(
            filter(lambda x: x.practice.title == deep_tillage_practice_title,
                   results))
        shallow_tillage_result = next(
            filter(
                lambda x: x.practice.title == shallow_tillage_practice_title,
                results))

        self.assertGreater(deep_tillage_result.weight, 0)
        self.assertGreater(shallow_tillage_result.weight, 0)
Ejemplo n.º 22
0
class Bot(object):

    def __init__(self, url, driver_path=None, browser_name=None, opponent="Friend"):
        super().__init__()
        self.url = url
        self.driver_path = driver_path
        self.driver = None
        self.browser_name = None

        self.opponent = opponent
        
        self.engine = None

        self.last_move_tracker = None
        self.match_end = False

    def set_driver_path(self, path, browser_name):
        self.driver_path = path
        self.browser_name = browser_name
    
    def set_opponent(self, opponent):
        self.opponent = opponent

    def create_browser(self):
        if self.browser_name.lower() == "chrome":
            self.driver = webdriver.Chrome(executable_path=self.driver_path)
        else:
            self.driver = webdriver.Firefox()

    def close_driver(self):
        self.driver.close()

    def create_engine(self):
        self.engine = Engine()
        self.engine.set_engine(skill_level=1)  # Default = stockfish
    
    def click_coords(self, coords):
        y, x = coords
        action = webdriver.common.action_chains.ActionChains(self.driver)
        action.move_to_element_with_offset(self.board_web_element, y, x)
        action.click()
        action.perform()
        print("Clicked:", coords)
        return True

    def click_accept(self):
        try:
            button = self.driver.find_element_by_xpath("//form[@class='accept']/button")
            button.click()
            return True
        except Exception as e:
            print(e)
            return False

    def create_board(self, board, my_colour):
        self.main_board = Board(board, my_colour)

        self.main_board.set_current_board_image()       # Set screenshot image

    def make_move(self):
        move = self.engine.get_best_move()
        print("BOT: Best move -", move)
        # Make move on GUI

        pp_piece = None  # Pawn promotion piece
        if len(move) == 5:
            pp_piece = move[-1]
            move = move[:-1]

        # Get coordinates of rank/file (old_square, new_square)
        old_fr, new_fr = self.main_board.from_engine_split_fr(move) # Split string to old, new
        old_square = self.main_board.from_engine_convert(old_fr)    # Co-ordinates to click
        new_square = self.main_board.from_engine_convert(new_fr)    # Co-ordinates to click 
        # Click old_square
        self.click_coords(old_square)
        time.sleep(0.1)
        # Click new_square
        self.click_coords(new_square)

        # Select new piece if pawn promoted
        if pp_piece:
            width, height = new_square
            if pp_piece.lower() == "q":
                pass  # Click again same square
            elif pp_piece.lower() == "n":
                height = height + self.main_board.square_height      # Click 1 square down
            elif pp_piece.lower() == "r":
                height = height + (self.main_board.square_height*2)  # Click 2 squares down
            elif pp_piece.lower() == "b":
                height = height + (self.main_board.square_height*3)  # Click 3 squares down
            time.sleep(0.2)
            self.click_coords( (height, width) )
            move += pp_piece


        # Update engine with new move
        self.update_engine(move)

        # Update bot with last move
        self.last_move_tracker = new_fr
        print("My move finished.\n")

    def update_engine(self, move):
        self.engine.move(move)



    def start(self):
        print("Bot starting...")

        self.create_browser()
        
        self.driver.get(self.url)            
        
        if self.opponent == "friend":
            print("Playing against a friend")
            if not self.click_accept():
                print("No accept button so: Spectating")
                return           
        elif self.opponent == "computer":
            print("Playing against computer")
        elif self.opponent == "player":
            print("Playing against a player")

        my_colour_text = self.driver.find_element_by_xpath("//div[contains(@class, 'cg-wrap')]").get_attribute("class")
        my_colour = re.search(r'orientation-(\w+) ', my_colour_text).group(1)


        print("I am",my_colour)
        
        
        if my_colour == "black":
            print("Flip the board")
            flip_btn = self.driver.find_element_by_xpath("//button[contains(@title, 'Flip board')]").click()
        
        # Find main board in browser
        self.board_web_element = self.driver.find_element_by_tag_name('cg-board')
        # Create a board object
        self.create_board(self.board_web_element, my_colour)
        # Create engine
        self.create_engine()
        # self.engine.set_engine_skill_level(5)

        

        
        
        # If white then: Move first
        if self.main_board.my_turn():
            self.make_move()
            self.main_board.update_turn()


        while True:

            # If game ended (Checkmate) then break
            if self.match_end:
                break
            
            time.sleep(3)  # Sleep for 3 seconds
            try:
                # Get last move
                move_list_parent = self.driver.find_element_by_tag_name('l4x')  # Does not exist if there are no moves i.e. white first
                last_move = move_list_parent.find_element_by_xpath("//u8t[contains(@class, 'a1t')]").text
                print("Ultimate last move", last_move)
                castle_move = None
                pawn_promotion = None
                new_pp_piece = None

                if cst_n := last_move.count('-'):  # O-O or O-O-O
                    print("Castle")
                    if cst_n == 1:
                        castle_move = "e1g1" if self.main_board.turn == "white" else "e8g8"  # If white turn:   O-O: e1g1, if black: e8g8
                    elif cst_n == 2:
                        castle_move = "e1c1" if self.main_board.turn == "white" else "e8c8"  # If white turn: O-O-O: e1c1, if black: e8c8 
                elif pawn_promotion := last_move.count('='):  # b8=Q
                    print("Pawn promotion")
                    last_move = last_move.strip('+=')  # If check with new piece
                    new_pp_piece = last_move[-1]
                    last_move = last_move[:-1]
                elif any(x in last_move for x in ('+','#')):  # Bxf7+
                    print("Check")
                    if '#' in last_move:
                        self.match_end = True
                        break
                    last_move = last_move.strip('+#')[-2:]
                    
                print("Last move:",last_move)



                # Whose turn it is
                turn_text = self.driver.find_element_by_xpath("//div[contains(@class, 'rclock-turn__text')]").text
                print(turn_text)

                if turn_text == "Your turn":
                    # Update board with their latest move
                    self.main_board.set_current_board_image()
                    self.main_board.update_turn()

                    # If move is castle then set opponent move as one generated previously
                    if castle_move:
                        opponent_move = castle_move
                    else:
                        opponent_move = self.main_board.to_engine_get_fr()
                    
                    if pawn_promotion:
                        opponent_move += new_pp_piece
                    # Update engine
                    self.update_engine(opponent_move)

                    # I finally make my move
                    self.make_move()  # My move
                    # Update board with my latest move
                    self.main_board.set_current_board_image()
                    self.main_board.update_turn()
                    
                    # Set last move
                    # self.last_move_tracker = last_move

                elif turn_text == "Waiting for opponent":
                    pass
                    # print("Waiting for their move")
                else:
                    print("Error: User turn text is wrong.")
Ejemplo n.º 23
0
 def get_results(answers, practice_blacklist, type_blacklist):
     engine = Engine(answers, practice_blacklist, type_blacklist)
     practices = engine.calculate_results()
     suggestions = engine.get_suggestions(practices)
     return (practices, suggestions)
Ejemplo n.º 24
0
 def create_engine(self):
     self.engine = Engine()
     self.engine.set_engine(skill_level=1)  # Default = stockfish