oTree Forum >

Looping over variable names

#1 by AndreL

Stack Exchange seems to absolutely hate patterned variable names or loops over var names, but I need to have loops, within either Page or Player classes, over logically-named sets of variables. 

In Stata or R, this is extremely easy to do. How can I simplify this highly redundant code from Python for oTree 5, without using ExtraModel?

=========================
class C.Constants(BaseConstants)
    N_STEPS= 5
    COST= 120
    GLOBAL_MAX = 800

class Player(BasePlayer):
    max_price= models.FloatField()
    cost= models.FloatField()
    
    step1= models.IntegerField()
    step2= models.IntegerField()
    step3= models.IntegerField()
    step4= models.IntegerField()
    step5= models.IntegerField()
    
    bid_step1= models.IntegerField()
    bid_step2= models.IntegerField()
    bid_step3= models.IntegerField()
    bid_step4= models.IntegerField()
    bid_step5= models.IntegerField()
    
def creating_session(subsession: Subsession)
    player.cost= C.COST
    
class Boundary(Page):
    form_model = 'player'
    form_fields = ['max_price']
    
    def before_next_page(player: Player, timeout_happened):
        player.step1= ((player.max_price -player.cost) / C.N_STEPS * 1)
        player.step2= ((player.max_price -player.cost) / C.N_STEPS * 2)
        player.step3= ((player.max_price -player.cost) / C.N_STEPS * 3)
        player.step4= ((player.max_price -player.cost) / C.N_STEPS * 4)
        player.step5= ((player.max_price -player.cost) / C.N_STEPS * 5)

class ElicitBids(Page):
    form_model = 'player'
    form_fields = ['offer_step1', 'offer_step2', 'offer_step3', 'offer_step4, 'offer_step5']
    
---------------
Then on Elicitation.html

{{ if player.step1 <= C.GLOBAL_MAX }}
    {{ formfield offer_step1 }}
{{ endif }}

{{ if player.step2 <= C.GLOBAL_MAX }}
    {{ formfield offer_step2 }}
{{ endif }}

{{ if player.step3 <= C.GLOBAL_MAX }}
    {{ formfield offer_step3 }}
{{ endif }}

{{ if player.step4 <= C.GLOBAL_MAX }}
    {{ formfield offer_step4 }}
{{ endif }}

{{ if player.step5 <= C.GLOBAL_MAX }}
    {{ formfield offer_step5 }}
{{ endif }}
====================================================

#2 by gr0ssmann

I think within Python something like this should work:

    for i in range(20):
        setattr(Player, f"myvar{i}", models.IntegerField())
   
I believe that within the template, you would have to generate the code beforehand. I don't think oTree's template language permits reflection (https://en.wikipedia.org/wiki/Reflective_programming).

#3 by Chris_oTree

See getattr_setattr in otree-snippets.
As for the form in the template, you should put the dynamic logic in get_form_fields then just do {{ formfields }}.

#4 by BonnEconLab

See also https://www.otreehub.com/forum/823/.

Write a reply

Set forum username