Coverage for rfpy/api/endpoints/network.py: 100%
100 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-31 16:00 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-31 16:00 +0000
1"""
2Define a network of relationships between organisations
3"""
4from enum import Enum
5from rfpy.model.exc import BusinessRuleViolation
6from rfpy.model.humans import OrganisationType
7from typing import List
9from sqlalchemy.orm import Session
10from sqlalchemy.orm.exc import NoResultFound
12from rfpy.api import validate, fetch
13from rfpy.auth import perms
14from rfpy.suxint import http
15from rfpy.model import User, Edge, RelationshipType
17from rfpy.web import serial
20@http
21def get_network(session: Session, user: User) -> List[serial.NetworkRelationship]:
22 '''
23 Get an array of all the Network Relationships described by your organisation
24 '''
25 q = fetch.edges_for_org_query(session, user.org_id)
26 return [serial.NetworkRelationship.from_orm(r).dict() for r in q]
29@http
30def delete_network(session: Session, user: User):
31 '''
32 Delete all relationships defined for this network.
33 Relationship Types are not deleted.
34 '''
35 validate.check(user, perms.MANAGE_ORGANISATION, target_org=user.organisation)
36 rids = {rt.id for rt in user.organisation.relationship_types}
37 session.query(Edge).filter(Edge.relationship_id.in_(rids)).delete(synchronize_session=False)
40@http
41def get_reltypes(session: Session, user: User) -> List[serial.RelationshipType]:
42 '''
43 Get an array of Relationship Types for your organisation
44 '''
45 return [
46 dict(id=rt.id, name=rt.name, description=rt.description)
47 for rt in user.organisation.relationship_types
48 ]
51@http
52def post_reltype(session: Session, user: User, reltype_doc: serial.RelationshipType) -> serial.Id:
53 '''
54 Create a new Relationship Type for your organisation
56 A RelationshipType must be defined for your organisation before a Relationship can
57 be defined between two 3rd party organisations.
58 Multiple RelationshipTypes can be defined for your organisation in order to model different
59 types of inter-organisation relationships - e.g. 'Partner', 'Downstream Supplier',
60 'Regulator', etc.
62 @permission MANAGE_ORGANISATION
63 '''
64 validate.check(user, perms.MANAGE_ORGANISATION, target_org=user.organisation)
65 rt = RelationshipType(**reltype_doc)
66 user.organisation.relationship_types.append(rt)
67 session.flush()
68 return serial.Id(id=rt.id)
71@http
72def put_reltype(session: Session, user: User, reltype_id, reltype_doc) -> serial.Id:
73 '''
74 Update the name or description for the Relationship Type with the given ID
75 '''
76 validate.check(user, perms.MANAGE_ORGANISATION, target_org=user.organisation)
77 rt = user.organisation.relationship_types.filter(RelationshipType.id == reltype_id).one()
78 rt.name = reltype_doc['name']
79 if 'description' in reltype_doc:
80 rt.description = reltype_doc['description']
83@http
84def delete_reltype(session: Session, user: User, reltype_id: int) -> serial.Id:
85 '''
86 Delete the Relationship Type with the given ID together with all
87 relationships of that type. Underlying Organisations are not deleted.
88 '''
89 validate.check(user, perms.MANAGE_ORGANISATION, target_org=user.organisation)
90 rt = user.organisation.relationship_types.filter(RelationshipType.id == reltype_id).one()
91 session.delete(rt)
94@http
95def post_relationship(session: Session, user: User, relationship_doc):
96 '''
97 Create a new Relationship between two organisations
98 '''
99 validate.check(user, perms.MANAGE_ORGANISATION, target_org=user.organisation)
100 reltype_id = relationship_doc['reltype_id']
101 rt = user.organisation.relationship_types.filter(RelationshipType.id == reltype_id).one()
102 from_org = fetch.organisation(session, relationship_doc['from_org_id'])
103 to_org = fetch.organisation(session, relationship_doc['to_org_id'])
104 edge = Edge(to_org=to_org, from_org=from_org, relationship_type=rt)
105 session.add(edge)
108@http
109def delete_relationship(session: Session, user: User, relationship_doc):
110 '''
111 Delete a new Relationship between two organisations
112 '''
113 validate.check(user, perms.MANAGE_ORGANISATION, target_org=user.organisation)
114 reltype_id = relationship_doc['reltype_id']
115 rt = user.organisation.relationship_types.filter(RelationshipType.id == reltype_id).one()
116 from_org = fetch.organisation(session, relationship_doc['from_org_id'])
117 to_org = fetch.organisation(session, relationship_doc['to_org_id'])
118 edge = session.query(Edge)\
119 .filter_by(to_org_id=to_org.id, from_org_id=from_org.id, relationship_id=rt.id)\
120 .one()
121 session.delete(edge)
124class StandardRelTypes(str, Enum):
125 CONSULTING_ENGAGEMENT = 'Consults For'
126 CONSULTING_PARTNERSHIP = 'Partners With'
127 SUPPLIES = 'Supplies'
128 VENDOR_EVALUATION = 'Evaluates'
131@http
132def post_network_project(session: Session,
133 user: User,
134 project_id: int) -> List[serial.NetworkRelationship]:
135 '''
136 Generate a network of relationships between vendors and participants for the project ID
137 provided. Creates default Relationship Types for standard RFP project relationships.
138 '''
140 validate.check(user, perms.MANAGE_ORGANISATION, target_org=user.organisation)
141 if user.organisation.type != OrganisationType.CONSULTANT:
142 raise BusinessRuleViolation('Action only permitted for Consultant Organisations')
143 project = fetch.project(session, project_id=project_id)
144 validate.check(user, action=perms.PROJECT_ACCESS, project=project)
145 org = user.organisation
147 rt_lookup = dict()
148 for SRT in StandardRelTypes:
150 try:
151 rel = org.relationship_types.filter_by(name=SRT.value).one()
152 except NoResultFound:
153 rel = RelationshipType(org_id=org.id, name=SRT.value)
154 session.add(rel)
155 rt_lookup[SRT] = rel
157 # Populate Edge objects in the Session to facilitate merge() operations later
158 fetch.edges_for_org_query(session, user.org_id).all()
160 for participant in project.participants:
161 if participant.organisation is org:
162 continue
163 if participant.organisation.is_consultant:
164 rel = rt_lookup[StandardRelTypes.CONSULTING_PARTNERSHIP]
165 else:
166 rel = rt_lookup[StandardRelTypes.CONSULTING_ENGAGEMENT]
167 edge = Edge(from_org_id=org.id, to_org_id=participant.org_id, relationship_id=rel.id)
168 session.merge(edge)
170 respondent_id_set = {i.respondent_id for i in project.issues}
172 for respondent_id in respondent_id_set:
173 if respondent_id is None or respondent_id == org.id:
174 continue
176 buyer_id = project.org_id # Owner org assumed to be the buyer
178 rel = rt_lookup[StandardRelTypes.SUPPLIES]
179 session.merge(Edge(from_org_id=respondent_id, to_org_id=buyer_id, relationship_id=rel.id))
181 evaluates_rel = rt_lookup[StandardRelTypes.VENDOR_EVALUATION]
182 session.merge(Edge(from_org_id=org.id, to_org_id=respondent_id,
183 relationship_id=evaluates_rel.id))
185 return get_network(session, user)