Skip to content

Commit

Permalink
Add form for submitting tfed tickets (#525)
Browse files Browse the repository at this point in the history
  • Loading branch information
tnurse18 committed Jul 5, 2021
1 parent 453b64f commit fdb146f
Show file tree
Hide file tree
Showing 15 changed files with 233 additions and 7 deletions.
3 changes: 2 additions & 1 deletion docs/help/report-bugs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ server error, but it often helps to have a bit more context, so please don't hes

You can submit a bug report by doing any of the following:

#. `Submit a new issue on GitHub <https://github.com/WPI-LNL/lnldb>`_
#. `Submit a ticket online <https://lnl.wpi.edu/support/tickets/new>`_ or through Slack
#. Email tfed-db@wpi.edu
#. `Submit a new issue on GitHub <https://github.com/WPI-LNL/lnldb>`_

In your message, be sure to tell us where you were on the site, what you were doing, and what went wrong. The more
details you can provide, the better.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ Table of Contents
reference/pages
reference/pdfs
reference/projection
reference/rt

.. toctree::
:maxdepth: 2
Expand Down
18 changes: 18 additions & 0 deletions docs/reference/rt.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
RT
==

This module leverages RT's REST API to create new interfaces for LNL's ticketing system.

Views
-----
.. automodule:: rt.views
:members:
:undoc-members:

-----

Forms
-----
.. automodule:: rt.forms
:members:
:undoc-members:
13 changes: 8 additions & 5 deletions lnldb/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ def from_runtime(*x):
'release': GIT_RELEASE,
}

SNIPE_URL = env.str('SNIPE_URL', '')
SNIPE_API_KEY = env.str('SNIPE_API_KEY', '')
SNIPE_GENERAL_USER = env.str('SNIPE_USERNAME', "")
SNIPE_GENERAL_PASS = env.str('SNIPE_PASSWORD', "")

RT_TOKEN = env.str('RT_API_KEY', '')

TESTING = sys.argv[1:2] == ['test']

DEBUG = env.bool("DEBUG", default=True)
Expand Down Expand Up @@ -88,11 +95,6 @@ def from_runtime(*x):
CCC_PASS = env.str("CCC_PASS", "")
SECRET_KEY = env.str("SECRET_KEY", "I am insecure.")

SNIPE_URL = env.str('SNIPE_URL', '')
SNIPE_API_KEY = env.str('SNIPE_API_KEY', '')
SNIPE_GENERAL_USER = env.str('SNIPE_USERNAME', "")
SNIPE_GENERAL_PASS = env.str('SNIPE_PASSWORD', "")

SAML2_AUTH = {
'METADATA_AUTO_CONF_URL': env.str('SAML2_IDP_METADATA_URL', 'https://samltest.id/saml/idp'),
'DEFAULT_NEXT_URL': '/db/',
Expand Down Expand Up @@ -307,6 +309,7 @@ def from_runtime(*x):
'mptt',
'devices',
'api',
'rt',

'bootstrap3',
'crispy_forms',
Expand Down
1 change: 1 addition & 0 deletions lnldb/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
url(r'', include(('members.urls', 'members'), namespace='members')),
url(r'^api/', include(('api.urls', 'api'), namespace='api')),
url(r'^mdm/', include(('devices.urls.mdm', 'mdm'), namespace="mdm")),
url(r'^support/', include(('rt.urls', 'support'), namespace='support')),
url(r'', include(('pages.urls', 'pages'), namespace='pages')),

# special urls
Expand Down
1 change: 1 addition & 0 deletions requirements_base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ pysaml2==6.1.0
-e git+https://github.com/WPI-LNL/django-saml2-auth.git@6348b4efcc7fc99dff5303846c60785026f67116#egg=django_saml2_auth
django-jchart==0.4.2
django-semanticui-forms==1.6.5
filetype==1.0.7
Empty file added rt/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions rt/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class RtConfig(AppConfig):
name = 'rt'
15 changes: 15 additions & 0 deletions rt/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django import forms
from multiupload.fields import MultiFileField


class TicketSubmissionForm(forms.Form):
subject = forms.CharField(max_length=100)
description = forms.CharField(widget=forms.Textarea, label="Please describe your problem or request in detail.")
attachments = MultiFileField(max_file_size=1024 * 1024 * 20, required=False) # 20 MB limit

class Meta:
layout = [
("Field", "subject"),
("Field", "description"),
("Field", "attachments")
]
32 changes: 32 additions & 0 deletions rt/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from data.tests.util import ViewTestCase
from django.shortcuts import reverse
from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile


class RTAPITests(ViewTestCase):
def test_ticket_form(self):
# Check that form loads ok
self.assertOk(self.client.get(reverse("support:new-ticket")))

# Test response with invalid data
invalid_data = {
"subject": "Website broken",
"description": "",
"attachments": [],
"save": "Submit"
}
self.assertOk(self.client.post(reverse("support:new-ticket"), invalid_data))

# Check response with valid data
attachment = SimpleUploadedFile('test.txt', b'Contents of a file')
valid_data = {
"subject": "Website broken",
"description": "I tried using this website to file my taxes but it isn't working for some reason",
"attachments": [attachment],
"save": "Submit"
}

# We do not want to submit new tickets if testing is triggered in prod
if settings.RT_TOKEN in ['', None]:
self.assertRedirects(self.client.post(reverse("support:new-ticket"), valid_data), reverse("home"))
11 changes: 11 additions & 0 deletions rt/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.conf.urls import include, url
from . import views


app_name = "RT"

urlpatterns = [
url(r'^tickets/', include([
url(r'^new/$', views.new_ticket, name="new-ticket"),
])),
]
103 changes: 103 additions & 0 deletions rt/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import requests
import filetype
from django.shortcuts import render, reverse
from django.http import HttpResponseRedirect
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from base64 import b64encode
from . import forms


@login_required
def new_ticket(request):
""" Form for submitting an RT ticket to the Database queue """
context = {}
if request.method == "POST":
form = forms.TicketSubmissionForm(request.POST)
if form.is_valid():
subject = request.POST['subject']
description = request.POST['description'] + "\n\n-----"
description += "\nVersion: %s" % settings.GIT_RELEASE[:7]
description += "\nSubmitted from: %s" % request.META.get('REMOTE_ADDR')
description += "\nDevice Info: %s" % request.META.get('HTTP_USER_AGENT')
attachments = request.FILES.getlist('attachments')
resp = create_ticket("Database", request.user.email, subject, description, attachments=attachments)
if resp.get('id', None):
messages.success(request, "Your ticket has been submitted. Thank you!")
else:
messages.add_message(
request, messages.WARNING,
'Failed to open ticket: %s. Please contact the Webmaster.' % resp.get('message')
)
return HttpResponseRedirect(reverse("home"))
else:
form = forms.TicketSubmissionForm()
context['form'] = form
return render(request, 'helpdesk_form.html', context)


# API Methods
def api_request(method, endpoint, data=None):
"""
Send an API request to the RT server
:param method: `GET`, `POST`, `PUT`, or `DELETE`
:param endpoint: RT endpoint
:param data: JSON data (if applicable)
:return: Response
"""
host = 'https://lnl-rt.wpi.edu/rt/REST/2.0/'
headers = {"Content-Type": "application/json", "Authorization": "token " + settings.RT_TOKEN}
if method.lower() == 'get':
response = requests.get(host + endpoint, headers=headers)
if response.status_code != 500:
return response.json()
return {"message": "An unknown error occurred"}
elif method.lower() == 'post':
if data:
response = requests.post(host + endpoint, json=data, headers=headers)
if response.status_code != 500:
return response.json()
return {"message": "An unknown error occurred"}
return {"message": "Bad request"}


def create_ticket(queue, reporter, subject, content, html=False, cc=None, attachments=None):
"""
Create a new ticket in RT
:param queue: The ticket queue to send the ticket to
:param reporter: Email address of the user submitting the ticket
:param subject: Brief subject line for quick reference
:param content: The contents of the ticket
:param html: If true, will set ContentType to text/html instead of text/plain
:param cc: List of additional email addresses to include in the ticket [Optional]
:param attachments: A list of attachments to include [Optional]
:return: API response
"""
endpoint = 'ticket'
payload = {
"Subject": subject,
"Queue": queue,
"Requestor": reporter,
"Content": content,
"ContentType": "text/plain"
}
if cc:
payload["Cc"] = ','.join(cc)
if html:
payload["ContentType"] = "text/html"
if attachments:
files = []
for attachment in attachments:
ft = filetype.guess(attachment)
if ft:
mime_type = ft.mime
else:
mime_type = "application/octet-stream"
attachment.seek(0)
file_contents = b64encode(attachment.read()).decode('utf-8')
files.append({"FileName": attachment.name, "FileType": mime_type, "FileContent": file_contents})
payload["Attachments"] = files
return api_request('POST', endpoint, payload)
2 changes: 1 addition & 1 deletion site_tmpl/base_semanticui.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<script src="https://code.jquery.com/jquery-3.1.1.min.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
<link rel="stylesheet" href="/static/css/onboarding.css">
<link rel="stylesheet" href="/static/css/semanticui.css">
<link rel="stylesheet" href="/static/css/dropdown/dropdown.min.css">
<script src="/static/js/dropdown/dropdown.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
Expand Down
21 changes: 21 additions & 0 deletions site_tmpl/helpdesk_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% extends 'base_semanticui.html' %}
{% load i18n %}
{% load semanticui %}

{% block content %}
<div class="ui middle aligned centered grid container">
<div class="twelve wide column">
<h3 class="ui {% if inverted %}inverted {% endif %}dividing header">Submit a Ticket</h3>
<form class="ui {% if inverted %}inverted {% endif %}form" action="" method="POST" id="form" enctype="multipart/form-data">
{% csrf_token %}
{{ form.management_form }}
{% render_form form %}
<br>
<button class="ui animated {% if inverted %}inverted {% else %}secondary {% endif %}button" type="submit" value="{% trans "submit" %}">
<div class="visible content">Submit</div>
<div class="hidden content"><i class="check icon"></i></div>
</button>
</form>
</div>
</div>
{% endblock %}
14 changes: 14 additions & 0 deletions static/css/onboarding.css → static/css/semanticui.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ input:read-only {
cursor: not-allowed;
}

.inverted .field input, .inverted .field textarea {
background-color: #333 !important;
color: white !important;
}

.grid, .ui.text.container {
opacity: 1;
transition: all 1s ease;
Expand Down Expand Up @@ -47,6 +52,15 @@ input:read-only {
transition: all 1s ease;
}

#id_attachments {
cursor: pointer;
border-radius: unset;
border-top: none;
border-left: none;
border-right: none;
padding-bottom: 2%;
}

@keyframes fadein {
from {opacity: 0;}
to {opacity: 1;}
Expand Down

0 comments on commit fdb146f

Please sign in to comment.