Locust/Python: Splitting a tasks array with if conditions in a SequentialTaskSet - python

I'm new to Locust, and Python in general. I've been using JMeter for several years, and I'm trying to implement similar logic to what I've used there for handling failures in login.
I want to run a simple scenario: Login, Enter a folder, Logout
Where I'm having trouble is implementing this logic:
If any login call fails, I don't want to attempt the folder enter (avoid a cascading failure after login), but I still want to run the logout to ensure there is no active session.
My current SequentialTaskSet reads like this, which works for executing the child tasksets:
class folder_enter_scalability_taskset(SequentialTaskSet):
def on_start(self):
self.tm = TransactionManager()
#task
def seedata_config(self):
seeddata = next(seeddata_reader)
self.client.username = seeddata['username']
self.client.password = seeddata['password']
tasks = [login_taskset, folder_enter_taskset, logout_taskset]
Is there a way to split up the tasks array into multiple steps within the same SequentialTaskSet?
Always Login
if login passed, enter folder
Always Logout

Thanks to phil in the comments for pointing me in the right direction, I now have a working code with try-finally and if-else instead of a tasks array. Hope this can help someone else:
class folder_enter_scalability_taskset(SequentialTaskSet):
def on_start(self):
self.tm = TransactionManager()
#task
def seedata_config(self):
seeddata = next(seeddata_reader)
self.client.username = seeddata['username']
self.client.password = seeddata['password']
self.client.userrole = seeddata['userrole']
#task
def folder_enter_scalability(self):
try:
login_taskset.login(self)
if self.client.loginFail is False:
folder_enter_taskset.folder_enter(self)
else:
logging.error("Login failed, skipping to logout")
finally:
logout_taskset.logout(self)
I wasn't having luck with getting it to skip folder enter if I placed it in a try-else-finally condition, so I went with adding this self.client.loginFail in the login taskset to support if-else within try.
It's initiated as 'False', and flipped to 'True' in case of failure in the taskset.

Related

In odoo 10, using the UI I cannot get my "Server action +Automated action" to work

I have the "reviewer" field available in my task, and I want to switch the reviewer with the task assignee automatically when the task is moved from the 'In progress' stage to the 'Review' stage. I have the following Python code in my server action:
picture of the code in context
def assignrev(self):
for record in self:
if record['project.task.type.stage_id.name']=='Review':
a=self.res.users.reviewer_id.name
b=self.res.users.user_id.name
record['res.users.user_id.name']=a
record['res.users.reviewer_id.name']=b
and below are links to pictures of my automated action settings:
Server action to run
"When to run" settings
Unfortunately, changing the task stage to 'Review' does not give the expected results. Any suggestion please?
Kazu
Ok I finally got the answer to this. below is a picture of the code in context for Odoo 10:
No "def" of "for record" needed: the code will not run.
I just hope this will be helpful to someone else...
Kazu
My guess is that you are incorrectly calling the fields you're trying to get.
# Instead of this
a = self.res.users.reviewer_id.name
b = self.res.users.user_id.name
record['res.users.user_id.name']=a
record['res.users.reviewer_id.name']=b
# Try this
# You don't need to update the name, you need to update the database ID reference
record['user_id'] = record.reviewer_id.id
record['reviewer_id'] = record.user_id.id
Furthermore, why don't you try using an onchange method instead?
#api.multi
def onchange_state(self):
for record in self:
if record.stage_id.name == 'Review':
record.update({
'user_id': record.reviewer_id.id,
'reviewer_id': record.user_id.id,
})
If you're still having problems, you can use ipdb to debug your code more easily by triggering set_trace in your method.
def assignrev(self):
# Triggers a break in code so that you can debug
import ipdb; ipdb.set_trace()
for record in self:
# Test line by line with the terminal to see where your problem is

rq enqueue function: fails with TypeError:function_xxx() takes 2 positional arguments but 3 were given

This is bugging me for past few hours and I can not seem to find a solution yet.
I am using django-rq to enqueue some long running tasks. In my tasks.py, I have the following:
from django_rq import job
#job
def long_running_task(hash, url, file_path):
#doing some work
and in my views.py,
def post(self, request, hash, file_path, format=None):
URL = "http://127.0.0.1:9000/work/"
task = django_rq.enqueue(long_running_task, hash, URL, file_path)
print("job result is: ", task.result)
return JsonResponse({"task_result": task.result})
When I run it, however, it fails with the following message-
TypeError: long_running_task() takes 2 positional arguments but 3 were given
Clearly, I am doing something silly here, but I am not able to figure it out yet. Can someone please let me know what's going on here?
What happens when you remove the decorator?
AFAIU, enqueue just takes a function and its args and returns a job. The decorator declares a function as a job. You need to use one or the other.
To use w/ the decorator you'll need to do something like the following pseudo:
def post(self, request, hash, file_path, format=None):
URL = "http://127.0.0.1:9000/work/"
task = long_running_task.delay(hash, URL, file_path)
# wait some time for completion
print("job result is: ", task.result)
return JsonResponse({"task_result": task.result})
Strangely, this seemed to be a circular dependency type of issue. Previously, I had the tasks.py file (where I defined the long_running_task) one level up from my views.py file. I moved the tasks.py file to the same level and it started working fine. No idea though as to why the seemingly unrelated error was being thrown.

SQLAlchemy event how to get delete items info after db session commit

I am using SQLAlchemy event which is very very good, I can void a lots of repeated work.
Now I need to delete some items, and do some post work in after_delete listener after delete action, but, meanwhile in after_delete listener I need to using those deleted items info which cannot be accessed while the session had been committed, here's a piece of code .
def shop_delete_category(category_id):
item_category = ItemCategory.query.get(category_id)
if item_category is None:
result['code'] = 100
else:
deleted_category_id_items = ItemCategory.query.filter(ItemCategory.id == category_id).with_entities(ItemCategory.id).all()
# some other business logic
db.session.delete(item_category)
db.session.commit()
My event listener code:
#event.listens_for(ItemCategory, 'after_delete')
def after_delete_shop_category(mapper, connect, target):
# business logic
shop_category_delete_signal.send(target)
# here will need deleted_category_id_items
# how can I pass in ?
# logic will be like this
redis_cache_delete_category_ids(deleted_category_id_items)
I want to do some post work after delete action , and in delete action I will use deleted_category_id_items, my question is
how can I pass deleted_category_id_items the event? Or is there some better way to slove the problem ?
Edit: My own answer
found : before_delete listener can solve the problem
Edit:
Another question how can I pass my own parameter to a listener, like this
#event.listens_for(ItemCategory, 'after_delete')
def after_delete_shop_category(mapper, connect, target, *my_own_paramters):
# business logic
shop_category_delete_signal.send(target)
# here will need deleted_category_id_items
# how can I pass in ?
# logic will be like this
redis_cache_delete_category_ids(deleted_category_id_items)
# can use my_own_paramters do some work
Thanks very much !

redirect while passing arguments

In flask, I can do this:
render_template("foo.html", messages={'main':'hello'})
And if foo.html contains {{ messages['main'] }}, the page will show hello. But what if there's a route that leads to foo:
#app.route("/foo")
def do_foo():
# do some logic here
return render_template("foo.html")
In this case, the only way to get to foo.html, if I want that logic to happen anyway, is through a redirect:
#app.route("/baz")
def do_baz():
if some_condition:
return render_template("baz.html")
else:
return redirect("/foo", messages={"main":"Condition failed on page baz"})
# above produces TypeError: redirect() got an unexpected keyword argument 'messages'
So, how can I get that messages variable to be passed to the foo route, so that I don't have to just rewrite the same logic code that that route computes before loading it up?
You could pass the messages as explicit URL parameter (appropriately encoded), or store the messages into session (cookie) variable before redirecting and then get the variable before rendering the template. For example:
from flask import session, url_for
def do_baz():
messages = json.dumps({"main":"Condition failed on page baz"})
session['messages'] = messages
return redirect(url_for('.do_foo', messages=messages))
#app.route('/foo')
def do_foo():
messages = request.args['messages'] # counterpart for url_for()
messages = session['messages'] # counterpart for session
return render_template("foo.html", messages=json.loads(messages))
(encoding the session variable might not be necessary, flask may be handling it for you, but can't recall the details)
Or you could probably just use Flask Message Flashing if you just need to show simple messages.
I found that none of the answers here applied to my specific use case, so I thought I would share my solution.
I was looking to redirect an unauthentciated user to public version of an app page with any possible URL params. Example:
/app/4903294/my-great-car?email=coolguy%40gmail.com to
/public/4903294/my-great-car?email=coolguy%40gmail.com
Here's the solution that worked for me.
return redirect(url_for('app.vehicle', vid=vid, year_make_model=year_make_model, **request.args))
Hope this helps someone!
I'm a little confused. "foo.html" is just the name of your template. There's no inherent relationship between the route name "foo" and the template name "foo.html".
To achieve the goal of not rewriting logic code for two different routes, I would just define a function and call that for both routes. I wouldn't use redirect because that actually redirects the client/browser which requires them to load two pages instead of one just to save you some coding time - which seems mean :-P
So maybe:
def super_cool_logic():
# execute common code here
#app.route("/foo")
def do_foo():
# do some logic here
super_cool_logic()
return render_template("foo.html")
#app.route("/baz")
def do_baz():
if some_condition:
return render_template("baz.html")
else:
super_cool_logic()
return render_template("foo.html", messages={"main":"Condition failed on page baz"})
I feel like I'm missing something though and there's a better way to achieve what you're trying to do (I'm not really sure what you're trying to do)
You can however maintain your code and simply pass the variables in it separated by a comma: if you're passing arguments, you should rather use render_template:
#app.route("/baz")
def do_baz():
if some_condition:
return render_template("baz.html")
else:
return render_template("/foo", messages={"main":"Condition failed on page baz"})

python Queue not passing correct data

Just found the Queue module which is helping me adapt the pyftpdlib module. I'm running an very strict FTP server, and my goal is to restrict the filenames available to upload. This is to prevent people from uploading whatever they want (it's actually the backend of an upload client, not a complete FTP server).
I have this in the ftpserver Authorizer:
def fetch_worlds(queue, username):
while queue.empty():
worlds = models.World.objects.filter(is_synced=True, user__username=username)
print worlds
queue.put(worlds, timeout=1)
class FTPAuthorizer(ftpserver.DummyAuthorizer):
def __init__(self):
self.q = Queue.Queue()
self.t = None # Thread
self.world_item = None
def has_perm(self, username, perm, path=None):
print "Checking permission\n"
if perm not in ['r','w']:
return False
# Check world name
self.t = threading.Thread(target=fetch_worlds, args=(self.q, username))
self.t.daemon = True
self.t.start()
self.world_item = self.q.get()
print "WORLDITEM: %s" % self.world_item
if path is not None:
path = os.path.basename(path)
for world in self.world_item:
test = "{0}_{1}.zip".format(username, world.name)
if path == test:
print "Match on %s" % test
return True
return False
My issue is, after the server starts, the first time I STOR a file, it does an initial db call and gets all the worlds properly. But when I then add another world (for example, set is_synced=True on one, it still returns the old data from self.q.get(). has_perm() is called every time a file is uploaded, and it needs to return live data (to check if a file is allowed).
For example, brand new server:
STOR file.zip, self.q.get() returns <World1, World2>
Update the database via other methods etc
STOR file2.zip, inside fetch_worlds, print worlds returns <World1, World2, World3> but self.q.get() returns <World1, World2>
Just found the Queue module and it seemed like it would be helpful but I can't get the implementation right.
(also couldn't add tag pyftpdlib)
i think this is what could be happening here:
when has_perm is called, you create a Thread that will query a database (?) to add elements to the queue
after calling start the call to the database will take some time
meanwhile in your main thread you entered q.get which will block.
the db call finishes and the result is added to the queue
and is immediately removed from the queue again by the blocking q.get
the queue is now empty, your thread enters the while-loop again and executes the same query again and puts the result onto the queue.
the next call to q.get will return that instead of what it expects.
you see, you could have a race condition here, that already is aparent from the fact that you're adding something to a queue in a loop while you don't have a loop when pulling.
also you assume the element you get from the queue is the result to what you have put onto it before. that doesn't have to be true. if you call has_perm two times this will result in two calls to fetch_worlds with the possibility that the queue.empty() check for one of the calls fails. so only one result will be put onto the queue. now you have two threads waiting on q.get, but only one will get a result, while the oter one waits until one becomes ready...
has_perm looks like it should be a blocking call anyway.

Categories

Resources