Skip to content

Commit e337f96

Browse files
Merge pull request #6479 from bshankar/partner-to-project
Feature: Linking partners to projects
2 parents 0f63180 + 7918f64 commit e337f96

18 files changed

+1596
-18
lines changed

backend/__init__.py

+23
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ def add_api_endpoints(app):
243243
)
244244

245245
from backend.api.projects.favorites import ProjectsFavoritesAPI
246+
from backend.api.projects.partnerships import (
247+
ProjectPartnershipsRestApi,
248+
PartnersByProjectAPI,
249+
)
246250

247251
# Tasks API import
248252
from backend.api.tasks.resources import (
@@ -470,6 +474,25 @@ def add_api_endpoints(app):
470474
ProjectsStatisticsQueriesPopularAPI, format_url("projects/queries/popular/")
471475
)
472476

477+
api.add_resource(
478+
ProjectPartnershipsRestApi,
479+
format_url("projects/partnerships/<int:partnership_id>/"),
480+
methods=["GET", "PATCH", "DELETE"],
481+
)
482+
483+
api.add_resource(
484+
ProjectPartnershipsRestApi,
485+
format_url("projects/partnerships/"),
486+
endpoint="create_partnership",
487+
methods=["POST"],
488+
)
489+
490+
api.add_resource(
491+
PartnersByProjectAPI,
492+
format_url("/projects/<int:project_id>/partners"),
493+
methods=["GET"],
494+
)
495+
473496
api.add_resource(
474497
ProjectsTeamsAPI,
475498
format_url("projects/<int:project_id>/teams/"),

backend/api/projects/partnerships.py

+293
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
from flask_restful import Resource, request
2+
from backend.services.project_partnership_service import ProjectPartnershipService
3+
from backend.services.users.authentication_service import token_auth
4+
from backend.services.project_admin_service import ProjectAdminService
5+
from backend.models.dtos.project_partner_dto import (
6+
ProjectPartnershipDTO,
7+
ProjectPartnershipUpdateDTO,
8+
)
9+
from backend.models.postgis.utils import timestamp
10+
11+
12+
def check_if_manager(partnership_dto: ProjectPartnershipDTO):
13+
if not ProjectAdminService.is_user_action_permitted_on_project(
14+
token_auth.current_user(), partnership_dto.project_id
15+
):
16+
return {
17+
"Error": "User is not a manager of the project",
18+
"SubCode": "UserPermissionError",
19+
}, 401
20+
21+
22+
class ProjectPartnershipsRestApi(Resource):
23+
@staticmethod
24+
def get(partnership_id: int):
25+
"""
26+
Retrieves a Partnership by id
27+
---
28+
tags:
29+
- projects
30+
- partners
31+
- partnerships
32+
produces:
33+
- application/json
34+
parameters:
35+
- name: partnership_id
36+
in: path
37+
description: Unique partnership ID
38+
required: true
39+
type: integer
40+
default: 1
41+
responses:
42+
200:
43+
description: Partnership found
44+
404:
45+
description: Partnership not found
46+
500:
47+
description: Internal Server Error
48+
"""
49+
50+
partnership_dto = ProjectPartnershipService.get_partnership_as_dto(
51+
partnership_id
52+
)
53+
return partnership_dto.to_primitive(), 200
54+
55+
@token_auth.login_required
56+
def post(self):
57+
"""Assign a partner to a project
58+
---
59+
tags:
60+
- projects
61+
- partners
62+
- partnerships
63+
produces:
64+
- application/json
65+
parameters:
66+
- in: header
67+
name: Authorization
68+
description: Base64 encoded session token
69+
required: true
70+
type: string
71+
default: Token sessionTokenHere==
72+
- in: body
73+
name: body
74+
required: true
75+
description: JSON object for creating a partnership
76+
schema:
77+
properties:
78+
projectId:
79+
required: true
80+
type: int
81+
description: Unique project ID
82+
default: 1
83+
partnerId:
84+
required: true
85+
type: int
86+
description: Unique partner ID
87+
default: 1
88+
startedOn:
89+
type: date
90+
description: The timestamp when the partner is added to a project. Defaults to current time.
91+
default: "2017-04-11T12:38:49"
92+
endedOn:
93+
type: date
94+
description: The timestamp when the partner ended their work on a project.
95+
default: "2018-04-11T12:38:49"
96+
responses:
97+
201:
98+
description: Partner project association created
99+
400:
100+
description: Ivalid dates or started_on was after ended_on
101+
401:
102+
description: Forbidden, if user is not a manager of this project
103+
403:
104+
description: Forbidden, if user is not authenticated
105+
404:
106+
description: Not found
107+
500:
108+
description: Internal Server Error
109+
"""
110+
partnership_dto = ProjectPartnershipDTO(request.get_json())
111+
112+
is_not_manager_error = check_if_manager(partnership_dto)
113+
if is_not_manager_error is not None:
114+
return is_not_manager_error
115+
116+
if partnership_dto.started_on is None:
117+
partnership_dto.started_on = timestamp()
118+
119+
partnership_dto = ProjectPartnershipDTO(request.get_json())
120+
partnership_id = ProjectPartnershipService.create_partnership(
121+
partnership_dto.project_id,
122+
partnership_dto.partner_id,
123+
partnership_dto.started_on,
124+
partnership_dto.ended_on,
125+
)
126+
return (
127+
{
128+
"Success": "Partner {} assigned to project {}".format(
129+
partnership_dto.partner_id, partnership_dto.project_id
130+
),
131+
"partnershipId": partnership_id,
132+
},
133+
201,
134+
)
135+
136+
@staticmethod
137+
@token_auth.login_required
138+
def patch(partnership_id: int):
139+
"""Update the time range for a partner project link
140+
---
141+
tags:
142+
- projects
143+
- partners
144+
- partnerships
145+
produces:
146+
- application/json
147+
parameters:
148+
- in: header
149+
name: Authorization
150+
description: Base64 encoded session token
151+
required: true
152+
type: string
153+
default: Token sessionTokenHere==
154+
- name: partnership_id
155+
in: path
156+
description: Unique partnership ID
157+
required: true
158+
type: integer
159+
default: 1
160+
- in: body
161+
name: body
162+
required: true
163+
description: JSON object for creating a partnership
164+
schema:
165+
properties:
166+
startedOn:
167+
type: date
168+
description: The timestamp when the partner is added to a project. Defaults to current time.
169+
default: "2017-04-11T12:38:49"
170+
endedOn:
171+
type: date
172+
description: The timestamp when the partner ended their work on a project.
173+
default: "2018-04-11T12:38:49"
174+
responses:
175+
201:
176+
description: Partner project association created
177+
400:
178+
description: Ivalid dates or started_on was after ended_on
179+
401:
180+
description: Forbidden, if user is not a manager of this project
181+
403:
182+
description: Forbidden, if user is not authenticated
183+
404:
184+
description: Not found
185+
500:
186+
description: Internal Server Error
187+
"""
188+
partnership_updates = ProjectPartnershipUpdateDTO(request.get_json())
189+
partnership_dto = ProjectPartnershipService.get_partnership_as_dto(
190+
partnership_id
191+
)
192+
193+
is_not_manager_error = check_if_manager(partnership_dto)
194+
if is_not_manager_error is not None:
195+
return is_not_manager_error
196+
197+
partnership = ProjectPartnershipService.update_partnership_time_range(
198+
partnership_id,
199+
partnership_updates.started_on,
200+
partnership_updates.ended_on,
201+
)
202+
203+
return (
204+
{
205+
"Success": "Updated time range. startedOn: {}, endedOn: {}".format(
206+
partnership.started_on, partnership.ended_on
207+
),
208+
"startedOn": f"{partnership.started_on}",
209+
"endedOn": f"{partnership.ended_on}",
210+
},
211+
200,
212+
)
213+
214+
@staticmethod
215+
@token_auth.login_required
216+
def delete(partnership_id: int):
217+
"""Deletes a link between a project and a partner
218+
---
219+
tags:
220+
- projects
221+
- partners
222+
- partnerships
223+
produces:
224+
- application/json
225+
parameters:
226+
- in: header
227+
name: Authorization
228+
description: Base64 encoded session token
229+
required: true
230+
type: string
231+
default: Token sessionTokenHere==
232+
- name: partnership_id
233+
in: path
234+
description: Unique partnership ID
235+
required: true
236+
type: integer
237+
default: 1
238+
responses:
239+
201:
240+
description: Partner project association created
241+
401:
242+
description: Forbidden, if user is not a manager of this project
243+
403:
244+
description: Forbidden, if user is not authenticated
245+
404:
246+
description: Not found
247+
500:
248+
description: Internal Server Error
249+
"""
250+
partnership_dto = ProjectPartnershipService.get_partnership_as_dto(
251+
partnership_id
252+
)
253+
254+
is_not_manager_error = check_if_manager(partnership_dto)
255+
if is_not_manager_error is not None:
256+
return is_not_manager_error
257+
258+
ProjectPartnershipService.delete_partnership(partnership_id)
259+
return (
260+
{
261+
"Success": "Partnership ID {} deleted".format(partnership_id),
262+
},
263+
200,
264+
)
265+
266+
267+
class PartnersByProjectAPI(Resource):
268+
@staticmethod
269+
def get(project_id: int):
270+
"""
271+
Retrieves the list of partners associated with a project
272+
---
273+
tags:
274+
- projects
275+
- partners
276+
- partnerships
277+
produces:
278+
- application/json
279+
parameters:
280+
- name: project_id
281+
in: path
282+
description: Unique project ID
283+
required: true
284+
type: integer
285+
default: 1
286+
responses:
287+
200:
288+
description: List (possibly empty) of partners associated with this project_id
289+
500:
290+
description: Internal Server Error
291+
"""
292+
partnerships = ProjectPartnershipService.get_partnerships_by_project(project_id)
293+
return {"partnerships": partnerships}, 200

0 commit comments

Comments
 (0)