Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added report issue vikunja integration #273

Merged
merged 6 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion memberportal/api_general/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ def get(self, request):
"memberbucks_topup_options": json.loads(
config.STRIPE_MEMBERBUCKS_TOPUP_OPTIONS
),
"trelloIntegration": config.ENABLE_TRELLO_INTEGRATION,
"enableProxyVoting": config.ENABLE_PROXY_VOTING,
"enableStripe": config.ENABLE_STRIPE
and len(config.STRIPE_PUBLISHABLE_KEY) > 0
Expand Down
245 changes: 196 additions & 49 deletions memberportal/api_member_tools/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
from api_meeting.models import Meeting
from constance import config
from services.emails import send_email_to_admin
from services import discord

from random import shuffle
import requests
from django.utils import timezone

from rest_framework import status, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
import logging

logger = logging.getLogger("api_member_tools")


class SwipesList(APIView):
Expand Down Expand Up @@ -100,7 +104,7 @@ def get(self, request):

class IssueDetail(APIView):
"""
post: Creates a new issue by creating a trello card or emailing the management committee
post: Creates a new issue by creating a task card or emailing the management committee
"""

permission_classes = (permissions.IsAuthenticated,)
Expand All @@ -109,64 +113,207 @@ def post(self, request):
body = request.data
title = body["title"]
description = request.user.profile.get_full_name() + ": " + body["description"]
vikunja_task_url = None
trello_card_url = None

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

use_trello = config.ENABLE_TRELLO_INTEGRATION
trello_key = config.TRELLO_API_KEY
trello_token = config.TRELLO_API_TOKEN
trello_id_list = config.TRELLO_ID_LIST

if use_trello:
url = "https://api.trello.com/1/cards"

querystring = {
"name": title,
"desc": description,
"pos": "top",
"idList": trello_id_list,
"keepFromSource": "all",
"key": trello_key,
"token": trello_token,
}
failed = False

response = requests.request("POST", url, params=querystring)
request.user.log_event(
"Submitted issue: " + title + " Content: " + description,
"generic",
)

if response.status_code == 200:
request.user.log_event(
"Submitted issue: " + title + " Content: " + description,
"generic",
)
if config.REPORT_ISSUE_ENABLE_VIKUNJA and bool(
config.VIKUNJA_DEFAULT_PROJECT_ID
):
vikunja_project_id = int(config.VIKUNJA_DEFAULT_PROJECT_ID)
vikunja_label_id = int(config.VIKUNJA_DEFAULT_LABEL_ID)

try:
task_body = {

Choose a reason for hiding this comment

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

If you only want to create a task, you don't need to provide all fields. Here it is enough to provide title and description

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for that @kolaente! I had a couple issues implementing it from the docs as it wasn't super clear what was necessary (not a complaint, your docs are way better than ours!) so I ended up inspecting the browser request and copied it's payload.

Out of interest how did you find this random PR? 😂

Copy link
Member Author

Choose a reason for hiding this comment

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

Also while you're here, is there a way of adding a tag during the initial task creation request or does it have to be added after the task is created?

Choose a reason for hiding this comment

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

Out of interest how did you find this random PR? 😂

Someone in the Vikunja forum mentioned the MemberMatters integration and so I wanted to take a look at what that is. Because of the lack of docs about it (which makes sense since the feature seems pretty new), I ended up here 😅

Also while you're here, is there a way of adding a tag during the initial task creation request or does it have to be added after the task is created?

Currently there's no way to do that. PRs welcome!

Copy link
Contributor

Choose a reason for hiding this comment

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

Someone in the Vikunja forum mentioned the MemberMatters integration and so I wanted to take a look at what that is.

That would be me... 😅

I'm hoping to find time in the near future to update the docs for MM around all this, we'll see if life gets in the way or not! 👀

"max_right": None,
"id": 0,
"title": title,
"description": description,
"done": False,
"done_at": None,
"priority": 0,
"labels": [],
"assignees": [],
"due_date": None,
"start_date": None,
"end_date": None,
"repeat_after": 0,
"repeat_from_current_date": False,
"repeat_mode": 0,
"reminders": [],
"parent_task_id": 0,
"hex_color": "",
"percent_done": 0,
"related_tasks": {},
"attachments": [],
"cover_image_attachment_id": None,
"identifier": "",
"index": 0,
"is_favorite": False,
"subscription": None,
"position": 64,
"reactions": {},
"created_by": {
"max_right": None,
"id": 0,
"email": "",
"username": "",
"name": "",
"exp": 0,
"type": 0,
"created": None,
"updated": None,
"settings": {
"max_right": None,
"name": "",
"email_reminders_enabled": False,
"discoverable_by_name": False,
"discoverable_by_email": True,
"overdue_tasks_reminders_enabled": False,
"week_start": 0,
"timezone": "",
"language": "en",
"frontend_settings": {
"play_sound_when_done": False,
"quick_add_magic_mode": "vikunja",
"color_schema": "auto",
"default_view": "first",
},
},
},
"created": "1970-01-01T00:00:00.000Z",
"updated": "1970-01-01T00:00:00.000Z",
"project_id": vikunja_project_id,
"bucket_id": 0,
"reminder_dates": None,
}

return Response(
{"success": True, "url": response.json()["shortUrl"]},
status=status.HTTP_201_CREATED,
task_response = requests.request(
"PUT",
f"{config.VIKUNJA_API_URL}/api/v1/projects/{vikunja_project_id}/tasks",
json=task_body,
headers={"Authorization": "Bearer " + config.VIKUNJA_API_TOKEN},
)

else:
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
if (vikunja_label_id is not None) and (
task_response.status_code == 201
):
task_id = "unknown"
try:
task_id = task_response.json()["id"]
vikunja_task_url = f"{config.VIKUNJA_API_URL}/tasks/{task_id}"
label_body = {
"label_id": int(vikunja_label_id),
"created": "1970-01-01T00:00:00.000Z",
}

label_response = requests.request(
"PUT",
f"{config.VIKUNJA_API_URL}/api/v1/tasks/{task_id}/labels",
json=label_body,
headers={
"Authorization": "Bearer " + config.VIKUNJA_API_TOKEN
},
)

if label_response.status_code != 201:
logger.warning(
f"Failed to add label to Vikunja task {task_id}: %s",
label_response.json(),
)

except Exception:
logger.exception(
f"Failed to add label to Vikunja task {task_id}."
)
pass

if task_response.status_code != 201:
logger.error(
"Failed to create Vikunja task: %s", task_response.json()
)
failed = True

except Exception:
# uh oh, but don't stop processing other ones
failed = True
logger.exception("Failed to create reported issue Vikunja task.")

if config.REPORT_ISSUE_ENABLE_TRELLO:
try:
trello_key = config.TRELLO_API_KEY
trello_token = config.TRELLO_API_TOKEN
trello_id_list = config.TRELLO_ID_LIST
trello_url = "https://api.trello.com/1/cards"

querystring = {
"name": title,
"desc": description,
"pos": "top",
"idList": trello_id_list,
"keepFromSource": "all",
"key": trello_key,
"token": trello_token,
}

# if Trello isn't configured, use email instead
else:
subject = f"{request.user.profile.get_full_name()} submitted an issue about {title}"

if send_email_to_admin(
subject=subject,
template_vars={
"title": subject,
"message": description,
},
user=request.user,
reply_to=request.user.email,
):
return Response(
{"success": True},
status=status.HTTP_201_CREATED,
)
response = requests.request("POST", trello_url, params=querystring)

if response.status_code != 200:
failed = True

trello_card_url = response.json()["shortUrl"]

except Exception:
# uh oh, but don't stop processing other ones
failed = True
logger.exception("Failed to create reported issue Trello card.")

# email report
if config.REPORT_ISSUE_ENABLE_EMAIL:
try:
subject = f"{request.user.profile.get_full_name()}: {title}"

if not send_email_to_admin(
subject=subject,
template_vars={
"title": subject,
"message": description,
},
user=request.user,
reply_to=request.user.email,
):
failed = True

except Exception:
# uh oh, but don't stop processing other ones
failed = True
logger.exception("Failed to send reported issue email.")

# discord report
if config.REPORT_ISSUE_ENABLE_DISCORD:
username = request.user.profile.get_full_name()
description = body["description"]

discord.post_reported_issue_to_discord(
username, title, description, vikunja_task_url, trello_card_url
)

else:
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
if failed:
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
else:
return Response(
{"success": True},
status=status.HTTP_201_CREATED,
)


class MeetingList(APIView):
Expand Down
56 changes: 52 additions & 4 deletions memberportal/membermatters/constance_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,40 @@
False,
"Enable integration with stripe for membership payments.",
),
# ==== Report Issue Services ====
# Email config
"REPORT_ISSUE_ENABLE_EMAIL": (
True,
"Enable the submit issue to email integration.",
),
# Discord config
"REPORT_ISSUE_ENABLE_DISCORD": (
False,
"Enable the submit issue to Discord integration.",
),
# Vikunja config
"REPORT_ISSUE_ENABLE_VIKUNJA": (
False,
"Enable the submit issue to Vikunja integration.",
),
"VIKUNJA_API_URL": ("", "Set this to your Vikunja instance public URL."),
"VIKUNJA_API_TOKEN": ("", "Set this to your Vikunja API token."),
"VIKUNJA_DEFAULT_PROJECT_ID": (
"",
"Set this to the ID of your default project to create issues in.",
),
"VIKUNJA_DEFAULT_LABEL_ID": (
"",
"[optional] Set this to the ID of your default label if you want new issues to be tagged.",
),
"VIKUNJA_TEAMS": (
'[{"name": "Members", "oidcID": "members", "description": "The default team for all members.", "isPublic": false}]',
"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.",
),
# Trello config
"ENABLE_TRELLO_INTEGRATION": (
"REPORT_ISSUE_ENABLE_TRELLO": (
False,
"Enable the submit issue to trello integration. If disabled we'll send an email to EMAIL_ADMIN instead.",
"Enable the submit issue to trello integration.",
),
"TRELLO_API_KEY": ("", "Set this to your Trello API key."),
"TRELLO_API_TOKEN": ("", "Set this to your Trello API token."),
Expand Down Expand Up @@ -212,6 +237,10 @@
"https://discordapp.com/api/webhooks/<token>",
"Discord URL to send webhook notifications to for vending/memberbucks purchases.",
),
"DISCORD_REPORT_ISSUE_WEBHOOK": (
"https://discordapp.com/api/webhooks/<token>",
"Discord URL to send webhook notifications to when reporting issues.",
),
"ENABLE_DISCOURSE_SSO_PROTOCOL": (
False,
"Enable support for the discourse SSO protocol.",
Expand Down Expand Up @@ -416,6 +445,7 @@
"SMS_ENABLE",
"TWILIO_ACCOUNT_SID",
"TWILIO_AUTH_TOKEN",
"TWILIO_AUTH_TOKEN",
"SMS_DEFAULT_COUNTRY_CODE",
"SMS_SENDER_ID",
"SMS_MESSAGES",
Expand All @@ -432,11 +462,28 @@
"MEMBERBUCKS_CURRENCY",
),
),
("Vikunja Integration", ("VIKUNJA_TEAMS",)),
(
"Report Issue Services",
(
"REPORT_ISSUE_ENABLE_EMAIL",
"REPORT_ISSUE_ENABLE_DISCORD",
"REPORT_ISSUE_ENABLE_VIKUNJA",
"REPORT_ISSUE_ENABLE_TRELLO",
),
),
(
"Vikunja Integration",
(
"VIKUNJA_TEAMS",
"VIKUNJA_API_URL",
"VIKUNJA_API_TOKEN",
"VIKUNJA_DEFAULT_PROJECT_ID",
"VIKUNJA_DEFAULT_LABEL_ID",
),
),
(
"Trello Integration",
(
"ENABLE_TRELLO_INTEGRATION",
"TRELLO_API_KEY",
"TRELLO_API_TOKEN",
"TRELLO_ID_LIST",
Expand Down Expand Up @@ -508,6 +555,7 @@
"DISCORD_DOOR_WEBHOOK",
"DISCORD_INTERLOCK_WEBHOOK",
"DISCORD_MEMBERBUCKS_PURCHASE_WEBHOOK",
"DISCORD_REPORT_ISSUE_WEBHOOK",
),
),
]
Expand Down
Loading
Loading