friday night work
This commit is contained in:
parent
d400cabfde
commit
51994b4600
|
@ -40,7 +40,7 @@ INSTALLED_APPS = [
|
|||
'evolus',
|
||||
'jci',
|
||||
'reversion',
|
||||
'process'
|
||||
'process',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
|
@ -6,7 +6,7 @@ from .models import Audience, Document, DocumentType, Version, Site, Structure,
|
|||
|
||||
|
||||
class DocumentAdmin(VersionAdmin):
|
||||
list_filter = ('type', 'audiences', 'sites', 'structures')
|
||||
list_filter = ('type', 'title')
|
||||
|
||||
|
||||
class DocumentTypeAdmin(admin.ModelAdmin):
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.5 on 2017-10-27 19:13
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
('evolus', '0011_auto_20171027_1512'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='audience',
|
||||
name='group',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='auth.Group'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.5 on 2017-10-27 20:32
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evolus', '0012_audience_group'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='historicaldocument',
|
||||
name='history_user',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='historicaldocument',
|
||||
name='manager',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='historicaldocument',
|
||||
name='type',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='version',
|
||||
old_name='published',
|
||||
new_name='is_published',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='document',
|
||||
name='type',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='evolus.DocumentType'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='version',
|
||||
name='document',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='versions', to='evolus.Document'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='HistoricalDocument',
|
||||
),
|
||||
]
|
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.5 on 2017-10-27 20:55
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('evolus', '0013_auto_20171027_2032'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='document',
|
||||
name='audiences',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='document',
|
||||
name='sites',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='document',
|
||||
name='standards',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='document',
|
||||
name='structures',
|
||||
),
|
||||
]
|
112
evolus/models.py
112
evolus/models.py
|
@ -5,15 +5,17 @@ This module defines the structure and properties of documents.
|
|||
from django.db import models
|
||||
from closuretree.models import ClosureModel
|
||||
from simple_history.models import HistoricalRecords
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import Group, User
|
||||
import reversion
|
||||
|
||||
from process.models import Approval
|
||||
from jci.models import Standard
|
||||
|
||||
|
||||
class Audience(ClosureModel):
|
||||
name = models.CharField(max_length=50)
|
||||
parent = models.ForeignKey('self', related_name='children', null=True, blank=True)
|
||||
group = models.ForeignKey(Group)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -41,6 +43,17 @@ class Structure(ClosureModel):
|
|||
return self.name
|
||||
|
||||
|
||||
class Keyword(ClosureModel):
|
||||
name = models.CharField(max_length=255)
|
||||
parent = models.ForeignKey('self', null=True, blank=True)
|
||||
selectable = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class DocumentType(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
level = models.IntegerField()
|
||||
|
@ -56,16 +69,11 @@ class DocumentType(models.Model):
|
|||
|
||||
@reversion.register()
|
||||
class Document(models.Model):
|
||||
audiences = models.ManyToManyField(Audience)
|
||||
sites = models.ManyToManyField(Site)
|
||||
standards = models.ManyToManyField(Standard, related_name='documents')
|
||||
structures = models.ManyToManyField(Structure)
|
||||
title = models.CharField(max_length=255)
|
||||
overview = models.TextField(blank=True, null=True)
|
||||
type = models.ForeignKey(DocumentType, null=True)
|
||||
type = models.ForeignKey(DocumentType)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
manager = models.ForeignKey(User)
|
||||
history = HistoricalRecords()
|
||||
|
||||
@property
|
||||
def last_published_version(self):
|
||||
|
@ -83,6 +91,48 @@ class Document(models.Model):
|
|||
except Version.DoesNotExist:
|
||||
return 'None'
|
||||
|
||||
def create_new_version(self):
|
||||
"""Creates a new version for this document.
|
||||
|
||||
By default, version 0.0 is created with the chosen template for this document.
|
||||
|
||||
:raises
|
||||
IndexError if there is a current version still in draft
|
||||
|
||||
:returns
|
||||
A new `Version` object if it needs to be created.
|
||||
Raises an `IndexError` exception instead.
|
||||
"""
|
||||
latest_version = self.versions.last()
|
||||
|
||||
if not latest_version or latest_version.is_published:
|
||||
latest_version = Version(
|
||||
document = self,
|
||||
file = self.type.template,
|
||||
revision = 1,
|
||||
major = self.major,
|
||||
)
|
||||
latest_version.save()
|
||||
return latest_version
|
||||
|
||||
if not latest_version.is_published:
|
||||
raise IndexError('The latest version is still in draft.\n '
|
||||
'Publish this first before creating a new one.')
|
||||
|
||||
@property
|
||||
def major(self):
|
||||
latest_version = self.versions.filter(published=True).last()
|
||||
if latest_version:
|
||||
return latest_version.major
|
||||
return 0
|
||||
|
||||
@property
|
||||
def revision(self):
|
||||
latest_version = self.versions.last()
|
||||
if latest_version:
|
||||
return latest_version.revision
|
||||
return 0
|
||||
|
||||
@reversion.create_revision()
|
||||
def save(self, *args, **kwargs):
|
||||
super().save()
|
||||
|
@ -90,52 +140,50 @@ class Document(models.Model):
|
|||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def update_version(self, document):
|
||||
pass
|
||||
|
||||
def publish(self):
|
||||
pass
|
||||
|
||||
|
||||
class Keyword(ClosureModel):
|
||||
name = models.CharField(max_length=255)
|
||||
parent = models.ForeignKey('self', null=True, blank=True)
|
||||
selectable = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@reversion.register()
|
||||
class Version(models.Model):
|
||||
document = models.ForeignKey(Document)
|
||||
document = models.ForeignKey(Document, related_name='versions')
|
||||
file = models.FileField(upload_to='revisions/')
|
||||
keywords = models.ManyToManyField(Keyword, blank=True, related_name='document_versions')
|
||||
major = models.PositiveIntegerField()
|
||||
revision = models.PositiveIntegerField()
|
||||
published = models.BooleanField(default=False)
|
||||
is_published = models.BooleanField(default=False)
|
||||
revised_at = models.DateTimeField(null=True, blank=True)
|
||||
authors = models.ManyToManyField(User, related_name='authors')
|
||||
reviewers = models.ManyToManyField(User, related_name='reviewers')
|
||||
validators = models.ManyToManyField(User, related_name='validators')
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
@reversion.create_revision()
|
||||
def publish(self):
|
||||
if not self.is_published:
|
||||
if self.validators.count() == 0:
|
||||
raise ValueError('There are no validators for this version. \n'
|
||||
'Please, add at least one of them.')
|
||||
|
||||
Approval.objects.get_or_create(
|
||||
document_version=self
|
||||
)
|
||||
|
||||
@reversion.create_revision()
|
||||
def update(self, *args, **kwargs):
|
||||
if not self.is_published:
|
||||
self.revision = self.revision + 1
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
raise ValueError('A published version cannot be updated.')
|
||||
|
||||
@reversion.create_revision()
|
||||
def save(self, *args, **kwargs):
|
||||
self.revision = self.revision + 1
|
||||
super().save(*args, **kwargs)
|
||||
super().save()
|
||||
|
||||
class Meta:
|
||||
unique_together = ('document', 'major')
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return 'Published' if self.published else 'Draft'
|
||||
return 'Published' if self.is_published else 'Draft'
|
||||
|
||||
def __str__(self):
|
||||
return 'v{}.{} ({})'.format(self.major, self.revision, self.status)
|
||||
|
||||
def publish(self):
|
||||
pass
|
||||
|
|
|
@ -1,3 +1,36 @@
|
|||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User, Group
|
||||
from model_mommy import mommy
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
class TestDocument(TestCase):
|
||||
def setUp(self):
|
||||
self.document = mommy.make('evolus.Document')
|
||||
|
||||
def test_create_document_version(self):
|
||||
version = self.document.create_new_version()
|
||||
|
||||
self.assertEquals('v0.1 (Draft)', str(version))
|
||||
|
||||
def test_document_major_version(self):
|
||||
self.assertEquals(0, self.document.major)
|
||||
v1 = self.document.create_new_version()
|
||||
self.assertEquals(0, self.document.major)
|
||||
|
||||
|
||||
def test_document_major_version_0(self):
|
||||
v1 = self.document.create_new_version()
|
||||
v1.publish()
|
||||
self.assertEquals(1, self.document.major)
|
||||
self.assertEquals(0, v1.revision)
|
||||
|
||||
def test_document_version_update(self):
|
||||
v1 = self.document.create_new_version()
|
||||
print(v1)
|
||||
self.assertEquals(1, v1.revision)
|
||||
v1.update()
|
||||
self.assertEquals(2, v1.revision)
|
||||
v1.update()
|
||||
self.assertEquals(3, v1.revision)
|
||||
v1.publish()
|
||||
self.assertEquals(0, v1.revision)
|
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.5 on 2017-10-27 20:55
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('process', '0003_auto_20170921_1855'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='process',
|
||||
name='process_type',
|
||||
),
|
||||
]
|
|
@ -2,12 +2,9 @@ from django.db import models
|
|||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from evolus.models import Version
|
||||
|
||||
|
||||
class Process(models.Model):
|
||||
process_type = models.CharField(max_length=50)
|
||||
document_version = models.ForeignKey(Version)
|
||||
document_version = models.ForeignKey('evolus.Version')
|
||||
|
||||
def percentage_of_completion(self):
|
||||
total = self.tasks.count()
|
||||
|
@ -15,13 +12,8 @@ class Process(models.Model):
|
|||
return self.tasks.filter(status=3).count() / self.tasks.count() * 100
|
||||
return 'NaN'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not self.pk and not self.process_type:
|
||||
self.process_type = self.PROCESS_TYPE
|
||||
|
||||
def __str__(self):
|
||||
return '{}: {}'.format(self.process_type, self.document_version)
|
||||
return 'Process on {}'.format(self.document_version)
|
||||
|
||||
def create_task(self, assigned_to):
|
||||
task, created = Task.objects.get_or_create(status=1, assigned_to=assigned_to, process=self)
|
||||
|
@ -50,6 +42,12 @@ class Task(models.Model):
|
|||
class Meta:
|
||||
unique_together = ('assigned_to', 'process', 'status')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save()
|
||||
|
||||
if self.process.percentage_of_completion == 100.0:
|
||||
self.process.finalize()
|
||||
|
||||
|
||||
class PublishedProcessMixin(object):
|
||||
def save(self, *args, **kwargs):
|
||||
|
@ -80,6 +78,12 @@ class Approval(Process, DraftProcessMixin):
|
|||
for validator in self.document_version.validators.all():
|
||||
self.create_task(validator)
|
||||
|
||||
def finalize(self):
|
||||
self.document_version.is_published = True
|
||||
self.document_version.major = self.document_version.major + 1
|
||||
self.document_version.revision = 0
|
||||
self.document_version.save()
|
||||
|
||||
|
||||
class GatherComments(Process, DraftProcessMixin):
|
||||
PROCESS_TYPE = 'GatherComments'
|
||||
|
|
Loading…
Reference in New Issue