Commit e15eb771 authored by Mirko Vucicevich's avatar Mirko Vucicevich
Browse files

Added multiple documents option. Separated tracked documents to a separate...

Added multiple documents option. Separated tracked documents to a separate page. Need to add a filter in td dropdown.
parent 7ed998e9
......@@ -3,6 +3,8 @@ from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import Group as UselessGroup
from projector.models import *
from markdownx.admin import MarkdownxModelAdmin
from martor.widgets import AdminMartorWidget
admin.AdminSite.index_template = 'admin-extra/index.html'
......@@ -46,16 +48,54 @@ class NoticeInline(admin.StackedInline):
extra = 1
class SLAInline(admin.StackedInline):
model = ServiceLevelAgreement
fields = [
'body'
]
class OwnProjectFilter(admin.SimpleListFilter):
title = "Project"
parameter_name = 'project'
def lookups(self, request, model_admin):
return model_admin.get_queryset(request)\
.values_list('project__pk', 'project__title')
#.filter(project__contact__user=request.user)\
def queryset(self, request, queryset):
if self.value():
return queryset.filter(project=self.value())
class TrackedDocumentAdmin(MarkdownxModelAdmin):
list_display = ['title', 'project']
list_filter = [OwnProjectFilter]
fields = [
'title',
'slug',
'body',
]
prepopulated_fields = {'slug': ("title",)}
class Media:
css = {'all': ['project_admin.css']}
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(project__contact__user=request.user)
def save_model(self, request, obj, form, change):
# Create revisions...
if change:
# Clone this guy...
original = TrackedDocument.objects\
.values('author_id', 'body', 'date_updated')\
.get(id=obj.id)
print(original)
DocumentVersion.objects.create(
document=obj,
**original
)
obj.author = request.user
super().save_model(request, obj, form, change)
class ProjectAdmin(MarkdownxModelAdmin):
change_form_template = 'project_admin_change.html'
search_fields = ['title']
list_filter = ['groups__name']
list_display = ['title', 'url', 'get_groups']
......@@ -74,31 +114,10 @@ class ProjectAdmin(MarkdownxModelAdmin):
prepopulated_fields = {'slug': ("title",)}
filter_horizontal = ['technologies', 'groups']
inlines = [ContributorInline, ContactInline, NoticeInline, SLAInline]
inlines = [ContributorInline, ContactInline, NoticeInline]
def get_groups(self, obj):
return ", ".join(obj.groups.order_by('name').values_list('name', flat=True))
def save_formset(self, request, form, formset, change):
# Create revisions for SLA versions...
if formset.model == ServiceLevelAgreement:
instances = formset.save(commit=False)
for instance in instances:
if instance.id is not None:
# Clone this guy...
original = ServiceLevelAgreement.objects\
.values('author_id', 'body', 'date_updated')\
.get(id=instance.id)
print(original)
SLAVersion.objects.create(
sla=instance,
**original
)
instance.author = request.user
instance.save()
else:
formset.save()
class CustomUserAdmin(UserAdmin):
fieldsets = (
(None, {'fields': ('avatar', 'username', 'password')}),
......@@ -117,6 +136,7 @@ class GroupAdmin(admin.ModelAdmin):
admin.site.register(User, CustomUserAdmin)
admin.site.register(TrackedDocument, TrackedDocumentAdmin)
admin.site.register(Project, ProjectAdmin)
admin.site.register(Technology, TechnologyAdmin)
# hide these because they are more usable from the project model view.
......
# Generated by Django 2.2.16 on 2020-10-13 18:00
from django.db import migrations
class Migration(migrations.Migration):
atomic = False
dependencies = [
('projector', '0035_auto_20201007_1510'),
]
operations = [
migrations.RenameModel(
old_name='SLAVersion',
new_name='DocumentVersion',
),
migrations.RenameModel(
old_name='ServiceLevelAgreement',
new_name='TrackedDocument',
),
migrations.AlterModelOptions(
name='documentversion',
options={'ordering': ('-date_updated',)},
),
]
# Generated by Django 2.2.16 on 2020-10-13 18:04
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('projector', '0036_auto_20201013_1400'),
]
operations = [
migrations.RenameField(
model_name='documentversion',
old_name='sla',
new_name='document',
),
]
# Generated by Django 2.2.16 on 2020-10-13 18:06
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('projector', '0037_auto_20201013_1404'),
]
operations = [
migrations.AddField(
model_name='trackeddocument',
name='slug',
field=models.SlugField(blank=True, max_length=256),
),
migrations.AddField(
model_name='trackeddocument',
name='title',
field=models.CharField(blank=True, max_length=256),
),
migrations.AlterField(
model_name='trackeddocument',
name='project',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='projector.Project'),
),
]
# Generated by Django 2.2.16 on 2020-10-13 20:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('projector', '0038_auto_20201013_1406'),
]
operations = [
migrations.AlterField(
model_name='trackeddocument',
name='slug',
field=models.SlugField(max_length=256),
),
migrations.AlterField(
model_name='trackeddocument',
name='title',
field=models.CharField(max_length=256),
),
]
......@@ -2,8 +2,8 @@ from .projects import (
Project,
Technology,
Notice,
ServiceLevelAgreement,
SLAVersion,
TrackedDocument,
DocumentVersion,
)
from .users import (
......
from django.db import models
"""class Hook(models.Model):
project = models.ForeignKey('projector.Project')
endpoint = models.SlugField(max_length=256)
configuration = models.JSONField
log = models.TextField(blank=True)
"""
......@@ -139,11 +139,13 @@ class Technology(models.Model):
return self.name
class ServiceLevelAgreement(models.Model):
project = models.OneToOneField(
class TrackedDocument(models.Model):
project = models.ForeignKey(
'projector.Project',
on_delete=models.CASCADE
)
title = models.CharField(max_length=256)
slug = models.SlugField(max_length=256)
author = models.ForeignKey(
'projector.User',
null=True, blank=True,
......@@ -152,11 +154,11 @@ class ServiceLevelAgreement(models.Model):
date_updated = models.DateTimeField(auto_now=True)
body = MarkdownxField()
class SLAVersion(models.Model):
class DocumentVersion(models.Model):
# Identical to above -- we store a version of past SLAs when they change
# Business logic found in admin.py
sla = models.ForeignKey(
'projector.ServiceLevelAgreement',
document = models.ForeignKey(
'projector.TrackedDocument',
on_delete=models.CASCADE,
related_name='past_versions'
)
......
......@@ -16,6 +16,7 @@ INSTALLED_APPS = [
'markdownx',
'projector',
'overlay',
'martor',
]
MIDDLEWARE = [
......
......@@ -22,13 +22,13 @@ body{
}
h1{
font-size: 1.5em;
font-size: 1.8em;
}
h2{
font-size: 1.3em;
font-size: 1.5em;
}
h3{
font-size: 1.13em;
font-size: 1.3em;
}
h4{
font-size: 1.1em;
......
.markdownx{
display: flex !important;
}
.markdownx > *{
flex-basis: 0;
flex-grow: 1;
margin: 0 8px;
}
.markdownx-preview * {
all: revert;
}
\ No newline at end of file
(function($) {
$(document).on('formset:added', function(event, $row, formsetName) {
if (formsetName == 'trackeddocument_set'){
let title_field = $row.find('input[id$="title"]')
let slug_field = $row.find('input[id$="slug"]') .data('dependency_list', ['title'])
slug_field.prepopulate(
['#' + title_field[0].id], 256, false
);
}
});
$(document).on('formset:removed', function(event, $row, formsetName) {
// Row removed
});
})(django.jQuery);
\ No newline at end of file
......@@ -35,16 +35,16 @@
<a class="nav-link" id="pills-profile-tab" data-toggle="pill" href="#pills-profile" role="tab" aria-controls="pills-profile" aria-selected="false">Notifications <span class="badge badge-light">{{ project.active_notices.count }}</span></a>
</li>
{% endif %}
{% if project.servicelevelagreement %}
<li class="nav-item">
<a class="nav-link" id="pills-sla-tab" data-toggle="pill" href="#pills-sla" role="tab" aria-controls="pills-profile" aria-selected="false">Service Level Agreement</a>
</li>
{% endif %}
{% if project.gitlab_id and project.changelogmd %}
<li class="nav-item">
<a class="nav-link" id="pills-changelog-tab" data-toggle="pill" href="#pills-changelog" role="tab" aria-controls="pills-changelog" aria-selected="false">Change Log</a>
</li>
{% endif %}
{% for doc in project.trackeddocument_set.all %}
<li class="nav-item">
<a class="nav-link" id="pills-sla-tab" data-toggle="pill" href="#pills-td-{{doc.slug|default:forloop.counter0}}" role="tab" aria-controls="pills-profile" aria-selected="false">{{doc.title|default:forloop.counter0}}</a>
</li>
{% endfor %}
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab">
......@@ -61,17 +61,17 @@
</div>
{% endfor %}
</div>
{% if project.servicelevelagreement %}
<div class="tab-pane fade" id="pills-sla" role="tabpanel" aria-labelledby="pills-sla-tab">
{% for doc in project.trackeddocument_set.all %}
<div class="tab-pane fade" id="pills-td-{{doc.slug|default:forloop.counter0}}" role="tabpanel" aria-labelledby="pills-td-{{doc.slug|default:forloop.counter0}}">
<div class="d-flex justify-content-between align-items-end">
<span>Updated on {{project.servicelevelagreement.date_updated}}</span>
<a href="{% url 'service-level-agreement' project.slug %}" target="_blank" class="">
<span>Updated on {{doc.date_updated}}</span>
<a href="{% url 'view-document' project.slug doc.slug %}" target="_blank" class="">
View Printable Version / History
</a>
</div>
{{ project.servicelevelagreement.body|markdown }}
{{ doc.body|markdown }}
</div>
{% endif %}
{% endfor %}
<div class="tab-pane fade" id="pills-changelog" role="tabpanel" aria-labelledby="pills-changelog-tab">
{{ project.changelog|markdown }}
</div>
......
{% extends 'admin/change_form.html' %}
{% load static %}
{% block admin_change_form_document_ready %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'project_admin.css' %}">
<script src="{% static 'project_admin.js' %}"></script>
{% endblock %}
\ No newline at end of file
{% load components %}
{% load static %}
<html>
<head>
<link rel="stylesheet" href="{% static 'printable.css' %}">
<style>
......@@ -7,10 +8,11 @@
display: none;
}
</style>
<title>{{object.project.title}}: {{object.title}}</title>
</head>
<body>
<h1>
{{project.title}} Service Level Agreement
{{object.project.title}}: {{object.title}}
</h1>
<table class="table table-sm table-bordered">
......@@ -19,7 +21,7 @@
</thead>
<tbody>
<tr>
{% with project.servicelevelagreement as sla %}
{% with object as sla %}
<td>{{sla.date_updated}}</td>
<td>{{sla.author}}</td>
<td class="sla-version text-center">
......@@ -28,7 +30,7 @@
</td>
{% endwith %}
</tr>
{% for sla in project.servicelevelagreement.past_versions.all %}
{% for sla in object.past_versions.all %}
<tr>
<td>{{sla.date_updated}}</td>
<td>{{sla.author}}</td>
......@@ -42,12 +44,12 @@
</table>
<h1>Table of Contents</h1>
{% with project.servicelevelagreement as sla %}
{% with object as sla %}
<div class="sla-view d-none" id="current">
{{sla.body|markdown_with_toc}}
</div>
{% endwith %}
{% for sla in project.servicelevelagreement.past_versions.all %}
{% for sla in object.past_versions.all %}
<div class="sla-view d-none" id="{{sla.id}}">
{{sla.body|markdown_with_toc}}
</div>
......@@ -72,6 +74,7 @@
for (let i=0; i < views.length; i++){
let content = views[i]
let link = links[i]
console.log(i)
let id = content.getAttribute('id')
if ( id == val){
......@@ -82,6 +85,8 @@
else {
content.classList.add('d-none')
link.querySelector('.currently-viewing').classList.add('d-none')
console.log(link.querySelector('.view-this'))
link.querySelector('.view-this').classList.remove('d-none')
}
}
......@@ -90,4 +95,6 @@
updateURL(val)
}
}
</script>
\ No newline at end of file
</script>
</body>
</html>
\ No newline at end of file
......@@ -18,7 +18,6 @@ from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from django.conf.urls import url
from markdownx import urls as markdownx
from projector import views
from overlay.views import send_message, project_json_view
try:
......@@ -41,6 +40,6 @@ urlpatterns = [
path('<slug:slug>/', views.DetailView.as_view(), name='detail'),
path('<slug:slug>/json', project_json_view, name='json-detail'),
path('<slug:slug>/send-message', send_message, name='send-message'),
path('<slug:slug>/sla', views.ServiceLevelAgreement.as_view(), name='service-level-agreement'),
path('<slug:slug>/document/<slug:doc_slug>', views.DocumentView.as_view(), name='view-document'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
from django.shortcuts import render, get_object_or_404
from django.views import generic
from .models import Project, Contributor, Contact, Group
from .models import Project, Contributor, Contact, Group, TrackedDocument
from django.http import HttpResponse
from django.db.models import Q
......@@ -70,9 +70,19 @@ class DetailView(generic.DetailView):
context['contributors'] = contributors
return context
class ServiceLevelAgreement(generic.DetailView):
queryset = Project.objects.exclude(servicelevelagreement__isnull=True)
template_name = 'service-level-agreement.html'
class DocumentView(generic.DetailView):
template_name = 'view-document.html'
def get_object(self):
queryset = TrackedDocument.objects.all()
try:
obj = queryset.get(
project__slug=self.kwargs['slug'],
slug=self.kwargs['doc_slug']
)
except queryset.model.DoesNotExist:
raise Http404
return obj
def about(request):
return render(request, 'about.html')
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment