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