Skip to content

Commit c7d31b4

Browse files
committed
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 ietf-tools#2505 and ietf-tools#2507.
- Legacy-Id: 15096
1 parent 53c4ac3 commit c7d31b4

7 files changed

Lines changed: 112 additions & 48 deletions

File tree

ietf/help/tests_views.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ietf.utils.test_utils import TestCase
88
from ietf.doc.models import StateType
99

10-
class StateHelpTest(TestCase):
10+
class HelpPageTests(TestCase):
1111

1212
def test_state_index(self):
1313
url = reverse('ietf.help.views.state_index')
@@ -21,3 +21,7 @@ def test_state_index(self):
2121
self.assertIn(name, content)
2222

2323

24+
def test_personal_information_help(self):
25+
r = self.client.get('/help/personal-information')
26+
self.assertContains(r, 'personal information')
27+
self.assertContains(r, 'GDPR')

ietf/person/admin.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from django.contrib import admin
22

33

4-
from ietf.person.models import Email, Alias, Person, PersonHistory, PersonalApiKey, PersonEvent, PersonApiKeyEvent
4+
from ietf.person.models import Email, Alias, Person, PersonalApiKey, PersonEvent, PersonApiKeyEvent, HistoricalPerson
55
from ietf.person.name import name_parts
66

77
class EmailAdmin(admin.ModelAdmin):
@@ -33,16 +33,6 @@ def plain_name(self, obj):
3333
# actions = None
3434
admin.site.register(Person, PersonAdmin)
3535

36-
class PersonHistoryAdmin(admin.ModelAdmin):
37-
def plain_name(self, obj):
38-
prefix, first, middle, last, suffix = name_parts(obj.name)
39-
return "%s %s" % (first, last)
40-
list_display = ['name', 'short', 'plain_name', 'time', 'user', 'person', ]
41-
list_filter = ['time']
42-
raw_id_fields = ['person', 'user']
43-
search_fields = ['name', 'ascii']
44-
admin.site.register(PersonHistory, PersonHistoryAdmin)
45-
4636
class PersonalApiKeyAdmin(admin.ModelAdmin):
4737
list_display = ['id', 'person', 'created', 'endpoint', 'valid', 'count', 'latest', ]
4838
list_filter = ['endpoint', 'created', ]
@@ -61,3 +51,14 @@ class PersonApiKeyEventAdmin(admin.ModelAdmin):
6151
search_fields = ["person__name", ]
6252
raw_id_fields = ['person', ]
6353
admin.site.register(PersonApiKeyEvent, PersonApiKeyEventAdmin)
54+
55+
56+
class HistoricalPersonAdmin(admin.ModelAdmin):
57+
def plain_name(self, obj):
58+
prefix, first, middle, last, suffix = name_parts(obj.name)
59+
return "%s %s" % (first, last)
60+
list_display = ["history_date", "name", "plain_name", "time", "history_user", "history_change_reason", ]
61+
search_fields = ["name", "ascii"]
62+
raw_id_fields = ["user", "history_user", ]
63+
# actions = None
64+
admin.site.register(HistoricalPerson, HistoricalPersonAdmin)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11.12 on 2018-04-27 06:46
3+
from __future__ import unicode_literals
4+
5+
import datetime
6+
from django.conf import settings
7+
from django.db import migrations, models
8+
import django.db.models.deletion
9+
10+
11+
class Migration(migrations.Migration):
12+
13+
dependencies = [
14+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15+
('person', '0003_auto_20180426_0517'),
16+
]
17+
18+
operations = [
19+
migrations.CreateModel(
20+
name='HistoricalPerson',
21+
fields=[
22+
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
23+
('time', models.DateTimeField(default=datetime.datetime.now)),
24+
('name', models.CharField(db_index=True, help_text=b'Preferred form of name.', max_length=255, verbose_name=b'Full Name (Unicode)')),
25+
('ascii', models.CharField(help_text=b'Name as rendered in ASCII (Latin, unaccented) characters.', max_length=255, verbose_name=b'Full Name (ASCII)')),
26+
('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)')),
27+
('affiliation', models.CharField(blank=True, help_text=b'Employer, university, sponsor, etc.', max_length=255)),
28+
('biography', models.TextField(blank=True, help_text=b'Short biography for use on leadership pages. Use plain text or reStructuredText markup.')),
29+
('photo', models.TextField(blank=True, default=None, max_length=100)),
30+
('photo_thumb', models.TextField(blank=True, default=None, max_length=100)),
31+
('history_id', models.AutoField(primary_key=True, serialize=False)),
32+
('history_date', models.DateTimeField()),
33+
('history_change_reason', models.CharField(max_length=100, null=True)),
34+
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
35+
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
36+
('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)),
37+
],
38+
options={
39+
'ordering': ('-history_date', '-history_id'),
40+
'get_latest_by': 'history_date',
41+
'verbose_name': 'historical person',
42+
},
43+
),
44+
migrations.RemoveField(
45+
model_name='personhistory',
46+
name='person',
47+
),
48+
migrations.RemoveField(
49+
model_name='personhistory',
50+
name='user',
51+
),
52+
migrations.DeleteModel(
53+
name='PersonHistory',
54+
),
55+
]

ietf/person/models.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from django.contrib.auth.models import User
1717
from django.template.loader import render_to_string
1818
from django.utils.text import slugify
19+
from simple_history.models import HistoricalRecords
1920

2021
import debug # pyflakes:ignore
2122

@@ -27,7 +28,9 @@
2728
from ietf.utils.models import ForeignKey, OneToOneField
2829

2930

30-
class PersonInfo(models.Model):
31+
class Person(models.Model):
32+
history = HistoricalRecords()
33+
user = OneToOneField(User, blank=True, null=True)
3134
time = models.DateTimeField(default=datetime.datetime.now) # When this Person record entered the system
3235
# The normal unicode form of the name. This must be
3336
# set to the same value as the ascii-form if equal.
@@ -143,24 +146,21 @@ def photo_name(self,thumb=False):
143146
def has_drafts(self):
144147
from ietf.doc.models import Document
145148
return Document.objects.filter(documentauthor__person=self, type='draft').exists()
149+
146150
def rfcs(self):
147151
from ietf.doc.models import Document
148152
rfcs = list(Document.objects.filter(documentauthor__person=self, type='draft', states__slug='rfc'))
149153
rfcs.sort(key=lambda d: d.canonical_name() )
150154
return rfcs
155+
151156
def active_drafts(self):
152157
from ietf.doc.models import Document
153158
return Document.objects.filter(documentauthor__person=self, type='draft', states__slug='active').order_by('-time')
159+
154160
def expired_drafts(self):
155161
from ietf.doc.models import Document
156162
return Document.objects.filter(documentauthor__person=self, type='draft', states__slug__in=['repl', 'expired', 'auth-rm', 'ietf-rm']).order_by('-time')
157163

158-
class Meta:
159-
abstract = True
160-
161-
class Person(PersonInfo):
162-
user = OneToOneField(User, blank=True, null=True)
163-
164164
def save(self, *args, **kwargs):
165165
created = not self.pk
166166
super(Person, self).save(*args, **kwargs)
@@ -198,10 +198,6 @@ def json_dict(self, hostscheme):
198198
ct1['affiliation']= self.affiliation
199199
return ct1
200200

201-
class PersonHistory(PersonInfo):
202-
person = ForeignKey(Person, related_name="history_set")
203-
user = ForeignKey(User, blank=True, null=True)
204-
205201
class Alias(models.Model):
206202
"""This is used for alternative forms of a name. This is the
207203
primary lookup point for names, and should always contain the

ietf/person/resources.py

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from ietf import api
88

9-
from ietf.person.models import (Person, Email, Alias, PersonHistory, PersonalApiKey, PersonEvent, PersonApiKeyEvent)
9+
from ietf.person.models import (Person, Email, Alias, PersonalApiKey, PersonEvent, PersonApiKeyEvent, HistoricalPerson)
1010

1111

1212
from ietf.utils.resources import UserResource
@@ -24,7 +24,6 @@ class Meta:
2424
"name": ALL,
2525
"ascii": ALL,
2626
"ascii_short": ALL,
27-
"address": ALL,
2827
"affiliation": ALL,
2928
"photo": ALL,
3029
"biography": ALL,
@@ -61,29 +60,6 @@ class Meta:
6160
}
6261
api.person.register(AliasResource())
6362

64-
from ietf.utils.resources import UserResource
65-
class PersonHistoryResource(ModelResource):
66-
person = ToOneField(PersonResource, 'person')
67-
user = ToOneField(UserResource, 'user', null=True)
68-
class Meta:
69-
cache = SimpleCache()
70-
queryset = PersonHistory.objects.all()
71-
serializer = api.Serializer()
72-
#resource_name = 'personhistory'
73-
filtering = {
74-
"id": ALL,
75-
"time": ALL,
76-
"name": ALL,
77-
"ascii": ALL,
78-
"ascii_short": ALL,
79-
"address": ALL,
80-
"affiliation": ALL,
81-
"person": ALL_WITH_RELATIONS,
82-
"user": ALL_WITH_RELATIONS,
83-
}
84-
api.person.register(PersonHistoryResource())
85-
86-
8763
class PersonalApiKeyResource(ModelResource):
8864
person = ToOneField(PersonResource, 'person')
8965
class Meta:
@@ -141,3 +117,32 @@ class Meta:
141117
"key": ALL_WITH_RELATIONS,
142118
}
143119
api.person.register(PersonApiKeyEventResource())
120+
121+
122+
from ietf.utils.resources import UserResource
123+
class HistoricalPersonResource(ModelResource):
124+
user = ToOneField(UserResource, 'user', null=True)
125+
history_user = ToOneField(UserResource, 'history_user', null=True)
126+
class Meta:
127+
queryset = HistoricalPerson.objects.all()
128+
serializer = api.Serializer()
129+
cache = SimpleCache()
130+
#resource_name = 'historicalperson'
131+
filtering = {
132+
"id": ALL,
133+
"time": ALL,
134+
"name": ALL,
135+
"ascii": ALL,
136+
"ascii_short": ALL,
137+
"affiliation": ALL,
138+
"biography": ALL,
139+
"photo": ALL,
140+
"photo_thumb": ALL,
141+
"history_id": ALL,
142+
"history_date": ALL,
143+
"history_change_reason": ALL,
144+
"history_type": ALL,
145+
"user": ALL_WITH_RELATIONS,
146+
"history_user": ALL_WITH_RELATIONS,
147+
}
148+
api.person.register(HistoricalPersonResource())

ietf/settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ def skip_unreadable_post(record):
349349
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
350350
'django.contrib.messages.middleware.MessageMiddleware',
351351
'django.middleware.http.ConditionalGetMiddleware',
352+
'simple_history.middleware.HistoryRequestMiddleware',
352353
# 'ietf.middleware.sql_log_middleware',
353354
'ietf.middleware.SMTPExceptionMiddleware',
354355
'ietf.middleware.Utf8ExceptionMiddleware',
@@ -385,6 +386,7 @@ def skip_unreadable_post(record):
385386
'django_password_strength',
386387
'djangobwr',
387388
'form_utils',
389+
'simple_history',
388390
'tastypie',
389391
'widget_tweaks',
390392
# IETF apps

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ django-bootstrap3>=8.2.1,<9.0.0
1616
django-formtools>=1.0 # instead of django.contrib.formtools in 1.8
1717
django-markup>=1.1
1818
django-password-strength>=1.2.1
19+
django-simple-history>=2.0
1920
django-tastypie>=0.13.2
2021
django-widget-tweaks>=1.3
2122
docutils>=0.12

0 commit comments

Comments
 (0)