Added django-simple-history and replaced the old (and unused) PersonHistory class with a history=HistoricalRecords() field on Person. Added the needed migrations and changes to admin, resources, and settings. Related to issues #2505 and #2507.

- Legacy-Id: 15096
This commit is contained in:
Henrik Levkowetz 2018-04-27 17:36:20 +00:00
parent 53c4ac36db
commit c7d31b44c3
7 changed files with 112 additions and 48 deletions

View file

@ -7,7 +7,7 @@ import debug # pyflakes:ignore
from ietf.utils.test_utils import TestCase
from ietf.doc.models import StateType
class StateHelpTest(TestCase):
class HelpPageTests(TestCase):
def test_state_index(self):
url = reverse('ietf.help.views.state_index')
@ -21,3 +21,7 @@ class StateHelpTest(TestCase):
self.assertIn(name, content)
def test_personal_information_help(self):
r = self.client.get('/help/personal-information')
self.assertContains(r, 'personal information')
self.assertContains(r, 'GDPR')

View file

@ -1,7 +1,7 @@
from django.contrib import admin
from ietf.person.models import Email, Alias, Person, PersonHistory, PersonalApiKey, PersonEvent, PersonApiKeyEvent
from ietf.person.models import Email, Alias, Person, PersonalApiKey, PersonEvent, PersonApiKeyEvent, HistoricalPerson
from ietf.person.name import name_parts
class EmailAdmin(admin.ModelAdmin):
@ -33,16 +33,6 @@ class PersonAdmin(admin.ModelAdmin):
# actions = None
admin.site.register(Person, PersonAdmin)
class PersonHistoryAdmin(admin.ModelAdmin):
def plain_name(self, obj):
prefix, first, middle, last, suffix = name_parts(obj.name)
return "%s %s" % (first, last)
list_display = ['name', 'short', 'plain_name', 'time', 'user', 'person', ]
list_filter = ['time']
raw_id_fields = ['person', 'user']
search_fields = ['name', 'ascii']
admin.site.register(PersonHistory, PersonHistoryAdmin)
class PersonalApiKeyAdmin(admin.ModelAdmin):
list_display = ['id', 'person', 'created', 'endpoint', 'valid', 'count', 'latest', ]
list_filter = ['endpoint', 'created', ]
@ -61,3 +51,14 @@ class PersonApiKeyEventAdmin(admin.ModelAdmin):
search_fields = ["person__name", ]
raw_id_fields = ['person', ]
admin.site.register(PersonApiKeyEvent, PersonApiKeyEventAdmin)
class HistoricalPersonAdmin(admin.ModelAdmin):
def plain_name(self, obj):
prefix, first, middle, last, suffix = name_parts(obj.name)
return "%s %s" % (first, last)
list_display = ["history_date", "name", "plain_name", "time", "history_user", "history_change_reason", ]
search_fields = ["name", "ascii"]
raw_id_fields = ["user", "history_user", ]
# actions = None
admin.site.register(HistoricalPerson, HistoricalPersonAdmin)

View file

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-27 06:46
from __future__ import unicode_literals
import datetime
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('person', '0003_auto_20180426_0517'),
]
operations = [
migrations.CreateModel(
name='HistoricalPerson',
fields=[
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('time', models.DateTimeField(default=datetime.datetime.now)),
('name', models.CharField(db_index=True, help_text=b'Preferred form of name.', max_length=255, verbose_name=b'Full Name (Unicode)')),
('ascii', models.CharField(help_text=b'Name as rendered in ASCII (Latin, unaccented) characters.', max_length=255, verbose_name=b'Full Name (ASCII)')),
('ascii_short', models.CharField(blank=True, help_text=b'Example: A. Nonymous. Fill in this with initials and surname only if taking the initials and surname of the ASCII name above produces an incorrect initials-only form. (Blank is OK).', max_length=32, null=True, verbose_name=b'Abbreviated Name (ASCII)')),
('affiliation', models.CharField(blank=True, help_text=b'Employer, university, sponsor, etc.', max_length=255)),
('biography', models.TextField(blank=True, help_text=b'Short biography for use on leadership pages. Use plain text or reStructuredText markup.')),
('photo', models.TextField(blank=True, default=None, max_length=100)),
('photo_thumb', models.TextField(blank=True, default=None, max_length=100)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_change_reason', models.CharField(max_length=100, null=True)),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
'verbose_name': 'historical person',
},
),
migrations.RemoveField(
model_name='personhistory',
name='person',
),
migrations.RemoveField(
model_name='personhistory',
name='user',
),
migrations.DeleteModel(
name='PersonHistory',
),
]

View file

@ -16,6 +16,7 @@ from django.db import models
from django.contrib.auth.models import User
from django.template.loader import render_to_string
from django.utils.text import slugify
from simple_history.models import HistoricalRecords
import debug # pyflakes:ignore
@ -27,7 +28,9 @@ from ietf.person.name import unidecode_name
from ietf.utils.models import ForeignKey, OneToOneField
class PersonInfo(models.Model):
class Person(models.Model):
history = HistoricalRecords()
user = OneToOneField(User, blank=True, null=True)
time = models.DateTimeField(default=datetime.datetime.now) # When this Person record entered the system
# The normal unicode form of the name. This must be
# set to the same value as the ascii-form if equal.
@ -143,24 +146,21 @@ class PersonInfo(models.Model):
def has_drafts(self):
from ietf.doc.models import Document
return Document.objects.filter(documentauthor__person=self, type='draft').exists()
def rfcs(self):
from ietf.doc.models import Document
rfcs = list(Document.objects.filter(documentauthor__person=self, type='draft', states__slug='rfc'))
rfcs.sort(key=lambda d: d.canonical_name() )
return rfcs
def active_drafts(self):
from ietf.doc.models import Document
return Document.objects.filter(documentauthor__person=self, type='draft', states__slug='active').order_by('-time')
def expired_drafts(self):
from ietf.doc.models import Document
return Document.objects.filter(documentauthor__person=self, type='draft', states__slug__in=['repl', 'expired', 'auth-rm', 'ietf-rm']).order_by('-time')
class Meta:
abstract = True
class Person(PersonInfo):
user = OneToOneField(User, blank=True, null=True)
def save(self, *args, **kwargs):
created = not self.pk
super(Person, self).save(*args, **kwargs)
@ -198,10 +198,6 @@ class Person(PersonInfo):
ct1['affiliation']= self.affiliation
return ct1
class PersonHistory(PersonInfo):
person = ForeignKey(Person, related_name="history_set")
user = ForeignKey(User, blank=True, null=True)
class Alias(models.Model):
"""This is used for alternative forms of a name. This is the
primary lookup point for names, and should always contain the

View file

@ -6,7 +6,7 @@ from tastypie.cache import SimpleCache
from ietf import api
from ietf.person.models import (Person, Email, Alias, PersonHistory, PersonalApiKey, PersonEvent, PersonApiKeyEvent)
from ietf.person.models import (Person, Email, Alias, PersonalApiKey, PersonEvent, PersonApiKeyEvent, HistoricalPerson)
from ietf.utils.resources import UserResource
@ -24,7 +24,6 @@ class PersonResource(ModelResource):
"name": ALL,
"ascii": ALL,
"ascii_short": ALL,
"address": ALL,
"affiliation": ALL,
"photo": ALL,
"biography": ALL,
@ -61,29 +60,6 @@ class AliasResource(ModelResource):
}
api.person.register(AliasResource())
from ietf.utils.resources import UserResource
class PersonHistoryResource(ModelResource):
person = ToOneField(PersonResource, 'person')
user = ToOneField(UserResource, 'user', null=True)
class Meta:
cache = SimpleCache()
queryset = PersonHistory.objects.all()
serializer = api.Serializer()
#resource_name = 'personhistory'
filtering = {
"id": ALL,
"time": ALL,
"name": ALL,
"ascii": ALL,
"ascii_short": ALL,
"address": ALL,
"affiliation": ALL,
"person": ALL_WITH_RELATIONS,
"user": ALL_WITH_RELATIONS,
}
api.person.register(PersonHistoryResource())
class PersonalApiKeyResource(ModelResource):
person = ToOneField(PersonResource, 'person')
class Meta:
@ -141,3 +117,32 @@ class PersonApiKeyEventResource(ModelResource):
"key": ALL_WITH_RELATIONS,
}
api.person.register(PersonApiKeyEventResource())
from ietf.utils.resources import UserResource
class HistoricalPersonResource(ModelResource):
user = ToOneField(UserResource, 'user', null=True)
history_user = ToOneField(UserResource, 'history_user', null=True)
class Meta:
queryset = HistoricalPerson.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'historicalperson'
filtering = {
"id": ALL,
"time": ALL,
"name": ALL,
"ascii": ALL,
"ascii_short": ALL,
"affiliation": ALL,
"biography": ALL,
"photo": ALL,
"photo_thumb": ALL,
"history_id": ALL,
"history_date": ALL,
"history_change_reason": ALL,
"history_type": ALL,
"user": ALL_WITH_RELATIONS,
"history_user": ALL_WITH_RELATIONS,
}
api.person.register(HistoricalPersonResource())

View file

@ -349,6 +349,7 @@ MIDDLEWARE = (
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.http.ConditionalGetMiddleware',
'simple_history.middleware.HistoryRequestMiddleware',
# 'ietf.middleware.sql_log_middleware',
'ietf.middleware.SMTPExceptionMiddleware',
'ietf.middleware.Utf8ExceptionMiddleware',
@ -385,6 +386,7 @@ INSTALLED_APPS = (
'django_password_strength',
'djangobwr',
'form_utils',
'simple_history',
'tastypie',
'widget_tweaks',
# IETF apps

View file

@ -16,6 +16,7 @@ django-bootstrap3>=8.2.1,<9.0.0
django-formtools>=1.0 # instead of django.contrib.formtools in 1.8
django-markup>=1.1
django-password-strength>=1.2.1
django-simple-history>=2.0
django-tastypie>=0.13.2
django-widget-tweaks>=1.3
docutils>=0.12