I am trying to update Dynamo Table using expression, I am trying to generate keys dynamically. The issue looks like if they key has space in it, expressions don't evaluate properly.
Following in my update implementation:
update_expression = 'SET {}'.format(','.join(f'#{p}=:{p}' for p in row_as_dict))
expression_attribute_values = {f':{p}': v for p, v in row_as_dict.items()}
expression_attribute_names = {f'#{p}': p for p in row_as_dict}
if result_set.get('Item') and result_set.get('Item').get(primary_key):
TABLE.update_item(
Key={
primary_key: row_as_dict[primary_key],
sort_key: row_as_dict[sort_key]
},
UpdateExpression=update_expression,
ExpressionAttributeValues=expression_attribute_values,
ExpressionAttributeNames=expression_attribute_names
)
if i print the values for update_expression and expression_attribute_values and expression_attribute_names i get following output.
update_expression
SET #Ref No=:Ref No,#FT/Hot=:FT/Hot,#Irfan watch List=:Irfan watch List,#F.Soahil watchList=:F.Soahil watchList,#HR Responible=:HR Responible,#Pipeline(Adv/Ref)=:Pipeline
(Adv/Ref),#Source=:Source,#Date Submitted=:Date Submitted,#Name=:Name,#Candidate Location=:Candidate Location,#Tech =:Tech ,#Current Company=:Current Company,#Exp=:Exp,#Position being considered for=:Position being considered for,#Proj. / Gen Hiring=:Proj. / Gen Hiring,#Comments / Latest Status / Next Step=:Comments / Latest Status / Next Step,#Status=:Status,#hiring status=:hiring status,#Link to Lever=:Link to Lever,#LinkedIn Link / Resume=:LinkedIn Link / Resume,#Interview Feedback Google Doc=:Interview Feedback Google Doc,#Email=:Email
expression_attribute_values
{':Ref No': '1', ':FT/Hot': 'NaN', ':Irfan watch List': 'NaN', ':F.Soahil watchList': 'NaN', ':HR Responible': 'Khurram', ':Pipeline\r\n(Adv/Ref)': 'Referred', ':Source': 'Usman Khan', ':Date Submitted': '17-Dec', ':Name': 'Asif Mahmood Mughal Zain', ':Candidate Location': 'ISL', ':Tech ': 'Spark. Hadoop\r\nSQL, Big Data', ':Current Company': 'Zain Telecom', ':Exp': '18+', ':Position being considered for': 'Senior SA', ':Proj. / Gen Hiring': 'General', ':Comments / Latest Status / Next Step': 'Muhammaf Naseer Recommneded\r\nAdeel Ashraf Recommended\r\nFaheem Khan Recommended\r\n\r\nAccepted offer.will join on 6 jan', ':Status': 'Joined', ':hiring status': 'Hired', ':Link to Lever': 'Link to Lever', ':LinkedIn Link / Resume': 'Asif Mehmood Mughal', ':Interview Feedback Google Doc': 'Link to Feedback', ':Email': 'some_email1#test.com'}
expression_attribute_names
{'#Ref No': 'Ref No', '#FT/Hot': 'FT/Hot', '#Irfan watch List': 'Irfan watch List', '#F.Soahil watchList': 'F.Soahil watchList', '#HR Responible': 'HR Responible', '#Pipeline\r\n(Adv/Ref)': 'Pipeline\r\n(Adv/Ref)', '#Source': 'Source', '#Date Submitted': 'Date Submitted', '#Name': 'Name', '#Candidate Location': 'Candidate Location', '#Tech ': 'Tech ', '#Current Company': 'Current Company', '#Exp': 'Exp', '#Position being considered for': 'Position being considered for', '#Proj. / Gen Hiring': 'Proj. / Gen Hiring', '#Comments / Latest Status / Next Step': 'Comments / Latest Status / Next Step', '#Status': 'Status', '#hiring status': 'hiring status', '#Link to Lever': 'Link to Lever', '#LinkedIn Link / Resume': 'LinkedIn Link / Resume', '#Interview Feedback Google Doc': 'Interview Feedback Google Doc', '#Email': 'Email'}
The Error generated is
An error occurred (ValidationException) when calling the UpdateItem operation: ExpressionAttributeValues contains invalid key: Syntax error; key: ":hiring status"
You can't have spaces in your attribute keys/values in the expression. So :hiring status needs to be something like :hiring_status.
Go Dynamof!
You might be very interested in dynamof. Its a library purposed to do exactly what your trying to do. At the very least you can take a peak and check out how it handles dynamically creating all the expressions.
I suggest you take a look at the args module.
It has the functions that do what your trying to do. The args module gets some help from another module (the request builder) in dynamof that is responsible for some preprocessing of the entire action's arguments so the args module's functions get an easy to use and parse RequestTree object.
Answer...
I would just use dynamof - but I'm biased. If you want to write it yourself, heres a little example on how its been done straight from dynamof.
def UpdateExpression(request: RequestTree):
def expression(attr):
if attr.func is not None:
return attr.func.expression(attr)
return f'{attr.alias} = {attr.key}'
key_expressions = [expression(key) for key in request.attributes.values]
key_expression = ', '.join(key_expressions)
return f'SET {key_expression}'
NOTE: The attr.alias is whats used to handle the special name cases - like spaces. Its set in the request builder module mentioned above.
disclaimer: I wrote dynamof
Related
I took this to the Godot Reddit first, but honestly it's not much help imo. A lot of questions go unanswered there. So here I am.
As the title says, Im making a quest system in godot 2d using nested dictionaries. Ive seen people use Nodes, Classes or otherwise for their quest systems, and a few dictionary based ones out there. I chose dictionaries as I know them the best(still isnt a whole lot, or i probably wouldnt be here asking this lol) And my quest system is set up like so:
QuestBase --> QuestHandler --> PlayerData
QuestBase is a giant dictionary of all available quests in the game.
PlayerData is a giant dictionary of all the player stats(including active, completed, or failed quests)
and QuestHandler takes a questName in a function to Copy the quest(questName) from QuestBase dict into the PlayerData.quests_active dict.(quests_active is a dictionary of quests(also dictionaries) inside of the PlayerData dictionary lol) But i cant seem to get it to work. I've done it a couple different ways now, including the way the Godot documentation states on how to add Dinctionaries into dictionaries. Please help, this is the error I get from QuestHandler:
Invalid set index 'tutorialQuest' (on base: 'Nil') with value of type 'Dictionary'
QuestBase:
var storyQuests = {
"tutorialQuest":{
"name" : "Your First Steps",#Name of the Quest
"desc" : "Get to know your environment and learn the ropes.",#Description of the Quest
"level" : 1, #Required level before player can accept quest
"questType" : 0, #0=Story Quest || 1=Side Quest || 2=Repeateable Quest
"taskType": 0, #0=Progressive(task must be completed in order) || 1=Synchronous(tasks can be completed in any order, and all at once)
"tasks":{#Dictionary of Quest's Tasks
"task1":{
"text":"Talk to Bjorn",#Text to display in Quest log
"type":0,#0=Talk,1=Slay,2=Fetch,3=Deliver,4=Collect,5=Goto
"quantity":0,#Determines the amount to complete task for Slay, Fetch, and Collect
"target":"Bjorn",#Determines Who to talk to, or who to slay, or what to collect.
"completed":false#Is this task complete?
},
"task2":{
"text":"Talk to Bjorn",
"type":"Talk",
"target":"Bjorn",
"completed":false
},
"task3":{
"text":"Talk to Bjorn",
"type":"Talk",
"target":"Bjorn",
"completed":false
}
},
"itemReward":{
"items":["Sword", "Basic Elixer"],#Names of items to give
"amount":[1, 5]#Amount of each item to give, respectively.
},
"expReward":{
"skill":["Forestry","Smithing"],#Names of skills to add Exp to
"amount": [100,100] #Amount to add to each skill, respectively.
#1st number will increase first skill in "skill", etc...
},
"Complete":false,
"Failed":false
}
}
PlayerData:
var playerData = {
... irrelevant player data...
#Quests
"quests_active": {"blank":"blank"},
"quests_completed": {},
"quests_failed": {},
and Finally, Quest Handler:
func startQuest(questName: String):
var active_quests = PlayerData.playerData.get("quests_active")
if QuestBase.storyQuests.has(questName):
var questCopy = QuestBase.storyQuests.get(questName).duplicate(true)
PlayerData.playerData.get("quests_active")[questName] = questCopy #.quests_active.append(questCopy)
#print("Story Quest Started: " + PlayerData.playerData.QuestsActive.get(questName).get("name"))
elif QuestBase.sideQuests.has(questName):
var questCopy = QuestBase.sideQuests.get(questName).duplicate(true)
active_quests.append(questCopy)
#print("Side Quest Started: " + PlayerData.playerData.QuestsActive.get(questName).get("name"))
else:
print("Quest Doesn't Exist! Check Spelling!")
I have a nested dictionary
users={'user502':{'firstname':'James' , 'lastname':'Jones'}}
User can search for first or last name and be able to update to something else. I wrote the following code:
name_change=input('Enter name to change: ')
for key,value in users.items():
for k,v in value.items():
if name_change==v:
#print('name found in', value.get(name_change))
#print('name found in', value.get(k))
print('name found in', k)
updated_name=input('What would be the new name: ')
users.update(k=updated_name)
break
else:
print('name not found')
I am getting RuntimeError: dictionary changed size during iteration error. I did some research on update and also looked at this stackoverflow discussion Update value in nested dictionary - Python
I am not able to figure out how to point the code to pick first name or last name based on whatever user enters. Some hints would be helpful.
Thanks in advance.
**From other discussion it seems like in Python 3 it does not work because of '3.x because keys returns an iterator instead of a list.'
I tried to do a pop first and then update
updated_name=input('What would be the new name: ')
# users[k][v]=updated_name
x= value.get(k)
users.pop(x)
users[x]=updated_name
However now I get KeyError: 'James'
This code is doing what I wanted. May be my question was not clear sorry about that
print('Actual-', users)
name_change=input('Enter name to change: ')
for key,value in users.items():
for k,v in value.items():
if name_change==v:
#print('name found in', value.get(name_change))
#print('name found in', value.get(k))
print('name found in', k)
updated_name=input('What would be the new name: ')
value[k]=updated_name
break
else:
print('name not found')
print("modified name:", users)
Output:
Actual- {'user502': {'lastname': 'Jones', 'firstname': 'James'}}
Enter name to change: Jones
name found in lastname
What would be the new name: January
modified name: {'user502': {'lastname': 'January', 'firstname': 'James'}}
I'm using AutoIt and SciTE to create an installation script. The problem I am running into is that there is a tree menu for selection of features. I can select the whole treeview (SysTreeView32), but am not sure how to get inside it to check the boxes without doing a mouse move and click (not a great option).
The Treeview looks like this:
The Control Info from AutoIT is like this:
I'm sure it is possible, just can't figure out how to do it. This is my first attempt a such a script. Creating a response file does not work for this exe for some reason. So - this appears to be my only way out to create a somewhat silent install (not silent anymore, but at least automated).
* EDIT - Current State of Things *
I did figure out how to do some of this, but I still can't figure out if the items is selected before accessing it. So - since it toggles, I could be turning off a feature I want!
$hWnd = WinWaitActive($WindowTitle, 'Select Features')
$tvCtl = ControlGetHandle($WindowTitle, '', 'SysTreeView321')
$firstItem = _GUICtrlTreeView_FindItem($tvCtl, 'eBooks')
_GUICtrlTreeView_SelectItem($tvCtl, $firstItem, $TVGN_FIRSTVISIBLE)
_GUICtrlTreeView_ClickItem($tvCtl, $firstItem, "left", True, 1)
Send('{SPACE}')
I wouldn't think I would have to send the space since I sent the ClickItem, but seems so.
I could also do this:
ControlTreeView($hWnd, '', $tvCtl, 'Select', '#0')
ControlSend($hWnd, '', $tvCtl, ' ')
That will toggle the first one. So - i can count them all up and do it that way.
But when I check for "IsEnabled" or "IsChecked", it always says NO. So - I can't check the ones I need only. I have to hope their status is what I expect.
Here is how I am checking "IsChecked" and "IsEnabled":
If ControlCommand($hWnd, '', $logTool, 'IsEnabled') then
ConsoleWrite('Log Tool - IsEnabled' & #CRLF)
Else
ConsoleWrite('Log Tool - NOTEnabled' & #CRLF)
EndIf
and
If ControlCommand($hWnd, '', $logTool, 'IsChecked') then
ConsoleWrite('Log Tool - IsChecked' & #CRLF)
Else
ConsoleWrite('Log Tool - NOTChecked' & #CRLF)
EndIf
It always comes back NOTEnabled and NOTChecked. I made sure that I ran the same procedure above: FindItem, SelectItem, ClickItem. And, the correct item is highlighted/selected when this procedure is run - I can see that. So - it just isn't returning a proper value.
Opt('WinTitleMatchMode', 2)
$hWnd = WinGetHandle("InstallShield Wizard") ; Notice the correct title
$hTree = ControlGetHandle($hWnd, '', "[CLASS:SysTreeView32;INSTANCE:1]")
; == Now you can interact with the treeview with functions from "GuiTreeView.au3"
EDIT:
Try this
; Select the item so:
_GUICtrlTreeView_SelectItem($hTree, $hItem, $TVGN_CARET)
; Get checked state:
_GUICtrlTreeView_GetChecked($hTree, $hItem)
For more deatails read the AutoIt help.
My goal is to access and mutate a property of a node in a cypher query where the name of the property to be accessed and mutated is an unknown string value.
For example, consider a command:
Find all nodes containing a two properties such that the name of the first property is lower-case and the name of the latter is the upper-case representation of the former. Then, propagate the value of the property with the lower-case string name to the value of the property with the upper-case name.
The particular case is easy:
MATCH ( node )
WHERE has(node.age) AND has(node.AGE) AND node.age <> node.AGE
SET node.AGE = node.age
RETURN node;
But I can't seem to find a way to implement the general case in a single request.
Specifically, I am unable to:
Access the property of the node with a string and a value
Mutate the property of the node with a string and a value
For the sake of clarity, I'll include my attempt to handle the general case. Where I failed to modify the property of the node I was able to generate the cypher for a command that would accomplish my end goal if it were executed in a subsequent transaction.
MERGE ( justToMakeSureOneExists { age: 14, AGE : 140 } ) WITH justToMakeSureOneExists
MATCH (node)
WHERE ANY ( kx IN keys(node) WHERE kx = LOWER(kx) AND ANY ( ky in keys(node) WHERE ky = UPPER(kx) ) )
REMOVE node.name_conflicts // make sure results are current
FOREACH(kx in keys(node) |
SET node.name_conflicts
= COALESCE(node.name_conflicts,[])
+ CASE kx
WHEN lower(kx)
THEN []
+ CASE WHEN any ( ky in keys(node) WHERE ky = upper(kx) )
THEN ['match (node) where id(node) = ' + id(node)+ ' and node.' + upper(kx) + ' <> node.' + kx + ' set node.' + upper(kx) + ' = node.' + kx + ' return node;']
ELSE [] END
ELSE []
END )
RETURN node,keys(node)
Afterthought: It seems like the ability to mutate a node property by property name would be a pretty common requirement, but the lack of obvious support for the feature leads me to believe that the feature was omitted deliberately? If this feature is indeed unsupported is there any documentation to explain why and if there is some conflict between the approach and the recommended way of doing things in Neo/Cypher?
There is some discussion going on regarding improved support for dynamic property access in Cypher. I'm pretty confident that we will see support for this in the future, but I cannot comment on a target release nor on a date.
As a workaround I'd recommend implementing that into a unmanaged extension.
It appears that the desired language feature was added to Cypher in Neo4j 2.3.0 under the name "dynamic property". The Cypher docs from version 2.3.0-up declare the following syntax group as a valid cypher expression:
A dynamic property: n["prop"], rel[n.city + n.zip], map[coll[0]].
This feature is documented for 2.3.0 but is absent from the previous version (2.2.9).
Thank you Neo4j Team!
I'm currently implementing a SBT plugin for Gatling.
One of its features will be to open the last generated report in a new browser tab from SBT.
As each run can have a different "simulation ID" (basically a simple string), I'd like to offer tab completion on simulation ids.
An example :
Running the Gatling SBT plugin will produce several folders (named from simulationId + date of report generaation) in target/gatling, for example mysim-20140204234534, myothersim-20140203124534 and yetanothersim-20140204234534.
Let's call the task lastReport.
If someone start typing lastReport my, I'd like to filter out tab-completion to only suggest mysim and myothersim.
Getting the simulation ID is a breeze, but how can help the parser and filter out suggestions so that it only suggest an existing simulation ID ?
To sum up, I'd like to do what testOnly do, in a way : I only want to suggest things that make sense in my context.
Thanks in advance for your answers,
Pierre
Edit : As I got a bit stuck after my latest tries, here is the code of my inputTask, in it's current state :
package io.gatling.sbt
import sbt._
import sbt.complete.{ DefaultParsers, Parser }
import io.gatling.sbt.Utils._
object GatlingTasks {
val lastReport = inputKey[Unit]("Open last report in browser")
val allSimulationIds = taskKey[Set[String]]("List of simulation ids found in reports folder")
val allReports = taskKey[List[Report]]("List of all reports by simulation id and timestamp")
def findAllReports(reportsFolder: File): List[Report] = {
val allDirectories = (reportsFolder ** DirectoryFilter.&&(new PatternFilter(reportFolderRegex.pattern))).get
allDirectories.map(file => (file, reportFolderRegex.findFirstMatchIn(file.getPath).get)).map {
case (file, regexMatch) => Report(file, regexMatch.group(1), regexMatch.group(2))
}.toList
}
def findAllSimulationIds(allReports: Seq[Report]): Set[String] = allReports.map(_.simulationId).distinct.toSet
def openLastReport(allReports: List[Report], allSimulationIds: Set[String]): Unit = {
def simulationIdParser(allSimulationIds: Set[String]): Parser[Option[String]] =
DefaultParsers.ID.examples(allSimulationIds, check = true).?
def filterReportsIfSimulationIdSelected(allReports: List[Report], simulationId: Option[String]): List[Report] =
simulationId match {
case Some(id) => allReports.filter(_.simulationId == id)
case None => allReports
}
Def.inputTaskDyn {
val selectedSimulationId = simulationIdParser(allSimulationIds).parsed
val filteredReports = filterReportsIfSimulationIdSelected(allReports, selectedSimulationId)
val reportsSortedByDate = filteredReports.sorted.map(_.path)
Def.task(reportsSortedByDate.headOption.foreach(file => openInBrowser((file / "index.html").toURI)))
}
}
}
Of course, openReport is called using the results of allReports and allSimulationIds tasks.
I think I'm close to a functioning input task but I'm still missing something...
Def.inputTaskDyn returns a value of type InputTask[T] and doesn't perform any side effects. The result needs to be bound to an InputKey, like lastReport. The return type of openLastReport is Unit, which means that openLastReport will construct a value that will be discarded, effectively doing nothing useful. Instead, have:
def openLastReport(...): InputTask[...] = ...
lastReport := openLastReport(...).evaluated
(Or, the implementation of openLastReport can be inlined into the right hand side of :=)
You probably don't need inputTaskDyn, but just inputTask. You only need inputTaskDyn if you need to return a task. Otherwise, use inputTask and drop the Def.task.