From 40e726c7920217dc583a6f23b9ff0d5c8e1c801e Mon Sep 17 00:00:00 2001 From: Mikael Frykholm <mikael@frykholm.com> Date: Sun, 27 May 2018 11:53:08 +0200 Subject: [PATCH] Djangocon coding Pull contracts and invoices from fortnox, todo is local cache Some more flows in the auth backend. Needs cleanup. --- customerportal/admin.py | 28 ++++++- customerportal/models.py | 74 ++++++++++++++++++- .../templates/customerportal/landing.html | 9 ++- customerportal/views.py | 19 ++++- templates/base_generic.html | 2 +- tranquillity/auth_backend.py | 34 ++++++--- tranquillity/settings.py | 1 + 7 files changed, 149 insertions(+), 18 deletions(-) diff --git a/customerportal/admin.py b/customerportal/admin.py index 8c38f3f..e2bdf16 100644 --- a/customerportal/admin.py +++ b/customerportal/admin.py @@ -1,3 +1,29 @@ +"""Integrate with admin module.""" + from django.contrib import admin +from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin +from django.utils.translation import ugettext_lazy as _ + +from .models import User + + +@admin.register(User) +class UserAdmin(DjangoUserAdmin): + """Define admin model for custom User model with no email field.""" -# Register your models here. + fieldsets = ( + (None, {'fields': ('email', 'password')}), + (_('Personal info'), {'fields': ('first_name', 'last_name','fortnox_external_id')}), + (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', + 'groups', 'user_permissions')}), + (_('Important dates'), {'fields': ('last_login', 'date_joined')}), + ) + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('email', 'password1', 'password2'), + }), + ) + list_display = ('email', 'first_name', 'last_name', 'is_staff') + search_fields = ('email', 'first_name', 'last_name') + ordering = ('email',) diff --git a/customerportal/models.py b/customerportal/models.py index 71a8362..9a36b9f 100644 --- a/customerportal/models.py +++ b/customerportal/models.py @@ -1,3 +1,75 @@ from django.db import models +from django.contrib.auth.models import User +from django.contrib.auth.models import AbstractUser +from django.contrib.auth.models import AbstractUser, BaseUserManager ## A new class is imported. ## +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class UserManager(BaseUserManager): + """Define a model manager for User model with no username field.""" + + use_in_migrations = True + + def _create_user(self, email, password, **extra_fields): + """Create and save a User with the given email and password.""" + if not email: + raise ValueError('The given email must be set') + email = self.normalize_email(email) + user = self.model(email=email, **extra_fields) + user.set_password(password) + user.save(using=self._db) + return user + + def create_user(self, email, password=None, **extra_fields): + """Create and save a regular User with the given email and password.""" + extra_fields.setdefault('is_staff', False) + extra_fields.setdefault('is_superuser', False) + return self._create_user(email, password, **extra_fields) + + def create_superuser(self, email, password, **extra_fields): + """Create and save a SuperUser with the given email and password.""" + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) + + if extra_fields.get('is_staff') is not True: + raise ValueError('Superuser must have is_staff=True.') + if extra_fields.get('is_superuser') is not True: + raise ValueError('Superuser must have is_superuser=True.') + + return self._create_user(email, password, **extra_fields) + + +class User(AbstractUser): + username = None + fortnox_external_id = models.IntegerField() + gpg_public_key = models.CharField(null=True, max_length=4096) + ssh_public_key = models.CharField(null=True, max_length=4096) + last_login = models.DateField(null=True) + cos_id = models.CharField(null=True, max_length=4096) + cos_contractid = models.CharField(null=True, max_length=4096) + cos_anlaggningsid = models.CharField(null=True, max_length=4096) + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = ['fortnox_external_id'] + objects = UserManager() ## This is the new line in the User model. ## + +class vps(models.Model): + user = models.ForeignKey(to=User, on_delete=models.CASCADE) + uuid = models.CharField(max_length=4096) + +class physical_server(models.Model): + user = models.ForeignKey(to=User, on_delete=models.CASCADE) + rack = models.CharField(max_length=4096) + unit = models.IntegerField() + +class IP(models.Model): + user = models.ForeignKey(to=User, on_delete=models.CASCADE) + cidr = models.CharField(max_length=4096) + -# Create your models here. +# graph resources +# port on device (realtime) autodetect on description(Cust: fortnox_external_id) +# cpu +# memory +# disks +# diff --git a/customerportal/templates/customerportal/landing.html b/customerportal/templates/customerportal/landing.html index 7ac974b..f5f873b 100644 --- a/customerportal/templates/customerportal/landing.html +++ b/customerportal/templates/customerportal/landing.html @@ -3,12 +3,13 @@ {% block content %} <h1>Customer data</h1> - {% if bookinstance_list %} + {% if contracts %} <ul> + + {% for invoice in invoices %} + <li class="{% if not invoice.FinalPayDate %}text-danger{% endif %}"> - {% for bookinst in bookinstance_list %} - <li class="{% if bookinst.is_overdue %}text-danger{% endif %}"> - <a href="{% url 'book-detail' bookinst.book.pk %}">{{bookinst.book.title}}</a> ({{ bookinst.due_back }}) + Invoice date:{{invoice.InvoiceDate}} Due Date:{{invoice.DueDate}} Total:{{invoice.Total}} OCR:{{invoice.OCR}} </li> {% endfor %} </ul> diff --git a/customerportal/views.py b/customerportal/views.py index 3578d6a..182086e 100644 --- a/customerportal/views.py +++ b/customerportal/views.py @@ -1,9 +1,24 @@ from django.shortcuts import render from django.http import HttpResponse from django.contrib.auth.decorators import login_required +from django.conf import settings +try: + import requests +except ModuleNotFoundError as e: + pass + @login_required def index(request): - - return render(request, 'customerportal/landing.html') + headers = {"Access-Token":settings.FORTNOX_ACCESS_TOKEN, + "Client-Secret":settings.FORTNOX_CLIENT_SECRET, + "Content-Type":"application/json", + "Accept":"application/json" } + res = requests.get("https://api.fortnox.se/3/contracts/", headers=headers) + contracts=res.json() + contracts = [contract for contract in contracts['Contracts'] if int(contract['CustomerNumber']) == request.user.fortnox_external_id] + res = requests.get("https://api.fortnox.se/3/invoices/", headers=headers) + invoices = res.json() + invoices = [invoice for invoice in invoices['Invoices'] if int(invoice['CustomerNumber']) == request.user.fortnox_external_id] + return render(request, 'customerportal/landing.html',context={'contracts':contracts, 'invoices':invoices}) diff --git a/templates/base_generic.html b/templates/base_generic.html index 76065ac..5a88fa6 100644 --- a/templates/base_generic.html +++ b/templates/base_generic.html @@ -22,7 +22,7 @@ <ul class="sidebar-nav"> <li><a href="{% url 'index' %}">Home</a></li> {% if user.is_authenticated %} - <li>User: {{ user.get_username }}</li> + <li>Welcome {{ user.first_name }} {{user.last_name}}</li> <li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li> {% else %} <li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li> diff --git a/tranquillity/auth_backend.py b/tranquillity/auth_backend.py index e8c682a..de43992 100644 --- a/tranquillity/auth_backend.py +++ b/tranquillity/auth_backend.py @@ -1,11 +1,14 @@ from django.contrib.auth import get_user_model from django.contrib.auth.models import User from django.contrib.auth.backends import ModelBackend -import requests +try: + import requests +except ModuleNotFoundError as e: + pass from django.conf import settings class EmailBackend(ModelBackend): - def fetch_fortnox(self): + def fetch_fortnox(self, customer_id=None): """ Fetch all active customers from Fortnox API. Return as dict keyed on email.""" res = None customers = {} @@ -13,9 +16,14 @@ class EmailBackend(ModelBackend): "Client-Secret":settings.FORTNOX_CLIENT_SECRET, "Content-Type":"application/json", "Accept":"application/json" } - - res = requests.get("https://api.fortnox.se/3/customers?filter=active", headers=headers) - for customer in res.json()['Customers']: + if customer_id: #We already have id, use that to update info in local db + res = requests.get(f"https://api.fortnox.se/3/customers/{customer_id}?filter=active", headers=headers) + res = res.json() + res['Customer'] = [res['Customer']] + else: + res = requests.get("https://api.fortnox.se/3/customers?filter=active", headers=headers) + + for customer in res['Customer']: customers[customer['Email']] = customer return customers @@ -25,22 +33,30 @@ class EmailBackend(ModelBackend): user = UserModel.objects.get(email=username) except UserModel.DoesNotExist: customers = self.fetch_fortnox() + # import pdb;pdb.set_trace() if username in customers: if ' ' in customers[username]['Name']: (fname,lname) = customers[username]['Name'].split(' ',1) - user = User.objects.create_user(username=username, - email=username, + user = User.objects.create_user(email=username, first_name=fname, - last_name=lname) + last_name=lname, + fortnox_external_id=int(customers[username]['CustomerNumber'])) return user else: fname = customers[username]['Name'] user = User.objects.create_user(username=username, email=username, - first_name=fname) + first_name=fname, + fortnox_external_id=int(customers[username]['CustomerNumber'])) return user return None else: + customer = self.fetch_fortnox(customer_id=user.fortnox_external_id) + if not customer: + user.is_active=False + return None + user.first_name = customer[user.email]['Name'] #TODO synd more data + user.save() if user.check_password(password): return user return None diff --git a/tranquillity/settings.py b/tranquillity/settings.py index b01cf5a..fc78bab 100644 --- a/tranquillity/settings.py +++ b/tranquillity/settings.py @@ -128,3 +128,4 @@ STATIC_URL = '/static/' # Redirect to home URL after login (Default redirects to /accounts/profile/) LOGIN_REDIRECT_URL = '/' AUTHENTICATION_BACKENDS = ['tranquillity.auth_backend.EmailBackend'] +AUTH_USER_MODEL = 'customerportal.User' -- 2.39.5