Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
df45e29
New models and methods
Mar 3, 2019
4b8db77
New relations cased according to the rest of the project.
Mar 4, 2019
b063698
Name change for relations between contact and organization and small …
Mar 4, 2019
501bf69
New methods, nodes and relationships added to the test suite.
Mar 4, 2019
1b63629
Parent relationship bugfix
Mar 6, 2019
2fce4cf
Merge pull request #2 from PaKZer0/newmodels
PaKZer0 Mar 6, 2019
5228e9b
Defects detected on PR fixed
Mar 6, 2019
a38610a
Updated .travis.yml and added jenkins.yaml
johanlundberg Mar 7, 2019
6cf9ab3
Install wheel
johanlundberg Mar 7, 2019
87e61dd
New ContactModel method
Mar 8, 2019
99ad93c
'Has' relationship removed from query
Mar 8, 2019
ed8d0a7
Dict merge simplified.
Mar 8, 2019
9acc960
Merge pull request #2 from PaKZer0/master
johanlundberg Mar 8, 2019
d94620a
New method: add child organization
Mar 13, 2019
0325300
New method to get all the outgoing relations for Contacts
Mar 14, 2019
ee384cc
Added tests for some of the new methods.
Mar 15, 2019
b61891d
Merge pull request #3 from PaKZer0/master
johanlundberg Mar 19, 2019
6eff922
New methods: one for group and one for organization
Mar 20, 2019
abb0297
New tests for the new methods
Mar 20, 2019
283637a
Merge pull request #4 from PaKZer0/master
johanlundberg Mar 20, 2019
cdb7942
Merge pull request #3 from SUNET/master
PaKZer0 Mar 25, 2019
18bcc7c
New organization methods
Apr 8, 2019
020561b
Merge pull request #5 from PaKZer0/master
johanlundberg Apr 9, 2019
12903d8
Implementing methods RoleRelationship
Jun 3, 2019
3f882a6
Removed old methods and tests changed accordingly
Jun 4, 2019
a5c4889
Added load_from_nodes
Jun 4, 2019
ebb990f
Return distinct nodes
Jun 5, 2019
8eb2225
db manager as param in static methods and list roles testing
Jun 6, 2019
5a1ef56
Merge branch 'master' of https://github.com/SUNET/python-norduniclien…
Jun 7, 2019
fb359f3
Merge pull request #6 from SUNET/rolefix
johanlundberg Jun 10, 2019
f0f729c
New methods for roles, organizations and contacts
Jun 12, 2019
983dc9c
Merge pull request #7 from SUNET/rolefix
johanlundberg Jun 12, 2019
5848a98
Method to get all the persons with a determined rolename
Jun 14, 2019
64313c9
Merge pull request #8 from SUNET/rolefix
johanlundberg Jun 17, 2019
6619be3
Just a simple name change
Jun 28, 2019
f3122b4
Merge pull request #9 from SUNET/quickfix
johanlundberg Jul 2, 2019
68f50cb
Added organization to the result set
Jul 16, 2019
aa47996
Change in link method, now the handle_id is required
Jul 18, 2019
75890dc
New helper function
Jul 19, 2019
4aa8b13
RoleRelation: handle_id init, two methods for contact or role and delete
Jul 23, 2019
23ed9a2
Role: Added specific method to get a contact with a role
Jul 23, 2019
d90adb2
Deleted outdated methods, some renamed, the new ones added to the tests
Jul 24, 2019
b17ebf8
The role id lookup has been reverted to a name lookup in all methods
Jul 26, 2019
fcf2e69
Merge pull request #10 from SUNET/orgsandcntct
johanlundberg Jul 26, 2019
3b6b25c
Merge branch 'master' of https://github.com/NORDUnet/python-nordunicl…
johanlundberg Jul 29, 2019
6d3c9f4
Relationships returned are now Relationship objects not dicts
johanlundberg Jul 29, 2019
29b7aed
Remove SUNET specific files
johanlundberg Jul 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
336 changes: 334 additions & 2 deletions norduniclient/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# Python 3
from norduniclient import core

import six

__author__ = 'lundberg'


Expand Down Expand Up @@ -189,6 +191,29 @@ def delete(self):
def reload(self, node=None):
return core.get_node_model(self.manager, self.handle_id, node=node)

def add_property(self, property, value):
if isinstance(value, six.string_types):
value = "'{}'".format(value)

q = """
MATCH (n:Node {{handle_id: {{handle_id}}}})
SET n.{property} = {value}
RETURN n
""".format(property=property, value=value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that it matters a lot (and our code is probably full of these things all over the place), but this is not injection safe.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think setting properties ever was injection safe (and I think you are correct that the problem can be found elsewhere to).

with self.manager.session as s:
node = s.run(q, {'handle_id': self.handle_id}).single()['n']
return self.reload(node=node)

def remove_property(self, property):
q = """
MATCH (n:Node {{handle_id: {{handle_id}}}})
REMOVE n.{property}
RETURN n
""".format(property=property)
with self.manager.session as s:
node = s.run(q, {'handle_id': self.handle_id}).single()['n']
return self.reload(node=node)


class CommonQueries(BaseNodeModel):

Expand Down Expand Up @@ -216,7 +241,7 @@ def get_child_form_data(self, node_type):

def get_relations(self):
q = """
MATCH (n:Node {handle_id: {handle_id}})<-[r:Owns|Uses|Provides|Responsible_for]-(node)
MATCH (n:Node {handle_id: {handle_id}})<-[r:Owns|Uses|Provides|Responsible_for|Works_for|Parent_of|Member_of|Uses_a]-(node)
RETURN r, node
"""
return self._basic_read_query_to_dict(q)
Expand Down Expand Up @@ -277,7 +302,6 @@ def get_ports(self):
"""
return core.query_to_list(self.manager, q, handle_id=self.handle_id)


class LogicalModel(CommonQueries):

def get_part_of(self):
Expand Down Expand Up @@ -892,6 +916,314 @@ class FPCModel(SubEquipmentModel):
class CustomerModel(RelationModel):
pass

class OrganizationModel(RelationModel):
def set_parent(self, org_handle_id):
q = """
MATCH (n:Node:Organization {handle_id: {handle_id}}), (m:Node:Organization {handle_id: {org_handle_id}})
MERGE (m)-[r:Parent_of]->(n)
RETURN m as created, r, n as node
"""
return self._basic_write_query_to_dict(q, org_handle_id=org_handle_id)

def set_child(self, org_handle_id):
q = """
MATCH (n:Node:Organization {handle_id: {handle_id}}), (m:Node:Organization {handle_id: {org_handle_id}})
MERGE (n)-[r:Parent_of]->(m)
RETURN m as created, r, n as node
"""
return self._basic_write_query_to_dict(q, org_handle_id=org_handle_id)

def add_procedure(self, proc_handle_id):
q = """
MATCH (n:Node:Organization {handle_id: {handle_id}}), (m:Node:Procedure {handle_id: {proc_handle_id}})
MERGE (n)-[r:Uses_a]->(m)
RETURN m as created, r, n as node
"""
return self._basic_write_query_to_dict(q, proc_handle_id=proc_handle_id)

def get_outgoing_relations(self):
q = """
MATCH (n:Node:Organization {handle_id: {handle_id}})-[r:Parent|Uses_a]->(node)
RETURN r, node
"""
return self._basic_read_query_to_dict(q)

def get_contacts(self):
q = """
MATCH (c:Node:Contact)-[:Works_for]->(o:Node:Organization)
WHERE o.handle_id = {handle_id}
RETURN DISTINCT c.handle_id as handle_id, c.name as name
"""
return core.query_to_list(self.manager, q, handle_id=self.handle_id)


class ProviderModel(RelationModel):
pass


class ContactModel(RelationModel):
def add_group(self, group_handle_id):
q = """
MATCH (n:Node:Contact {handle_id: {handle_id}}), (m:Node:Group {handle_id: {group_handle_id}})
MERGE (n)-[r:Member_of]->(m)
RETURN m as created, r, n as node
"""
return self._basic_write_query_to_dict(q, group_handle_id=group_handle_id)

def get_outgoing_relations(self):
q = """
MATCH (n:Node:Contact {handle_id: {handle_id}})-[r:Works_for|Member_of]->(node)
RETURN r, node
"""
return self._basic_read_query_to_dict(q)

class GroupModel(LogicalModel):
def add_member(self, contact_handle_id):
q = """
MATCH (n:Node:Contact {handle_id: {contact_handle_id}}), (m:Node:Group {handle_id: {handle_id}})
MERGE (n)-[r:Member_of]->(m)
RETURN m as created, r, n as node
"""
return self._basic_write_query_to_dict(q, contact_handle_id=contact_handle_id)


class RoleRelationship(BaseRelationshipModel):
RELATION_NAME = 'Works_for'
DEFAULT_ROLE_NAME = 'Employee'

def __init__(self, manager):
super(RoleRelationship, self).__init__(manager)
self.type = RoleRelationship.RELATION_NAME
self.name = None
self.handle_id = None

def load(self, relationship_bundle):
super(RoleRelationship, self).load(relationship_bundle)
self.type = RoleRelationship.RELATION_NAME
self.name = self.data.get('name', None)
self.handle_id = self.data.get('handle_id', None)

return self

@classmethod
def get_manager(cls, manager):
if not manager:
manager = core.GraphDB.get_instance().manager

return manager

@classmethod
def link_contact_organization(cls, contact_id, organization_id, role_name, manager=None):
if isinstance(contact_id, six.string_types):
contact_id = "'{}'".format(contact_id)

if isinstance(organization_id, six.string_types):
organization_id = "'{}'".format(organization_id)

if not role_name:
role_name = cls.DEFAULT_ROLE_NAME

# create relation
manager = cls.get_manager(manager)

q = """
MATCH (c:Contact), (o:Organization)
WHERE c.handle_id = {contact_id} AND o.handle_id = {organization_id}
MERGE (c)-[r:Works_for {{ name: '{role_name}'}}]->(o)
RETURN ID(r) as relation_id
""".format(contact_id=contact_id, organization_id=organization_id, role_name=role_name)
ret = core.query_to_dict(manager, q)

# load and return
if ret:
relation_id = ret['relation_id']
relation = cls.get_relationship_model(manager, relationship_id=relation_id)

return relation

@classmethod
def get_role_relation_from_organization(cls, organization_id, role_name, manager=None):
if isinstance(organization_id, six.string_types):
organization_id = "'{}'".format(organization_id)

manager = cls.get_manager(manager)

q = """
MATCH (c:Node:Contact)-[r:Works_for]->(o:Node:Organization)
WHERE r.name = "{role_name}" AND o.handle_id = {organization_id}
RETURN ID(r) as relation_id
""".format(role_name=role_name, organization_id=organization_id)
ret = core.query_to_dict(manager, q)

if ret:
relation_id = ret['relation_id']
relation = cls.get_relationship_model(manager, relationship_id=relation_id)

return relation

@classmethod
def get_contact_with_role_in_organization(cls, organization_id, role_name, manager=None):
if isinstance(organization_id, six.string_types):
organization_id = "'{}'".format(organization_id)

manager = cls.get_manager(manager)

q = """
MATCH (c:Node:Contact)-[r:Works_for]->(o:Node:Organization)
WHERE r.name = "{role_name}" AND o.handle_id = {organization_id}
RETURN c.handle_id as contact_handle_id
""".format(role_name=role_name, organization_id=organization_id)
ret = core.query_to_dict(manager, q)

if ret:
return ret['contact_handle_id']

@classmethod
def get_role_relation_from_contact_organization(cls, organization_id, role_name, contact_id, manager=None):
if isinstance(contact_id, six.string_types):
contact_id = "'{}'".format(contact_id)

if isinstance(organization_id, six.string_types):
organization_id = "'{}'".format(organization_id)

manager = cls.get_manager(manager)

q = """
MATCH (c:Node:Contact)-[r:Works_for]->(o:Node:Organization)
WHERE c.handle_id = {contact_id}
AND r.name = "{role_name}"
AND o.handle_id = {organization_id}
RETURN ID(r) as relation_id
""".format(contact_id=contact_id, role_name=role_name,
organization_id=organization_id)
ret = core.query_to_dict(manager, q)

if ret:
relation_id = ret['relation_id']
relation = cls.get_relationship_model(manager, relationship_id=relation_id)

return relation

@classmethod
def unlink_contact_with_role_organization(cls, contact_id, organization_id, role_name, manager=None):
if isinstance(contact_id, six.string_types):
contact_id = "'{}'".format(contact_id)

if isinstance(organization_id, six.string_types):
organization_id = "'{}'".format(organization_id)

manager = cls.get_manager(manager)

q = '''
MATCH (c:Contact)-[r:Works_for]->(o:Organization)
WHERE c.handle_id = {contact_id}
AND r.name = "{role_name}"
AND o.handle_id = {organization_id}
DELETE r RETURN c
'''.format(contact_id=contact_id, role_name=role_name, \
organization_id=organization_id)
ret = core.query_to_dict(manager, q)

@classmethod
def update_roles_withname(cls, role_name, new_name, manager=None):
manager = cls.get_manager(manager)

q = """
MATCH (c:Contact)-[r:Works_for]->(o:Organization)
WHERE r.name = "{role_name}"
SET r.name = "{new_name}"
RETURN r
""".format(role_name=role_name, new_name=new_name)

core.query_to_dict(manager, q)

@classmethod
def delete_roles_withname(cls, role_name, manager=None):
manager = cls.get_manager(manager)

q = """
MATCH (c:Contact)-[r:Works_for]->(o:Organization)
WHERE r.name = "{role_name}"
DELETE r
""".format(role_name=role_name)

core.query_to_dict(manager, q)

def load_from_nodes(self, contact_id, organization_id):
if isinstance(contact_id, six.string_types):
contact_id = "'{}'".format(contact_id)

if isinstance(organization_id, six.string_types):
organization_id = "'{}'".format(organization_id)

q = """
MATCH (c:Contact)-[r:Works_for]->(o:Organization)
WHERE c.handle_id = {contact_id} AND o.handle_id = {organization_id}
RETURN ID(r) as relation_id
""".format(contact_id=contact_id, organization_id=organization_id)

ret = core.query_to_dict(self.manager, q)

bundle = core.get_relationship_bundle(self.manager, ret['relation_id'])
self.load(bundle)

@classmethod
def get_relationship_model(cls, manager, relationship_id):
"""
:param manager: Context manager to handle transactions
:type manager: Neo4jDBSessionManager
:param relationship_id: Internal Neo4j relationship id
:type relationship_id: int
:return: Relationship model
:rtype: models.BaseRelationshipModel
"""
manager = cls.get_manager(manager)

bundle = core.get_relationship_bundle(manager, relationship_id)
return cls(manager).load(bundle)

@classmethod
def get_all_role_names(cls, manager=None):
manager = cls.get_manager(manager)

q = """MATCH (n:Contact)-[r:Works_for]->(m:Organization)
WHERE r.name IS NOT NULL
RETURN DISTINCT r.name as role_name"""

result = core.query_to_list(manager, q)
endresult = []
for r in result:
endresult.append(r['role_name'])

return endresult

@classmethod
def get_contacts_with_role_name(cls, role_name, manager=None):
manager = cls.get_manager(manager)

q = """
MATCH (c:Contact)-[r:Works_for]->(o:Organization)
WHERE r.name = "{role_name}"
RETURN c, o
""".format(role_name=role_name)

result = core.query_to_list(manager, q)
contact_list = []

for node in result:
contact = ContactModel(manager)
contact.data = {}
contact.data['handle_id'] = node['c'].properties['handle_id']
contact.reload(node['c'])

organization = OrganizationModel(manager)
organization.data = {}
organization.data['handle_id'] = node['o'].properties['handle_id']
organization.reload(node['o'])

contact_list.append((contact, organization))

return contact_list

class ProcedureModel(LogicalModel):
pass
Loading