How to update a Wagtail Page - python

I am using a wagtail_hook to update a Page object and am running into trouble. More specifically, when an end-user hits the "Save draft" button from the browser I want the following code to fire. The purpose of this code is to change the knowledge_base_id dynamically, based on the results of the conditional statements listed below.
def sync_knowledge_base_page_with_zendesk2(request, page):
if isinstance(page, ContentPage):
page_instance = ContentPage.objects.get(pk=page.pk)
pageJsonHolder = page_instance.get_latest_revision().content_json
content = json.loads(pageJsonHolder)
print("content at the start = ")
print(content['knowledge_base_id'])
kb_id_revision = content['knowledge_base_id']
kb_active_revision = content['kb_active']
if kb_active_revision == "T":
if kb_id_revision == 0:
print("create the article")
content['knowledge_base_id'] = 456
#page_instance.knowledge_base_id = 456 # change this API call
else:
print("update the article")
elif kb_id_revision != 0:
print("delete the article")
content['knowledge_base_id'] = 0
#page_instance.knowledge_base_id = 0
print("content at the end = ")
print(content['knowledge_base_id'])
#page_instance.save_revision().publish
So when the hook code fires, it updates the draft with all the info EXCEPT the knowledge_base_id.
However when I change the knowledge_base_id like this (seen commented out above)
page_instance.knowledge_base_id = 0
And save it like this (also seen commented out above)
page_instance.save_revision().publish()
It saves the updated knowledge_base_id BUT skips over the other revisions. In short, what the heck am I doing wrong. Thanks in advance for the assist. Take care and have a good day.

So I figured out the problem. Instead of trying to use the Page method save_revisions(), I opted to use revisions.create(). Inside revisions.create(), you pass it a JSON object with your updated values. In addition to that you pass an instance of the user, and values for submitted_for_moderation and approved_go_live_at. Listed below is my updated code sample, with comments. Please let me know if you have any questions for me. I hope this post helps others avoid frustrations with updating revisions. Thanks for reading. Take care and have a good day.
from wagtail.wagtailcore import hooks
from .models import ContentPage
import json
# Allows the latest page revision JSON to be updated based on conditionals
def sync_kb_page_with_zendesk(request, page):
# Sanity check to ensure page is an instance of ContentPage
if isinstance(page, ContentPage):
# this sets the user variable
user_var = request.user
# sets the Content Page
page_instance = ContentPage.objects.get(pk=page.pk)
# this retrieves JSON str w/ latest revisions
pageJsonHolder = page_instance.get_latest_revision().content_json
# this takes the json string and converts it into a json object
content = json.loads(pageJsonHolder)
# this sets the kb id variable for use in the code
kb_id_revision = content['knowledge_base_id']
# this sets the kb active variable for use in the code
kb_active_revision = content['kb_active']
# this is the conditional logic
if kb_active_revision == "T":
if kb_id_revision == 0:
print("create the article")
# updates the kb id value in the JSON object
content['knowledge_base_id'] = 456
else:
print("update the article")
elif kb_id_revision != 0:
print("delete the article")
# updates the kb id value in the JSON object
content['knowledge_base_id'] = 0
# this takes the JSON object and coverts it back to a JSON str
revisionPageJsonHolder = json.dumps(content)
# this takes your JSON str and creates the latest revision of Page
revision = page_instance.revisions.create(
content_json=revisionPageJsonHolder,
user=user_var,
submitted_for_moderation=False,
approved_go_live_at=None,
)
# registers the function to fire after page edit
hooks.register('after_edit_page', sync_kb_page_with_zendesk)
# registers the function to fire after page creation
hooks.register('after_create_page', sync_kb_page_with_zendesk)

Related

multiprocessing a function with parameters that are iterated through

I'm trying to improve the speed of my program and I decided to use multiprocessing!
the problem is I can't seem to find any way to use the pool function (i think this is what i need) to use my function
here is the code that i am dealing with:
def dataLoading(output):
name = ""
link = ""
upCheck = ""
isSuccess = ""
for i in os.listdir():
with open(i) as currentFile:
data = json.loads(currentFile.read())
try:
name = data["name"]
link = data["link"]
upCheck = data["upCheck"]
isSuccess = data["isSuccess"]
except:
print("error in loading data from config: improper naming or formating used")
output[name] = [link, upCheck, isSuccess]
#working
def userCheck(link, user, isSuccess):
link = link.replace("<USERNAME>", user)
isSuccess = isSuccess.replace("<USERNAME>", user)
html = requests.get(link, headers=headers)
page_source = html.text
count = page_source.count(isSuccess)
if count > 0:
return True
else:
return False
I have a parent function to run these two together but I don't think i need to show the whole thing, just the part that gets the data iteratively:
for i in configData:
data = configData[i]
link = data[0]
print(link)
upCheck = data[1] #just for future use
isSuccess = data[2]
if userCheck(link, username, isSuccess) == True:
good.append(i)
you can see how I enter all of the data in there, how would I be able to use multiprocessing to do this when I am iterating through the dictionary to collect multiple parameters?
I like to use mp.Pool().map. I think it is easiest and most straight forward and handles most multiprocessing cases. So how does map work? For starts, we have to keep in mind that mp creates workers, each worker receives a copy of the namespace (ya the whole thing), then each worker works on what they are assigned and returns. Hence, doing something like "updating a global variable" while they work, doesn't work; since they are each going to receive a copy of the global variable and none of the workers are going to be communicating. (If you want communicating workers you need to use mp.Queue's and such, it gets complicated). Anyway, here is using map:
from multiprocessing import Pool
t = 'abcd'
def func(s):
return t[int(s)]
results = Pool().map(func,range(4))
Each worker received a copy of t, func, and the portion of range(4) they were assigned. They are then automatically tracked and everything is cleaned up in the end by Pool.
Something like your dataLoading won't work very well, we need to modify it. I also cleaned the code a little.
def loadfromfile(file):
data = json.loads(open(file).read())
items = [data.get(k,"") for k in ['name','link','upCheck','isSuccess']]
return items[0],items[1:]
output = dict(Pool().map(loadfromfile,os.listdir()))

Pyqt and general python, can this be considered a correct approach for coding?

I have a dialog window containing check-boxes, when each of them is checked a particular class needs to be instantiated and a run a a task on a separated thread (one for each check box). I have 14 check-boxes to check the .isChecked() property and is comprehensible checking the returned Boolean for each of them is not efficient and requires a lot more coding.
Hence I decided to get all the children items corresponding to check-box element, get just those that are checked, appending their names to list and loop through them matching their name to d dictionary which key is the name of the check box and the value is the corresponding class to instantiate.
EXAMPLE:
# class dictionary
self.summary_runnables = {'dupStreetCheckBox': [DupStreetDesc(),0],
'notStreetEsuCheckBox': [StreetsNoEsuDesc(),1],
'notType3CheckBox': [Type3Desc(False),2],
'incFootPathCheckBox': [Type3Desc(True),2],
'dupEsuRefCheckBox': [DupEsuRef(True),3],
'notEsuStreetCheckBox': [NoLinkEsuStreets(),4],
'invCrossRefCheckBox': [InvalidCrossReferences()],
'startEndCheckBox': [CheckStartEnd(tol=10),8],
'tinyEsuCheckBox': [CheckTinyEsus("esu",1)],
'notMaintReinsCheckBox': [CheckMaintReins()],
'asdStartEndCheckBox': [CheckAsdCoords()],
'notMaintPolysCheckBox': [MaintNoPoly(),16],
'notPolysMaintCheckBox': [PolyNoMaint()],
'tinyPolysCheckBox': [CheckTinyEsus("rd_poly",1)]}
# looping through list
self.long_task = QThreadPool(None).globalInstance()
self.long_task.setMaxThreadCount(1)
start_report = StartReport(val_file_path)
end_report = EndReport()
# start_report.setAutoDelete(False)
# end_report.setAutoDelete(False)
end_report.signals.result.connect(self.log_progress)
end_report.signals.finished.connect(self.show_finished)
# end_report.setAutoDelete(False)
start_report.signals.result.connect(self.log_progress)
self.long_task.start(start_report)
# print str(self.check_boxes_names)
for check_box_name in self.check_boxes_names:
run_class = self.summary_runnables[check_box_name]
if run_class[0].__class__.__name__ is 'CheckStartEnd':
run_class[0].tolerance = tolerance
runnable = run_class[0]()
runnable.signals.result.connect(self.log_progress)
self.long_task.start(runnable)
self.long_task.start(end_report)
example of a runnable (even if some of them use different global functions)
I can't post the global functions that write content to file as they are too many and not all 14 tasks execute the same type function. arguments of these functions are int keys to other dictionaries that contain the report static content and the SQL queries to return report main dynamic contents.
class StartReport(QRunnable):
def __init__(self, file_path):
super(StartReport,self).__init__()
# open the db connection in thread
db.open()
self.signals = GeneralSignals()
# self.simple_signal = SimpleSignal()
# print self.signals.result
self.file_path = file_path
self.task = "Starting Report"
self.progress = 1
self.org_name = org_name
self.user = user
self.report_title = "Validation Report"
print "instantiation of start report "
def run(self):
self.signals.result.emit(self.task, self.progress)
if self.file_path is None:
print "I started and found file none "
return
else:
global report_file
# create the file and prints the header
report_file = open(self.file_path, 'wb')
report_file.write(str(self.report_title) + ' for {0} \n'.format(self.org_name))
report_file.write('Created on : {0} at {1} By : {2} \n'.format(datetime.today().strftime("%d/%m/%Y"),
datetime.now().strftime("%H:%M"),
str(self.user)))
report_file.write(
"------------------------------------------------------------------------------------------ \n \n \n \n")
report_file.flush()
os.fsync(report_file.fileno())
class EndReport(QRunnable):
def __init__(self):
super(EndReport,self).__init__()
self.signals = GeneralSignals()
self.task = "Finishing report"
self.progress = 100
def run(self):
self.signals.result.emit(self.task, self.progress)
if report_file is not None:
# write footer and close file
report_file.write("\n \n \n")
report_file.write("---------- End of Report -----------")
report_file.flush()
os.fsync(report_file.fileno())
report_file.close()
self.signals.finished.emit()
# TODO: checking whether opening a db connection in thread might affect the db on the GUI
# if db.isOpen():
# db.close()
else:
return
class DupStreetDesc(QRunnable):
"""
duplicate street description report section creation
:return: void if the report is to text
list[string] if the report is to screen
"""
def __init__(self):
super(DupStreetDesc,self).__init__()
self.signals = GeneralSignals()
self.task = "Checking duplicate street descriptions..."
self.progress = 16.6
def run(self):
self.signals.result.emit(self.task,self.progress)
if report_file is None:
print "report file is none "
# items_list = write_content(0, 0, 0, 0)
# for item in items_list:
# self.signals.list.emit(item)
else:
write_content(0, 0, 0, 0)
Now, I used this approach before and it has always worked fine without using multiprocessing. In this case it works good to some extent, I can run the tasks the first time but if I try to run for the second time I get the following Python Error :
self.long_task.start(run_class[0])
RuntimeError: wrapped C/C++ object of type DupStreetDesc has been deleted
I tried to use run_class[0].setAutoDelete(False) before running them in the loop but pyQt crashes with a minidump error (I am running the code in QGIS) and I the programs exists with few chances to understand what has happened.
On the other hand, if I run my classes separately, checking with an if else statement each check-box, then it works fine, I can run the tasks again and the C++ classes are not deleted, but it isn't a nice coding approach, at least from my very little experience.
Is there anyone else out there who can advise a different approach in order to make this run smoothly without using too many lines of code? Or knows whether there is a more efficient pattern to handle this problem, which I think must be quite common?
It seems that you should create a new instance of each runnable, and allow Qt to automatically delete it. So your dictionary entries could look like this:
'dupStreetCheckBox': [lambda: DupStreetDesc(), 0],
and then you can do:
for check_box_name in self.check_boxes_names:
run_class = self.summary_runnables[check_box_name]
runnable = run_class[0]()
runnable.signals.result.connect(self.log_progress)
self.long_task.start(runnable)
I don't know why setAutoDelete does not work (assuming you are calling it before starting the threadpool). I suppose there might be a bug, but it's impossible to be sure without having a fully-working example to test.

how can I give a variable another value inside a function in python?

I'm creating a simple python script that checks for new comments on a WordPress blog using the xmlrpc API.
I'm stuck with a loop that should tell me if there are new comments or not. Here's the code:
def checkComm():
old_commCount = 0;
server = xmlrpclib.ServerProxy(server_uri); # connect to WP server
comments = server.wp.getComments(blog_id, server_admin, admin_pass, filters);
new_commCount = len(comments);
if new_commCount > old_commCount:
print "there are new comments"
old_commCount = new_commCount
else:
print "no new comments"
while True:
checkComm()
time.sleep(60)
I've skipped the variables like blog_id, server_admin etc.. since they add nothing to this question.
Can you tell what is wrong with my code?
Thanks a lot in advance.
You want to pass it as a parameter, because you are resetting it every time you call the function:
def checkComm(old_commCount): # passed as a parameter
server = xmlrpclib.ServerProxy(server_uri) # connect to WP server
comments = server.wp.getComments(blog_id, server_admin, admin_pass, filters)
new_commCount = len(comments)
if new_commCount > old_commCount:
print "there are new comments"
old_commCount = new_commCount
return old_commCount # return it so you can update it
else:
print "no new comments"
return old_commCount
comm_count = 0 # initialize it here
while True:
comm_count = checkComm(comm_count) # update it every time
time.sleep(60)

Story Progression through if blocks and variables: Failure

Ok. I'm designing a small text-based RPG, however, I need to have the player be able to save the game. I've succeeded in doing this through using the pickle module, but I'm trying to get the player to be able to get back to their previous point in the storyline through using this variable I call 'storypointe'. Basically it would work like this:
if storypointe == 0:
#Story, story, stuff happens here...
storypointe += 1
if storypointe == 1:
#More story, more story, more stuff happens here....
I would then pickle the variable storypointe, and when the game is loaded (meaning using pickle.load to get player stats and storypointe from whatever file I pickled it into), and IDEALLY it would just start from whichever code block storypointe corresponds to. The actual code is way too much work for the writer and (perhaps) the readers, so I've written the following code that simulates the same environment and replicates the same problem.
storypointe = 0
jump = 0
spin = 0
dive = 0
roar = 0
savefile = "C:\Users\Sammu\The Folder\databin.txt"
import pickle, sys
def save ():
with open(savefile, 'w') as savebin:
actions = [jump, spin, dive, roar, storypointe]
pickle.dump (actions, savebin)
def load ():
with open(savefile, 'r') as loadbin:
actions2 = pickle.load (loadbin)
print actions2
jump = actions2[0]
spin = actions2[1]
dive = actions2[2]
roar = actions2[3]
storypointe = actions2[4]
#Begin the code#
gameIO = raw_input ('Would you like to load previous game?\n>>> ')
if gameIO in ['yes', 'load', 'load game', 'Yes', 'Load', 'Load game']:
load ()
if storypointe == 0:
action = raw_input ('Would you like to jump, spin, dive or roar?\n>>> ')
if action in ['jump', 'Jump']:
jump += 1
print jump
if action in ['spin', 'Spin']:
spin += 1
print spin
if action in ['dive', 'Dive']:
dive += 1
print dive
if action in ['roar', 'Roar']:
roar += 1
print roar
storypointe += 1
if storypointe == 1:
print "\n\nYou have progressed to the next stage"
save ()
So if storypointe is equal to actions2[4], then that must mean it should be equal to 1. But the problem here is that it's always following the first code block, starting from
action = raw_input ('#yadayadayada')
instead of:
print "You have progressed to the next stage"
I think you're confused about Python scoping.
Here you make a new variable at module level:
storypointe = 0
[...]
But here:
def load ():
with open(savefile, 'r') as loadbin:
actions2 = pickle.load (loadbin)
[...]
storypointe = actions2[4]
you simply make a new local name "storypointe" in the function load. It doesn't affect what storypointe is at the module level. I would store your variables in a class or a dict instead, which would also prevent having to do the actions2[i] stuff.
Rather than express your narrative as a bunch of if statements consider it as a state machine, if you express your story-line as a tree then you can easily store routes through the game as references to the next node in the tree, you can also store references (unique) to each node, allowing for easy saving and loading of positions.
See for example
class Node(object):
def __init__(self, parent, children=None):
self.parent = parent
self.children = children or {}
story = {}
story['a'] = Node(None)
story['b'] = Node(a)
story['a'].children['b'] = story['b']

Web2py CRUD.read() record created though non-filled condition

So i worte a function in Web2py to create a record into a table in the Database under a Condition, but Web2py create the record allthough that condition is not filled,
here is the function
def buy_product():
price = price_set(db,auth,'product')
balance = limit(db,auth,'settings','account_balance')
if balance !=None:
if balance < price:
form=redirect(URL('order'))
else:
form=crud.create(db.letter)
if form.accepts(request.vars, session):
tax = float(postage(form.vars.tax_type).replace("-","."))
##########################
# I'm talking about this #
##########################
if balance < (price + tax):
response.flash='You don\'t have enough balance to buy this product'
redirect(URL('not_processed'))
else:
function_1(....)
...
...
update_field(db,auth,'settings','account_balance',-price)
response.flash='Done'
redirect(URL('products'))
pass
elif form.errors:
response.flash='Error 01'
else:
pass
###############################
else:
form=redirect(URL('settings'))
return dict(form=form)
it's sepposed that when the Balance < price + tax the user should be redirected to not_processed without creating new record in the database.
but web2py redirect the user to not_processed and create the record without executing this part with the entered information from the user. so the user see that he bought something, when it has not processed (see below)
function_1(....)
...
...
update_field(db,auth,'settings','account_balance',-price)
response.flash='Done'
redirect(URL('products'))
pass
any idea ??
thank you
Crud manages inserts/updates internally and it does not use the form.accepts.
You have two options:
1 - Use SQLFORM
form=SQLFORM(db.letter)
if form.accepts(request.vars, session):
tax = float(postage(form.vars.tax_type).replace("-","."))
##########################
# I'm talking about this #
##########################
2 - Use crud events
def myfunction(form):
# do your stuff here
# it will be called only when form is accepted
def myotherfunction(form):
if form.errors:
#do something here in case of errors
#it will be called during the form validation
crud.settings.create_onvalidation = myotherfunction
crud.settings.create_onaccept = myfunction
#the above can be:
#crud.create_onaccept = lambda form: myfunction(form)
# define the above events before the creation of the form
form=crud.create(db.letter)
Note that SQLFORM is a bit different of crud, SQLFORM expects you to validate the form in form.accepts method, while crud does it internally and uses events as onvalidation, onaccept to custom validations.

Categories

Resources