Skip to content

Commit 6ba7d5a

Browse files
authored
Merge pull request #273 from membermatters/feature/vikunja-integration
added report issue vikunja integration
2 parents 2a6a647 + e41cc6a commit 6ba7d5a

File tree

5 files changed

+289
-68
lines changed

5 files changed

+289
-68
lines changed

memberportal/api_general/views.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ def get(self, request):
3737
"memberbucks_topup_options": json.loads(
3838
config.STRIPE_MEMBERBUCKS_TOPUP_OPTIONS
3939
),
40-
"trelloIntegration": config.ENABLE_TRELLO_INTEGRATION,
4140
"enableProxyVoting": config.ENABLE_PROXY_VOTING,
4241
"enableStripe": config.ENABLE_STRIPE
4342
and len(config.STRIPE_PUBLISHABLE_KEY) > 0

memberportal/api_member_tools/views.py

Lines changed: 196 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
from api_meeting.models import Meeting
44
from constance import config
55
from services.emails import send_email_to_admin
6+
from services import discord
7+
68
from random import shuffle
79
import requests
810
from django.utils import timezone
9-
1011
from rest_framework import status, permissions
1112
from rest_framework.response import Response
1213
from rest_framework.views import APIView
14+
import logging
15+
16+
logger = logging.getLogger("api_member_tools")
1317

1418

1519
class SwipesList(APIView):
@@ -100,7 +104,7 @@ def get(self, request):
100104

101105
class IssueDetail(APIView):
102106
"""
103-
post: Creates a new issue by creating a trello card or emailing the management committee
107+
post: Creates a new issue by creating a task card or emailing the management committee
104108
"""
105109

106110
permission_classes = (permissions.IsAuthenticated,)
@@ -109,64 +113,207 @@ def post(self, request):
109113
body = request.data
110114
title = body["title"]
111115
description = request.user.profile.get_full_name() + ": " + body["description"]
116+
vikunja_task_url = None
117+
trello_card_url = None
112118

113119
if not (title and description):
114120
return Response(status=status.HTTP_400_BAD_REQUEST)
115121

116-
use_trello = config.ENABLE_TRELLO_INTEGRATION
117-
trello_key = config.TRELLO_API_KEY
118-
trello_token = config.TRELLO_API_TOKEN
119-
trello_id_list = config.TRELLO_ID_LIST
120-
121-
if use_trello:
122-
url = "https://api.trello.com/1/cards"
123-
124-
querystring = {
125-
"name": title,
126-
"desc": description,
127-
"pos": "top",
128-
"idList": trello_id_list,
129-
"keepFromSource": "all",
130-
"key": trello_key,
131-
"token": trello_token,
132-
}
122+
failed = False
133123

134-
response = requests.request("POST", url, params=querystring)
124+
request.user.log_event(
125+
"Submitted issue: " + title + " Content: " + description,
126+
"generic",
127+
)
135128

136-
if response.status_code == 200:
137-
request.user.log_event(
138-
"Submitted issue: " + title + " Content: " + description,
139-
"generic",
140-
)
129+
if config.REPORT_ISSUE_ENABLE_VIKUNJA and bool(
130+
config.VIKUNJA_DEFAULT_PROJECT_ID
131+
):
132+
vikunja_project_id = int(config.VIKUNJA_DEFAULT_PROJECT_ID)
133+
vikunja_label_id = int(config.VIKUNJA_DEFAULT_LABEL_ID)
134+
135+
try:
136+
task_body = {
137+
"max_right": None,
138+
"id": 0,
139+
"title": title,
140+
"description": description,
141+
"done": False,
142+
"done_at": None,
143+
"priority": 0,
144+
"labels": [],
145+
"assignees": [],
146+
"due_date": None,
147+
"start_date": None,
148+
"end_date": None,
149+
"repeat_after": 0,
150+
"repeat_from_current_date": False,
151+
"repeat_mode": 0,
152+
"reminders": [],
153+
"parent_task_id": 0,
154+
"hex_color": "",
155+
"percent_done": 0,
156+
"related_tasks": {},
157+
"attachments": [],
158+
"cover_image_attachment_id": None,
159+
"identifier": "",
160+
"index": 0,
161+
"is_favorite": False,
162+
"subscription": None,
163+
"position": 64,
164+
"reactions": {},
165+
"created_by": {
166+
"max_right": None,
167+
"id": 0,
168+
"email": "",
169+
"username": "",
170+
"name": "",
171+
"exp": 0,
172+
"type": 0,
173+
"created": None,
174+
"updated": None,
175+
"settings": {
176+
"max_right": None,
177+
"name": "",
178+
"email_reminders_enabled": False,
179+
"discoverable_by_name": False,
180+
"discoverable_by_email": True,
181+
"overdue_tasks_reminders_enabled": False,
182+
"week_start": 0,
183+
"timezone": "",
184+
"language": "en",
185+
"frontend_settings": {
186+
"play_sound_when_done": False,
187+
"quick_add_magic_mode": "vikunja",
188+
"color_schema": "auto",
189+
"default_view": "first",
190+
},
191+
},
192+
},
193+
"created": "1970-01-01T00:00:00.000Z",
194+
"updated": "1970-01-01T00:00:00.000Z",
195+
"project_id": vikunja_project_id,
196+
"bucket_id": 0,
197+
"reminder_dates": None,
198+
}
141199

142-
return Response(
143-
{"success": True, "url": response.json()["shortUrl"]},
144-
status=status.HTTP_201_CREATED,
200+
task_response = requests.request(
201+
"PUT",
202+
f"{config.VIKUNJA_API_URL}/api/v1/projects/{vikunja_project_id}/tasks",
203+
json=task_body,
204+
headers={"Authorization": "Bearer " + config.VIKUNJA_API_TOKEN},
145205
)
146206

147-
else:
148-
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
207+
if (vikunja_label_id is not None) and (
208+
task_response.status_code == 201
209+
):
210+
task_id = "unknown"
211+
try:
212+
task_id = task_response.json()["id"]
213+
vikunja_task_url = f"{config.VIKUNJA_API_URL}/tasks/{task_id}"
214+
label_body = {
215+
"label_id": int(vikunja_label_id),
216+
"created": "1970-01-01T00:00:00.000Z",
217+
}
218+
219+
label_response = requests.request(
220+
"PUT",
221+
f"{config.VIKUNJA_API_URL}/api/v1/tasks/{task_id}/labels",
222+
json=label_body,
223+
headers={
224+
"Authorization": "Bearer " + config.VIKUNJA_API_TOKEN
225+
},
226+
)
227+
228+
if label_response.status_code != 201:
229+
logger.warning(
230+
f"Failed to add label to Vikunja task {task_id}: %s",
231+
label_response.json(),
232+
)
233+
234+
except Exception:
235+
logger.exception(
236+
f"Failed to add label to Vikunja task {task_id}."
237+
)
238+
pass
239+
240+
if task_response.status_code != 201:
241+
logger.error(
242+
"Failed to create Vikunja task: %s", task_response.json()
243+
)
244+
failed = True
245+
246+
except Exception:
247+
# uh oh, but don't stop processing other ones
248+
failed = True
249+
logger.exception("Failed to create reported issue Vikunja task.")
250+
251+
if config.REPORT_ISSUE_ENABLE_TRELLO:
252+
try:
253+
trello_key = config.TRELLO_API_KEY
254+
trello_token = config.TRELLO_API_TOKEN
255+
trello_id_list = config.TRELLO_ID_LIST
256+
trello_url = "https://api.trello.com/1/cards"
257+
258+
querystring = {
259+
"name": title,
260+
"desc": description,
261+
"pos": "top",
262+
"idList": trello_id_list,
263+
"keepFromSource": "all",
264+
"key": trello_key,
265+
"token": trello_token,
266+
}
149267

150-
# if Trello isn't configured, use email instead
151-
else:
152-
subject = f"{request.user.profile.get_full_name()} submitted an issue about {title}"
153-
154-
if send_email_to_admin(
155-
subject=subject,
156-
template_vars={
157-
"title": subject,
158-
"message": description,
159-
},
160-
user=request.user,
161-
reply_to=request.user.email,
162-
):
163-
return Response(
164-
{"success": True},
165-
status=status.HTTP_201_CREATED,
166-
)
268+
response = requests.request("POST", trello_url, params=querystring)
269+
270+
if response.status_code != 200:
271+
failed = True
272+
273+
trello_card_url = response.json()["shortUrl"]
274+
275+
except Exception:
276+
# uh oh, but don't stop processing other ones
277+
failed = True
278+
logger.exception("Failed to create reported issue Trello card.")
279+
280+
# email report
281+
if config.REPORT_ISSUE_ENABLE_EMAIL:
282+
try:
283+
subject = f"{request.user.profile.get_full_name()}: {title}"
284+
285+
if not send_email_to_admin(
286+
subject=subject,
287+
template_vars={
288+
"title": subject,
289+
"message": description,
290+
},
291+
user=request.user,
292+
reply_to=request.user.email,
293+
):
294+
failed = True
295+
296+
except Exception:
297+
# uh oh, but don't stop processing other ones
298+
failed = True
299+
logger.exception("Failed to send reported issue email.")
300+
301+
# discord report
302+
if config.REPORT_ISSUE_ENABLE_DISCORD:
303+
username = request.user.profile.get_full_name()
304+
description = body["description"]
305+
306+
discord.post_reported_issue_to_discord(
307+
username, title, description, vikunja_task_url, trello_card_url
308+
)
167309

168-
else:
169-
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
310+
if failed:
311+
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
312+
else:
313+
return Response(
314+
{"success": True},
315+
status=status.HTTP_201_CREATED,
316+
)
170317

171318

172319
class MeetingList(APIView):

memberportal/membermatters/constance_config.py

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,40 @@
120120
False,
121121
"Enable integration with stripe for membership payments.",
122122
),
123+
# ==== Report Issue Services ====
124+
# Email config
125+
"REPORT_ISSUE_ENABLE_EMAIL": (
126+
True,
127+
"Enable the submit issue to email integration.",
128+
),
129+
# Discord config
130+
"REPORT_ISSUE_ENABLE_DISCORD": (
131+
False,
132+
"Enable the submit issue to Discord integration.",
133+
),
123134
# Vikunja config
135+
"REPORT_ISSUE_ENABLE_VIKUNJA": (
136+
False,
137+
"Enable the submit issue to Vikunja integration.",
138+
),
139+
"VIKUNJA_API_URL": ("", "Set this to your Vikunja instance public URL."),
140+
"VIKUNJA_API_TOKEN": ("", "Set this to your Vikunja API token."),
141+
"VIKUNJA_DEFAULT_PROJECT_ID": (
142+
"",
143+
"Set this to the ID of your default project to create issues in.",
144+
),
145+
"VIKUNJA_DEFAULT_LABEL_ID": (
146+
"",
147+
"[optional] Set this to the ID of your default label if you want new issues to be tagged.",
148+
),
124149
"VIKUNJA_TEAMS": (
125150
'[{"name": "Members", "oidcID": "members", "description": "The default team for all members.", "isPublic": false}]',
126151
"A JSON array of Vikunja teams to add users to when they login via SSO. Returned as an OIDC claim with the 'vikunja_teams' scope. Check Vikunja docs for syntax.",
127152
),
128153
# Trello config
129-
"ENABLE_TRELLO_INTEGRATION": (
154+
"REPORT_ISSUE_ENABLE_TRELLO": (
130155
False,
131-
"Enable the submit issue to trello integration. If disabled we'll send an email to EMAIL_ADMIN instead.",
156+
"Enable the submit issue to trello integration.",
132157
),
133158
"TRELLO_API_KEY": ("", "Set this to your Trello API key."),
134159
"TRELLO_API_TOKEN": ("", "Set this to your Trello API token."),
@@ -212,6 +237,10 @@
212237
"https://discordapp.com/api/webhooks/<token>",
213238
"Discord URL to send webhook notifications to for vending/memberbucks purchases.",
214239
),
240+
"DISCORD_REPORT_ISSUE_WEBHOOK": (
241+
"https://discordapp.com/api/webhooks/<token>",
242+
"Discord URL to send webhook notifications to when reporting issues.",
243+
),
215244
"ENABLE_DISCOURSE_SSO_PROTOCOL": (
216245
False,
217246
"Enable support for the discourse SSO protocol.",
@@ -416,6 +445,7 @@
416445
"SMS_ENABLE",
417446
"TWILIO_ACCOUNT_SID",
418447
"TWILIO_AUTH_TOKEN",
448+
"TWILIO_AUTH_TOKEN",
419449
"SMS_DEFAULT_COUNTRY_CODE",
420450
"SMS_SENDER_ID",
421451
"SMS_MESSAGES",
@@ -432,11 +462,28 @@
432462
"MEMBERBUCKS_CURRENCY",
433463
),
434464
),
435-
("Vikunja Integration", ("VIKUNJA_TEAMS",)),
465+
(
466+
"Report Issue Services",
467+
(
468+
"REPORT_ISSUE_ENABLE_EMAIL",
469+
"REPORT_ISSUE_ENABLE_DISCORD",
470+
"REPORT_ISSUE_ENABLE_VIKUNJA",
471+
"REPORT_ISSUE_ENABLE_TRELLO",
472+
),
473+
),
474+
(
475+
"Vikunja Integration",
476+
(
477+
"VIKUNJA_TEAMS",
478+
"VIKUNJA_API_URL",
479+
"VIKUNJA_API_TOKEN",
480+
"VIKUNJA_DEFAULT_PROJECT_ID",
481+
"VIKUNJA_DEFAULT_LABEL_ID",
482+
),
483+
),
436484
(
437485
"Trello Integration",
438486
(
439-
"ENABLE_TRELLO_INTEGRATION",
440487
"TRELLO_API_KEY",
441488
"TRELLO_API_TOKEN",
442489
"TRELLO_ID_LIST",
@@ -508,6 +555,7 @@
508555
"DISCORD_DOOR_WEBHOOK",
509556
"DISCORD_INTERLOCK_WEBHOOK",
510557
"DISCORD_MEMBERBUCKS_PURCHASE_WEBHOOK",
558+
"DISCORD_REPORT_ISSUE_WEBHOOK",
511559
),
512560
),
513561
]

0 commit comments

Comments
 (0)