Sync dashboard user data with jira 03/22703/2
authormaxbr <maxbr@mi.fu-berlin.de>
Wed, 5 Oct 2016 12:24:11 +0000 (14:24 +0200)
committerJose Lausuch <jose.lausuch@ericsson.com>
Wed, 12 Oct 2016 13:29:04 +0000 (13:29 +0000)
JIRA: PHAROS-264

Change-Id: Ic4533af04946ee0493c762ca79aaf46ee0f80e00
Signed-off-by: maxbr <maxbr@mi.fu-berlin.de>
12 files changed:
tools/pharos-dashboard/docker-compose.yml
tools/pharos-dashboard/src/account/migrations/0002_auto_20161005_1201.py [new file with mode: 0644]
tools/pharos-dashboard/src/account/models.py
tools/pharos-dashboard/src/account/tasks.py [new file with mode: 0644]
tools/pharos-dashboard/src/account/views.py
tools/pharos-dashboard/src/api/urls.py
tools/pharos-dashboard/src/api/views.py
tools/pharos-dashboard/src/dashboard/migrations/0002_auto_20161005_1202.py [new file with mode: 0644]
tools/pharos-dashboard/src/dashboard/models.py
tools/pharos-dashboard/src/templates/account/user_list.html
tools/pharos-dashboard/src/templates/account/userprofile_update_form.html
tools/pharos-dashboard/src/templates/dashboard/resource_detail.html

index 00a8d15..b487620 100644 (file)
@@ -59,7 +59,7 @@ services:
     worker:
         restart: always
         build: ./worker/
-        command: bash -c "celery -A pharos_dashboard worker -l info -B"
+        command: bash -c "celery -A pharos_dashboard worker -l info -B --schedule=~/celerybeat-schedule""
         env_file: config.env
         links:
             - postgres
diff --git a/tools/pharos-dashboard/src/account/migrations/0002_auto_20161005_1201.py b/tools/pharos-dashboard/src/account/migrations/0002_auto_20161005_1201.py
new file mode 100644 (file)
index 0000000..33d2cc5
--- /dev/null
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-10-05 12:01
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('account', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='userprofile',
+            name='full_name',
+            field=models.CharField(default='', max_length=100),
+        ),
+        migrations.AddField(
+            model_name='userprofile',
+            name='jira_url',
+            field=models.CharField(default='', max_length=100),
+        ),
+    ]
index 621f669..d87ee18 100644 (file)
@@ -11,6 +11,7 @@
 from django.db import models
 
 from django.contrib.auth.models import User
+from rest_framework.authtoken.models import Token
 
 from dashboard.models import Resource
 
@@ -23,8 +24,15 @@ class UserProfile(models.Model):
     ssh_public_key = models.FileField(upload_to=upload_to, null=True, blank=True)
     pgp_public_key = models.FileField(upload_to=upload_to, null=True, blank=True)
     company = models.CharField(max_length=200, blank=False)
+
     oauth_token = models.CharField(max_length=1024, blank=False)
     oauth_secret = models.CharField(max_length=1024, blank=False)
 
+    jira_url = models.CharField(max_length=100, default='')
+    full_name = models.CharField(max_length=100, default='')
+
     class Meta:
         db_table = 'user_profile'
+
+    def __str__(self):
+        return self.user.username
diff --git a/tools/pharos-dashboard/src/account/tasks.py b/tools/pharos-dashboard/src/account/tasks.py
new file mode 100644 (file)
index 0000000..bfb865d
--- /dev/null
@@ -0,0 +1,34 @@
+##############################################################################
+# Copyright (c) 2016 Max Breitenfeldt and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from celery import shared_task
+from django.contrib.auth.models import User
+from jira import JIRAError
+
+from account.jira_util import get_jira
+
+
+@shared_task
+def sync_jira_accounts():
+    users = User.objects.all()
+    for user in users:
+        jira = get_jira(user)
+        try:
+            user_dict = jira.myself()
+        except JIRAError:
+            # User can be anonymous (local django admin account)
+            continue
+        user.email = user_dict['emailAddress']
+        user.userprofile.url = user_dict['self']
+        user.userprofile.full_name = user_dict['displayName']
+        print(user_dict)
+
+        user.userprofile.save()
+        user.save()
\ No newline at end of file
index 3b4269d..ac973f5 100644 (file)
@@ -12,6 +12,7 @@ import os
 import urllib
 
 import oauth2 as oauth
+from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth import logout, authenticate, login
 from django.contrib.auth.decorators import login_required
@@ -19,17 +20,14 @@ from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.auth.models import User
 from django.urls import reverse
 from django.utils.decorators import method_decorator
-from django.views.generic import RedirectView
-from django.views.generic import TemplateView
-from django.views.generic import UpdateView
+from django.views.generic import RedirectView, TemplateView, UpdateView
+
 from jira import JIRA
+from rest_framework.authtoken.models import Token
 
 from account.forms import AccountSettingsForm
 from account.jira_util import SignatureMethod_RSA_SHA1
 from account.models import UserProfile
-from django.conf import settings
-
-consumer = oauth.Consumer(settings.OAUTH_CONSUMER_KEY, settings.OAUTH_CONSUMER_SECRET)
 
 
 @method_decorator(login_required, name='dispatch')
@@ -46,9 +44,16 @@ class AccountSettingsView(UpdateView):
     def get_object(self, queryset=None):
         return self.request.user.userprofile
 
+    def get_context_data(self, **kwargs):
+        token, created = Token.objects.get_or_create(user=self.request.user)
+        context = super(AccountSettingsView, self).get_context_data(**kwargs)
+        context.update({'title': "Settings", 'token': token})
+        return context
+
 
 class JiraLoginView(RedirectView):
     def get_redirect_url(self, *args, **kwargs):
+        consumer = oauth.Consumer(settings.OAUTH_CONSUMER_KEY, settings.OAUTH_CONSUMER_SECRET)
         client = oauth.Client(consumer)
         client.set_signature_method(SignatureMethod_RSA_SHA1())
 
@@ -61,7 +66,7 @@ class JiraLoginView(RedirectView):
         self.request.session['request_token'] = dict(urllib.parse.parse_qsl(content.decode()))
         # Step 3. Redirect the user to the authentication URL.
         url = settings.OAUTH_AUTHORIZE_URL + '?oauth_token=' + \
-                self.request.session['request_token']['oauth_token'] + \
+              self.request.session['request_token']['oauth_token'] + \
               '&oauth_callback=' + settings.OAUTH_CALLBACK_URL
         return url
 
@@ -75,6 +80,7 @@ class JiraLogoutView(LoginRequiredMixin, RedirectView):
 class JiraAuthenticatedView(RedirectView):
     def get_redirect_url(self, *args, **kwargs):
         # Step 1. Use the request token in the session to build a new client.
+        consumer = oauth.Consumer(settings.OAUTH_CONSUMER_KEY, settings.OAUTH_CONSUMER_SECRET)
         token = oauth.Token(self.request.session['request_token']['oauth_token'],
                             self.request.session['request_token']['oauth_token_secret'])
         client = oauth.Client(consumer, token)
@@ -122,6 +128,7 @@ class JiraAuthenticatedView(RedirectView):
         # redirect user to settings page to complete profile
         return url
 
+@method_decorator(login_required, name='dispatch')
 class UserListView(TemplateView):
     template_name = "account/user_list.html"
 
index 5206ac7..dfbe1ac 100644 (file)
@@ -35,4 +35,5 @@ router.register(r'bookings', BookingViewSet)
 
 urlpatterns = [
     url(r'^', include(router.urls)),
+    url(r'^token$', GenerateTokenView.as_view(), name='generate_token'),
 ]
\ No newline at end of file
index 761ce6e..55de5c6 100644 (file)
 
 from rest_framework import viewsets
 
+from django.contrib.auth.decorators import login_required
+from django.utils.decorators import method_decorator
 from api.serializers import ResourceSerializer, ServerSerializer, BookingSerializer
 from booking.models import Booking
 from dashboard.models import Resource, Server
+from django.views import View
+from rest_framework.authtoken.models import Token
+from django.shortcuts import redirect
 
 
 class BookingViewSet(viewsets.ModelViewSet):
@@ -30,4 +35,15 @@ class ServerViewSet(viewsets.ModelViewSet):
 class ResourceViewSet(viewsets.ModelViewSet):
     queryset = Resource.objects.all()
     serializer_class = ResourceSerializer
-    filter_fields = ('name',)
\ No newline at end of file
+    filter_fields = ('name',)
+
+
+@method_decorator(login_required, name='dispatch')
+class GenerateTokenView(View):
+    def get(self, request, *args, **kwargs):
+        user = self.request.user
+        token, created = Token.objects.get_or_create(user=user)
+        if not created:
+            token.delete()
+            Token.objects.create(user=user)
+        return redirect('account:settings')
diff --git a/tools/pharos-dashboard/src/dashboard/migrations/0002_auto_20161005_1202.py b/tools/pharos-dashboard/src/dashboard/migrations/0002_auto_20161005_1202.py
new file mode 100644 (file)
index 0000000..e47c3b1
--- /dev/null
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2016-10-05 12:02
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dashboard', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='resource',
+            name='vpn_users',
+            field=models.ManyToManyField(blank=True, related_name='user_vpn_users', to=settings.AUTH_USER_MODEL),
+        ),
+    ]
index 050834e..e3f22e6 100644 (file)
@@ -23,7 +23,7 @@ class Resource(models.Model):
     description = models.CharField(max_length=300, blank=True, null=True)
     url = models.CharField(max_length=100, blank=True, null=True)
     owner = models.ForeignKey(User, related_name='user_lab_owner', null=True)
-    vpn_users = models.ManyToManyField(User, related_name='user_vpn_users')
+    vpn_users = models.ManyToManyField(User, related_name='user_vpn_users', blank=True)
     slave = models.ForeignKey(JenkinsSlave, on_delete=models.DO_NOTHING, null=True)
 
     def get_booking_utilization(self, weeks):
index c2b8193..f18e161 100644 (file)
@@ -5,6 +5,8 @@
     <thead>
     <tr>
         <th>Username</th>
+        <th>Full Name</th>
+        <th>Email</th>
         <th>Company</th>
         <th>SSH Key</th>
         <th>GPG Key</th>
             <td>
                 {{ user.username }}
             </td>
+            <td>
+                {{ user.userprofile.full_name }}
+            </td>
+            <td>
+                {{ user.email }}
+            </td>
             <td>
                 {{ user.userprofile.company }}
             </td>
             <td>
-               {% if user.userprofile.ssh_public_key %}
-                <a href={{ user.userprofile.ssh_public_key.url }}>SSH</a>
-               {% endif %}
+                {% if user.userprofile.ssh_public_key %}
+                    <a href={{ user.userprofile.ssh_public_key.url }}>SSH</a>
+                {% endif %}
             </td>
             <td>
-               {% if user.userprofile.pgp_public_key %}
-                <a href={{ user.userprofile.pgp_public_key.url }}>GPG</a>
-               {% endif %}
+                {% if user.userprofile.pgp_public_key %}
+                    <a href={{ user.userprofile.pgp_public_key.url }}>GPG</a>
+                {% endif %}
             </td>
         </tr>
     {% endfor %}
index 542ea81..f4bb7b5 100644 (file)
                         <form enctype="multipart/form-data" method="post">
                             {% csrf_token %}
                             {% bootstrap_form form %}
+                            <p><b>API Token</b>
+                                <a href="{% url 'generate_token' %}" class="btn btn-default">
+                                    Generate
+                                </a>
+                            </p>
+                            <p style="word-wrap: break-word;">{{ token.key }}</p>
+
+                            <p></p>
                             {% buttons %}
                                 <button type="submit" class="btn btn btn-success">
-                                    Save
+                                    Save Profile
                                 </button>
                             {% endbuttons %}
                         </form>
index 657d565..e0b29bd 100644 (file)
                 </p>
                 <p>
                     <b>Email: </b>
+                    {{ resource.owner.email }}
                 </p>
                 <p>
                     <a href="{% url 'booking:create' resource_id=resource.id %}" class="btn