How to add participant.var to data set - otree

New to oTree, so I apologize if this is a super trivial question. I was trying to figure out how to pass my participant.var variable into the admin and exported data fields.
My model.py subsession class has the following:
def creating_session(self):
# randomize treatments
if self.round_number == 1:
for p in self.get_players():
p.participant.vars['treatment'] = random.choice(['no_info', 'full_info', 'info_choice'])
and my player class has
treatment = models.StringField()
participant_vars_treatment = models.LongStringField()
def treatment_allocation(self):
self.player.participant_vars_treatment = str(self.participant.vars['treatment'])
This does not produce the randomized treatments into the new variable participant_vars_treatment. Could someone point me in the right direction? Any help would be great!

Solved solution:
In the subsession class:
def creating_session(self):
# randomize treatments
import itertools
treatments = itertools.cycle(['no_info', 'full_info', 'info_choice'])
if self.round_number == 1:
for p in self.get_players():
p.participant.vars['treatment'] = next(treatments)
treatment = next(treatments)
p.participant.vars['treatment'] = treatment
p.treatment = treatment
and then the following in the play class:
treatment = models.StringField()

Related

PyQGIS, custom processing algorithm: How to use selected features only?

I want to create a custom processing algorithm with PyQGIS, which is able to take a vector layer as input (in this case of type point) and then do something with it's features. It's working well as long as I just choose the whole layer. But it doesn't work if I'm trying to work on selected features only.
I'm using QgsProcessingParameterFeatureSource to be able to work on selected features only. The option is shown and I can enable the checkbox. But when I'm executing the algorithm, I get NoneType as return of parameterAsVectorLayer.
Below you'll find a minimal working example to reproduce the problem:
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (
QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingParameterFeatureSource
)
name = "selectedonly"
display_name = "Selected features only example"
group = "Test"
group_id = "test"
short_help_string = "Minimal working example code for showing my problem."
class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
def tr(self, string):
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return ExampleProcessingAlgorithm()
def name(self):
return name
def displayName(self):
return self.tr(display_name)
def group(self):
return self.tr(group)
def groupId(self):
return group_id
def shortHelpString(self):
return self.tr(short_help_string)
def initAlgorithm(self, config=None):
self.addParameter(
QgsProcessingParameterFeatureSource(
'INPUT',
self.tr('Some point vector layer.'),
types=[QgsProcessing.TypeVectorPoint]
)
)
def processAlgorithm(self, parameters, context, feedback):
layer = self.parameterAsVectorLayer(
parameters,
'INPUT',
context
)
return {"OUTPUT": layer}
If I'm working on the whole layer, the output is {'OUTPUT': <QgsVectorLayer: 'Neuer Temporärlayer' (memory)>}, which is what I would expect.
If I'm working on selected features only, my output is {'OUTPUT': None}, which doesn't makes sense to me. I selected some of the features before executing of course.
I'm using QGIS-version 3.22 LTR, if it's relevant.
Can anybody tell me what I'm doing wrong?
I would suggest you trying to use the method 'parameterAsSource' in the 'processAlgorithm' method.
layer = self.parameterAsSource(
parameters,
'INPUT',
context
)

Building a QTreeView from strings

I'm trying to build a treeview using what's called a slug: which is a string that's split by hyphens (e.g. Fruit-Apple). Then you just loop through the parts and build the tree if the item doesn't already exist. The first item in the list is always the top most parent. Any suggestions or help would be greatly appreciated. I've having issues trying to append the item to the correct parent.
appendCategorySlug('Fruit-Apple')
appendCategorySlug('Fruit-Orange')
appendCategorySlug('Vegetable-Lettuce')
appendCategorySlug('Fruit-Kiwi')
appendCategorySlug('Vegetable-Carrot')
appendCategorySlug('Vegetable-Carrot-Blackbean')
appendCategorySlug('Vegan-Meat-Blackbean')
I'm not quite sure what I've got going wrong here. The results are kind close, but something is off...
import os, sys
from Qt import QtWidgets, QtGui, QtCore
class CategoryView(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.resize(250,400)
self.categoryModel = QtGui.QStandardItemModel()
self.categoryModel.setHorizontalHeaderLabels(['Items'])
self.categoryProxyModel = QtCore.QSortFilterProxyModel()
self.categoryProxyModel.setSourceModel(self.categoryModel)
self.categoryProxyModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.categoryProxyModel.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.categoryProxyModel.setDynamicSortFilter(True)
self.uiTreeView = QtWidgets.QTreeView()
self.uiTreeView.setModel(self.categoryProxyModel)
self.uiTreeView.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.uiTreeView)
self.setLayout(self.layout)
def appendCategorySlug(self, slug):
# sourceIndex = self.categoryProxyModel.mapToSource(proxyIndex)
# item = self.categoryModel.itemFromIndex(sourceIndex)
parts = slug.split('-')
parent = self.categoryModel.invisibleRootItem()
for name in parts:
# find part and it doesn't exist append it
items = self.categoryModel.findItems(name, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0)
if len(items) == 1:
print items[0].data()
parent = items[0]
item = QtGui.QStandardItem(name)
parent.appendRow(item)
parent = item
def test_CategoryView():
app = QtWidgets.QApplication(sys.argv)
ex = CategoryView()
ex.appendCategorySlug('Fruit-Apple')
ex.appendCategorySlug('Fruit-Orange')
ex.appendCategorySlug('Vegetable-Lettuce')
ex.appendCategorySlug('Fruit-Kiwi')
ex.appendCategorySlug('Vegetable-Carrot')
ex.appendCategorySlug('Vegetable-Carrot-Blackbean')
ex.appendCategorySlug('Vegan-Meat-Blackbean')
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
pass
test_CategoryView()
There are a few issues here. Searching recursively is error-prone because descendant items could have the same name as one of their ancestors. And it is also inefficient, because at each step we only want to know whether an item with the same name exists at the current level - the ones below it are irrelevant. Also, we should avoid searching if the current parent has no children, and make sure that new parents are only created when they're missing (which is the main problem with your current implementation).
The above issues can be fixed most easily if the model's match function is used instead of findItems, since it allows greater control over how the searching is done:
def appendCategorySlug(self, slug):
parts = slug.split('-')
parent = self.categoryModel.invisibleRootItem()
for name in parts:
if parent.rowCount():
indexes = self.categoryModel.match(
parent.child(0).index(),
QtCore.Qt.DisplayRole, name, 1,
QtCore.Qt.MatchExactly)
if indexes:
parent = self.categoryModel.itemFromIndex(indexes[0])
continue
item = QtGui.QStandardItem(name)
parent.appendRow(item)
parent = item
This algorithm can be implemented even more simply with a simple for-loop, and is perhaps easier to understand:
def appendCategorySlug(self, slug):
parts = slug.split('-')
parent = self.categoryModel.invisibleRootItem()
for name in parts:
for row in range(parent.rowCount()):
child = parent.child(row)
if child.text() == name:
parent = child
break
else:
item = QtGui.QStandardItem(name)
parent.appendRow(item)
parent = item

rails string substitution or similar solution in controller

I'm building a site with users in all 50 states. We need to display information for each user that is specific to their situation, e.g., the number of events they completed in that state. Each state's view (a partial) displays state-specific information and, therefore, relies upon state-specific calculations in a state-specific model. We'd like to do something similar to this:
##{user.state} = #{user.state.capitalize}.new(current_user)
in the users_controller instead of
#illinois = Illinois.new(current_user) if (#user.state == 'illinois')
.... [and the remaining 49 states]
#wisconsin = Wisconsin.new(current_user) if (#user.state == 'wisconsin')
to trigger the Illinois.rb model and, in turn, drive the view defined in the users_controller by
def user_state_view
#user = current_user
#events = Event.all
#illinois = Illinois.new(current_user) if (#user.state == 'illinois')
end
I'm struggling to find a better way to do this / refactor it. Thanks!
I would avoid dynamically defining instance variables if you can help it. It can be done with instance_variable_set but it's unnecessary. There's no reason you need to define the variable as #illinois instead of just #user_state or something like that. Here is one way to do it.
First make a static list of states:
def states
%{wisconsin arkansas new_york etc}
end
then make a dictionary which maps those states to their classes:
def state_classes
states.reduce({}) do |memo, state|
memo[state] = state.camelize.constantize
memo
end
end
# = { 'illinois' => Illinois, 'wisconsin' => Wisconsin, 'new_york' => NewYork, etc }
It's important that you hard-code a list of state identifiers somewhere, because it's not a good practice to pass arbitrary values to contantize.
Then instantiating the correct class is a breeze:
#user_state = state_classes[#user.state].new(current_user)
there are definitely other ways to do this (for example, it could be added on the model layer instead)

Get param value dynamically

My question model holds the prompt and the answer choices for questions that students can answer. It includes columns named :choice_0, :choice_1, :choice_2, :choice_3, :choice_4, and :choice_5.
In one section of my controller, I've used the following code:
correct_array.push(these_params[:choice_0]) if !these_params[:choice_0].blank?
correct_array.push(these_params[:choice_1]) if !these_params[:choice_1].blank?
correct_array.push(these_params[:choice_2]) if !these_params[:choice_2].blank?
correct_array.push(these_params[:choice_3]) if !these_params[:choice_3].blank?
correct_array.push(these_params[:choice_4]) if !these_params[:choice_4].blank?
correct_array.push(these_params[:choice_5]) if !these_params[:choice_5].blank?
In other areas of my app, I've used the #{} syntax, for example:
params[:choice_#{n}]
But that doesn't work within a params hash for some reason. I'm sure that there is a drier way to accomplish these five lines.
Thank you in advance for any insight.
A more Ruby way to do this is:
correct_array = (0..5).map { |i| these_params["choice_#{i}".to_sym] }.select(&:present?)
Or as a method:
def correct_array
(0..5).map { |i| these_params["choice_#{i}".to_sym] }.select(&:present?)
end
In either case, you have the added bonus of not having to initialize correct_array as it is created on the fly.
You may try this
(0..5).each do |i|
param_i = these_params["choice_#{i}".to_sym]
correct_array.push(param_i) if param_i.present?
end

Ruby: Interpolate strings in hash creation

I'm currently trying to write a small script which parses weekly reports(e-mails) and stores the data I want in variables, so I can handle them further.
The functionality is working already, but it's sort of hacked in at the moment and I'd like to learn how the code should look idealy.
This is my code at the moment - I shortened it a bit and made it more generic:
activated_accounts_rx = Regexp.new(/example/)
canceled_accounts_rx = Regexp.new(/example/)
converted_accounts_rx = Regexp.new(/example/)
File.open("weekly_report.txt") do |f|
input = f.read
activated_accounts = input.scan(activated_accounts_rx).join
canceled_accounts = input.scan(canceled_accounts_rx).join
converted_accounts = input.scan(converted_accounts_rx).join
end
I thought of something like this and I know that it can't work, but I don't know how I can get it to work:
var_names = ["activated_accounts",
"canceled_accounts",
"converted_accounts"]
regex = { "#{var_names[0]}": Regexp.new(/example/),
"#{var_names[1]}": Regexp.new(/example/) }
File.open("weekly_report.txt") do |f|
input = f.read
for name in var_names
name = input.scan(regex[:#{name}]).join
end
end
I would like to end up with variables like this:
activated_accounts = 13
canceled_accounts = 21
converted_accounts = 5
Can someone help me please?
You probably don't need to have a separate array for your variables. Just use them as the keys in the hash. Then you can access the values later from another hash. Better yet, if you don't need the regexes anymore, you can just replace that value with the scanned contents.
regexHash = {
activated_accounts: Regexp.new(/example/),
canceled_accounts : Regexp.new(/example/),
converted_accounts: Regexp.new(/example/)
}
values = {}
contents = File.open("weekly_report.txt").read
regexHash.each do |var, regex|
values[var] = contents.scan(regex).join
end
To access your values later just use
values[:var_name] # values[:activated_accounts] for example
If you want separate name's variable in an array, you can use to_sym:
regex = { var_names[0].to_sym => Regexp.new(/example/),
var_names[1].to_sym => Regexp.new(/example/) } #Rocket notation!
And:
for name in var_names
name = input.scan(regex[var_names[0].to_sym]).join
end
Anyway, i prefer the Rob Wagner advice.

Resources