py2neo cypher create several relations to central node in for loop - python

just starting out with neo4j, py2neo and Cypher.
I have encountered the following problem and google and my knowledge of what to ask have not yet given me an answer or a helpful hint in the right direction. Anyway:
Problem:
I don't know how to, in python/py2neo, create relations between a unique starting node and a number of following nodes that I create dynamically in a for loop.
Background:
I have a json object which defines a person object, who will have an id, and several properties, such as favourite colour, favourite food etc.
So at the start of my py2neo script I define my person. After this I loop through my json for every property this person has.
This works fine, and with no relations I end up with a neo4j chart with several nodes with the right parameters.
If I'm understanding the docs right I have to make a match to find my newly created person, for each new property I want to link. This seems absurd to me as I just created this person and still have the reference to the person object in memory. But for me it is unclear on how to actually write the code for creating the relation. Also, as a relative newbie in both python and Cypher, best practices are still an unknown to me.
What I understand is I can use py2neo
graph = Graph(http://...)
tx = graph.begin()
p = Node("Person", id)
tx.create(p)
and then I can reference p later on. But for my properties, of which there can be many, I create a string in python like so (pseudocode here, I have a nice oneliner for this that fits my actual case with lambda, join, map, format and so on)
for param in params:
par = "MERGE (par:" + param + ... )
tx.append(par)
tx.process()
tx.commit()
How do I create a relation "likes" back to the person for each and every par in the for loop?
Or do I need to rethink my whole solution?
Help?! :-)
//Jonas

Considering you've created a node Alice and you want to create the other as dynamic, I'll suggest while dynamically parsing through the nodes, store it everytime (in the loop) in a variable, make a node out of it and then implement in Relationship Syntax. The syntax is
Relationship(Node_1, Relation, Node_2)
Now key thing to know here is type(Node_1) and type(Node_2) both will be Node.
I've stored all the nodes (only their names) from json in a list named nodes.
Since you mentioned you only have reference to Alice
a = ("Person", name:"Alice")
for node in nodes: (excluding Alice)
= Node(, name:"")
= Relationship(a, ,
Make sure to iterate variable name, else it'll keep overwriting.

Related

Gremlin python - how to ignore property of type List when adding properties to vertex

I want to add persons as vertices in a graph which works with the following code:
from gremlin_python.process.graph_traversal import __
from gremlin_python.process.traversal import Column
persons = [{"id":1,"name":"bob","age":25}, {"id":2,"name":"joe","age":25,"occupation":"lawyer"}]
g.inject(persons).unfold().as_('entity').\
addV('entity').as_('v').\
sideEffect(__.select('entity').unfold().as_('kv').select('v').\
property(__.select('kv').by(Column.keys),
__.select('kv').by(Column.values)
)
).iterate()
Question 1:
What if one of the properties is a List or dict. Example:
persons = [{"id":1,"name":"bob","age":25, "house":{"a":1,"b":4}}, {"id":2,"name":"joe","age":25,"occupation":"lawyer","house":{"a":1,"b":4}}]
How do I ignore that 1 property (house) but still add the rest to the person vertex? Then take house and create another vertex (add properties a and b) with edge to person?
Question 2:
What if I want to modify an attribute before I add it as a property to the graph?
For example: Convert id into string and then add it as property
I could be wrong, but I sense that your question will end up being more complex than you've posted it. With that in mind, I will offer an answer that works given the assumption that each house is unique which I've made more clear with a "hid" (house id) that I've added to the data.
gremlin> persons = [["pid":1,"name":"bob","age":25, "house":["hid":10,"a":1,"b":4]],
......1> ["pid":2,"name":"joe","age":25,"occupation":"lawyer","house":["hid":20,"a":1,"b":4]]]
==>[pid:1,name:bob,age:25,house:[hid:10,a:1,b:4]]
==>[pid:2,name:joe,age:25,occupation:lawyer,house:[hid:20,a:1,b:4]]
gremlin> g.inject(persons).unfold().as('entity').
......1> addV('entity').as('v').
......2> sideEffect(select('entity').unfold().as('kv').select('v').
......3> choose(select('kv').by(keys).is('house'),
......4> addV('house').as('h').
......5> addE('owns').from('v').
......6> select('kv').by(values).unfold().as('hkv').select('h').
......7> property(select('hkv').by(keys),
......8> select('hkv').by(values)),
......9> property(select('kv').by(keys),
.....10> select('kv').by(values))))
==>v[0]
==>v[9]
gremlin> g.V().elementMap()
==>[id:0,label:entity,name:bob,pid:1,age:25]
==>[id:4,label:house,a:1,hid:10,b:4]
==>[id:9,label:entity,occupation:lawyer,name:joe,pid:2,age:25]
==>[id:14,label:house,a:1,hid:20,b:4]
gremlin> g.E().elementMap()
==>[id:5,label:owns,IN:[id:4,label:house],OUT:[id:0,label:entity]]
==>[id:15,label:owns,IN:[id:14,label:house],OUT:[id:9,label:entity]]
I've not really done anything new here, in that sense that I've largely just embedded the traversal pattern you were already using within itself. Note that at line 6 I'm just re-doing what was done on line 2 in the sideEffect().
Now, if my assumption was wrong about having unique houses in your data, then things get more complicated because you can't easily inline upsert traversal patterns in this context. Upserts typically involve a fold/coalesce/unfold pattern that immediately conflicts with this "insert only" pattern that you are using as you can't backtrack in a traversal (i.e. refer to a previous step) that is behind a reducing barrier (i.e. fold). I think I would try to restructure the source data in this case to make it more amenable for pure inserts rather than upsert operations.

Listing the title of created items in a choice field of another item in plone

I have a problem with my Plone item I cannot solve. 'Car' is supposed to create a list of all instances of 'Colour'.
All 'Colour' instances are in a given container. I cannot make it static because I want to add more 'Colour' instances in the future.
I tried selecting each item in my container and add it to my vocabularylist. I only need the id/title of my object, but I always end up with a giant stacktrace of failures.
In the end I want to choose a colour out of the given instances on creating a new 'Car' instance similar to a dropdown.
I have read the docs but cannot find a solution and this is my best idea.
I am also not a python programmer and this is my first plone project. I can add the complete failure list later if you need it.
I appreciate every bit of help. Thank you.
```colour= schema.Choice(
title=u"Colour",
description=u"Paintjob",
vocabulary=givecolour(),
required=False
)
#provider(IContextSourceBinder)
def givecolour():
colourlist = self.context.portal_catalog(path={"query" : "/madb-entw/it/colourcontainer", "depth" : 1})
list = []
i = 0
for colour in colourlist:
list.append(
SimpleVocabulary.createTerm(
colourlist.[i].getObject().id
)
)
i += 1
return SimpleVocabulary(list)```
Please always add your traces, so that we can help you better.
There is also the official community.plone.org forum, where are more people can help you.
I recommend you to use the plone.api to find your objects, this is a bit easier and well doumented.
something like this:
from plone import api
color_brains = api.content.find(context=api.content.get(path='/madb-entw/it/colourcontainer'), depth=1, portal_type='Color')
# no need to do getOject() here, get the id/title directly from the catalog brain
colors = [(color.id, color.Title) for color in color_brains]
One note to your query:
colourlist = self.context.portal_catalog(path={"query" :
"/madb-entw/it/colourcontainer", "depth" : 1})
Path has to be absolute, which means it includes the Plone site id and this can be different in another Plone site.
So an absolute path is not a good idea here, better get the portal object and traverse your path relative from there.
If madb-entw is your Plone site id:
portal.restrictedTraverse('it/colourcontainer')
or better as above, use plone.api.content.get(path='/it/colourcontainer')
Which is cleaner and easier.

What is the best way for allowing for dynamic object creation and removal in a Plotly Dash dashboard?

Currently, I have a class which stores a dictionary of Card elements, each of which is unique. The class can also generate these cards and append them to the dictionary or remove a card from a dictionary. However, I am not sure how to best allow for this action through a callback function since the ID for a card doesn't exist until the card is made, and the functionality isn't directly within the Dash framework since a dictionary object acts as an intermediary where the objects are stored.
Basically, I am wondering what the best way to dynamically create and destroy objects with a callback is?
Thank you!
Assuming you want to avoid extra computation for building cards you wont use, I'd suggest create a which creates each card and store those functions in a dictionary. (You can also create a universal function with params that allow specificity)
my_card_functions = {
'id_1': make_id_1,
'id_2': make_id_2,
}
Creating a card could be done as such:
my_id = 'id_1'
f = my_card_functions[my_id] # will break if id isn't registered
my_card = f()
You can store the cards you want to create in a dcc.store object. Here's an example of some code you might consider:
# Pretend these are structured properly
dcc.Store(id='cards_data')
html.Div(id='my_cards',children=[])
#app.callback(
Output('my_cards','children'),
[Input('cards_data','data')],
[State('my_cards','children')]
)
def make_cards(data, children):
"""Makes cards for each id"""
if not data:
raise PreventUpdate
# The following code is not correct but serves as a demonstrative example
# Some data structure manipulation is necessary to access
# the ids from the children elements
children_ids = [x['id'] for x in children]
# Assuming your data looks something like this:
# {'add':['id_1','id_2']}
for x in data['add']:
if x not in children_ids:
f = my_card_functions[my_id]
my_card = f()
# Store values
children.append(my_card)
return children
Note, this approach does not resolve removal of cards. That could easily be done but would probably require a more dynamic use of caching.
Just on the basis of your question, I have some immediate suggestions (since there is no working code that you have posted).
1. Generate all card elements by default. They can be generated, but not 'displayed'
2. Add your callbacks to toggle the display/rendering of the cards dynamically based on the use case. That way you will have card element ids to play around with in the callbacks.
Hope this helps.

Referencing objects which don't exist yet

I am teaching myself Python by reinventing the wheel, i.e. writing a simple console adventure game with interconnected rooms. My interest is not so much in finishing a real game, rather in learning about abstraction and data structures.
I have defined a few basic classes such as World, which contains references to Rooms. These Rooms have Actions and Items. The program checks which Actions are available for each Room and displays them in a list. All of this is working pretty well so I'm trying to complexify things a bit.
I have two problems I can't get my head around. I'll try to explain
them with as little detail as possible.
This is how I defined a possible Action for a Room, in this instance it's a Room referenced by the variable called main (for now data is declared in a module, later to be read from a CSV or XML file):
main.actions = [Action(type = 'move',
result = 'west',
desc = 'Go to the West Room.')]
I need to define an action type because some actions are not movements (e.g. pick up, pull a lever). I'll modify this later to uses subclasses of Action, it's not the issue here.
The 'west' string here refers to the Room which will be the result of the action, i.e. it will become the current room when the action is executed.
However, I'd like the result to be the Room object itself, not a
string ID. But I can't do that until all the Rooms have been initialized.
So I've solved this with the following method of the World object, which works:
def connect_rooms(self):
'''
Once all the rooms have been created,
replace all the string reference to other rooms by the Room objects themselves.
'''
for room in self.rooms:
for action in room.actions:
if action.type == 'move':
room_object = fetch_room(self, action.result)
action.result = room_object
The fetch_room() function (global scope) just does this:
def fetch_room(world, shortname):
# Find a room item by its shortname
for room in world.rooms:
if room.shortname == shortname:
return room
return None
I am sure there is a better way to handle this, since creating connections between nodes seems to be a basic abstract idea.
The other (related) problem is that I'm trying to create conditions built into the Actions themselves, so that the program only proposes them to the player if the conditions defined in the Action are met. For the initial building of the data I can't reference anything else because it hasn't been created yet. I thought of adding a condition in string form and run it later with exec(), but it seems very silly and ugly:
main.actions = [Action(type = 'move',
result = 'west',
desc = 'Go to the West Room.',
conditions = ["player.has('smallkey')"])]
If there is a text somewhere about build such data structures without going insane, I'd be happy to read it.
Thank you.

Access revit element data - python revit api

I'm a little confused about how you get access to Revit's element data, such as an element's parameters, location, Id, etc.. If I have this code:
collector = FilteredElementCollector(doc)
collector.OfCategory(BuiltInCategory.OST_Walls)
walls = collector.OfClass(FamilySymbol)
return walls
It will print: Autodesk.Revit.DB.FilteredElementCollector object at 0x0000000000000038 [Auto...]. Where do I go from here? For instance, How do I get a return of the walls' location?
There might be a lot in here, and multiple steps for each item. I am mainly looking for a general concept of getting and/or setting new element data.
Any thoughts?
I can't help with the Python, but I'm pretty familiar with Revit's API + C#.
You are using the collector to list all the walls on the project. What you want (to get the locations) is the FamilyInstance objects of these walls.
In C# would be something like this:
new FilteredElementCollector(uidoc.Document).OfClass(FamilyInstance).ToElements();
Next, you should loop the result to get each individual Element and convert it to a Wall:
foreach (Wall i in instances)
{
var location = i.Location as LocationCurve;
// The Curve element is a Line - 2 points defining it's position
var p0 = location.Curve.GetEndPoint(0);
var p1 = location.Curve.GetEndPoint(1);
}
Most of the information you want is on this FamilyInstance Object -> http://wikihelp.autodesk.com/Revit/enu/2014/Help/3665-Developers/0074-Revit_Ge74/0083-Family_I83/0086-FamilyIn86
The Revit API documentation points out that a FilteredElementCollector is an IEnumerable<Element>. So you actually have a list of wall objects. I like to add these to a python list to make working with them easier:
walls = list(collector)
Behind the scenes, list(collector) will do something like:
walls = []
for w in collector:
walls.append(w)
(note, that this is not really how it works, but sort of explains it).
You can use the .NET inner workings to enumerate the walls in the collector by doing this:
enumerator = collector.GetEnumerator()
walls = []
while not enumerator.IsDone():
walls.append(enumerator.Current)
enumerator.MoveNext()
You will want to check if the collector.OfClass(FamilySymbol) line is correct - in my example document, that yielded an empty list - but maybe you do have walls that are FamilySymbols...
Next, you want to work with a wall object. So, take the first wall:
wall = walls[0]
interior_type_parameter = wall.Parameter['Interior Type']
And then work with the parameter... If you install the Revit SDK, you will find a tool for snooping objects and finding their parameters and values. Use this! Explore! Have fun!

Categories

Resources