Skip to content

Commit

Permalink
Merge pull request #36 from SADiLaR/feature/add-simple-history
Browse files Browse the repository at this point in the history
added django-simple-history to project
  • Loading branch information
daniel-gray-tangent authored Apr 29, 2024
2 parents e3b78f0 + 91e014b commit 835f688
Show file tree
Hide file tree
Showing 14 changed files with 289 additions and 28 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ About the project:
4. Run `make stop` to stop the docker container


### Plugins installed
#### Django Simple History




https://django-simple-history.readthedocs.io/en/latest/
2 changes: 2 additions & 0 deletions app/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"django.contrib.staticfiles",
"users",
"general",
"simple_history",
]
if DEBUG:
INSTALLED_APPS += [
Expand All @@ -57,6 +58,7 @@
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"simple_history.middleware.HistoryRequestMiddleware",
]

ROOT_URLCONF = "app.urls"
Expand Down
49 changes: 34 additions & 15 deletions app/general/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@

from django.contrib import admin
from django.forms import HiddenInput, ModelForm, fields_for_model
from simple_history.admin import SimpleHistoryAdmin

from .models import DocumentFile, Institution, Language, Project, Subject


class ProjectAdminInline(admin.TabularInline):
model = Project
extra = 0


class DocumentFileForm(ModelForm):
class Meta:
model = DocumentFile
Expand Down Expand Up @@ -47,22 +43,45 @@ def clean(self):
return cleaned_data


class DocumentFileAdmin(admin.ModelAdmin):
class DocumentFileAdmin(SimpleHistoryAdmin):
list_display = ["title", "license", "document_type", "available"]
ordering = [
"license",
]
ordering = ["license"]
search_fields = ["title", "license", "document_type"]

form = DocumentFileForm
history_list_display = ["title", "license", "document_type", "available"]


class SubjectAdmin(SimpleHistoryAdmin):
search_fields = ["name"]
list_display = ["name"]
history_list_display = ["name"]


class LanguageAdmin(SimpleHistoryAdmin):
history_list_display = ["name", "iso_code"]
list_display = ["name", "iso_code"]


class ProjectAdminInline(admin.TabularInline):
model = Project
extra = 0


class ProjectAdmin(SimpleHistoryAdmin):
search_fields = ["name"]
list_display = ["name"]
history_list_display = ["name"]


class ProjectAdmin(admin.ModelAdmin):
class InstitutionAdmin(SimpleHistoryAdmin):
search_fields = ["name"]
list_display = ["name"]
inlines = [ProjectAdminInline]
history_list_display = ["name", "abbreviation"]


admin.site.register(Project)
admin.site.register(Institution, ProjectAdmin)
admin.site.register(Language)
admin.site.register(Subject)
admin.site.register(Project, ProjectAdmin)
admin.site.register(Institution, InstitutionAdmin)
admin.site.register(Language, LanguageAdmin)
admin.site.register(Subject, SubjectAdmin)
admin.site.register(DocumentFile, DocumentFileAdmin)
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import django.core.validators
import django.db.models.deletion
import simple_history.models
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('general', '0003_rename_institution_documentfile_institution_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='HistoricalDocumentFile',
fields=[
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('url', models.URLField(blank=True, verbose_name='URL')),
('uploaded_file', models.TextField(blank=True, help_text='PDF files up to 10MB are allowed.', max_length=100, validators=[django.core.validators.FileExtensionValidator(['pdf'])])),
('available', models.BooleanField(default=True)),
('license', models.CharField(choices=[('(c)', 'All rights reserved'), ('CC0', 'No rights reserved'), ('CC BY', 'Creative Commons Attribution'), ('CC BY-SA', 'Creative Commons Attribution-ShareAlike'), ('CC BY-NC', 'Creative Commons Attribution-NonCommercial'), ('CC BY-NC-SA', 'Creative Commons Attribution-NonCommercial-ShareAlike')], default='(c)', help_text='\n <a\n href="https://creativecommons.org/share-your-work/cclicenses/"\n rel="noreferrer"\n target="_blank"\n >\n More information about Creative Commons licenses.\n </a>\'\n ', max_length=200)),
('mime_type', models.CharField(blank=True, help_text='This input will auto-populate.', max_length=200)),
('document_type', models.CharField(choices=[('Glossary', 'Glossary'), ('Policy', 'Policy')], max_length=200)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField(db_index=True)),
('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)),
('institution', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='general.institution')),
],
options={
'verbose_name': 'historical document file',
'verbose_name_plural': 'historical document files',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': ('history_date', 'history_id'),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name='HistoricalInstitution',
fields=[
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('name', models.CharField(db_index=True, max_length=200)),
('abbreviation', models.CharField(max_length=200)),
('url', models.URLField(blank=True, verbose_name='URL')),
('email', models.EmailField(blank=True, max_length=200)),
('logo', models.TextField(blank=True, max_length=100)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField(db_index=True)),
('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)),
],
options={
'verbose_name': 'historical institution',
'verbose_name_plural': 'historical institutions',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': ('history_date', 'history_id'),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name='HistoricalLanguage',
fields=[
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('name', models.CharField(db_index=True, max_length=150)),
('iso_code', models.CharField(db_index=True, help_text='The 2 or 3 letter code from ISO 639.', max_length=50, verbose_name='ISO code')),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField(db_index=True)),
('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)),
],
options={
'verbose_name': 'historical language',
'verbose_name_plural': 'historical languages',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': ('history_date', 'history_id'),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name='HistoricalProject',
fields=[
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('url', models.URLField(blank=True, verbose_name='URL')),
('logo', models.TextField(blank=True, max_length=100)),
('start_date', models.DateField(blank=True, null=True)),
('end_date', models.DateField(blank=True, null=True)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField(db_index=True)),
('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)),
('institution', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='general.institution', verbose_name='institution')),
],
options={
'verbose_name': 'historical project',
'verbose_name_plural': 'historical projects',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': ('history_date', 'history_id'),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name='HistoricalSubject',
fields=[
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('name', models.CharField(db_index=True, max_length=150)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField(db_index=True)),
('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)),
],
options={
'verbose_name': 'historical subject',
'verbose_name_plural': 'historical subjects',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': ('history_date', 'history_id'),
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
]
16 changes: 16 additions & 0 deletions app/general/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.core.validators import FileExtensionValidator
from django.db import models
from simple_history.models import HistoricalRecords


class Project(models.Model):
Expand All @@ -14,6 +15,9 @@ class Project(models.Model):
subjects = models.ManyToManyField("Subject", blank=True)
languages = models.ManyToManyField("Language", blank=True)

# added simple historical records to the model
history = HistoricalRecords()

def __str__(self):
return self.name

Expand All @@ -25,6 +29,9 @@ class Institution(models.Model):
email = models.EmailField(max_length=200, blank=True)
logo = models.FileField(upload_to="logos/", blank=True)

# added simple historical records to the model
history = HistoricalRecords()

def __str__(self):
return f"{self.name} ({self.abbreviation})"

Expand All @@ -38,13 +45,19 @@ class Language(models.Model):
verbose_name="ISO code",
)

# added simple historical records to the model
history = HistoricalRecords()

def __str__(self):
return self.name


class Subject(models.Model):
name = models.CharField(max_length=150, unique=True)

# added simple historical records to the model
history = HistoricalRecords()

def __str__(self):
return self.name

Expand Down Expand Up @@ -98,5 +111,8 @@ class DocumentFile(models.Model):
subjects = models.ManyToManyField("Subject", blank=True)
languages = models.ManyToManyField("Language", blank=True)

# added simple historical records to the model
history = HistoricalRecords()

def __str__(self):
return self.title
12 changes: 12 additions & 0 deletions app/general/tests/test_document_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ def test_document_str_representation(self): # Test __str__ method
def test_document_available_by_default(self): # Test default value
self.assertTrue(self.document.available)

def test_history_records_creation(self):
self.assertEqual(self.document.history.count(), 1)
self.assertEqual(self.document.history.first().title, "Some document")
self.assertEqual(self.document.history.first().url, "https://example.com")
self.assertEqual(self.document.history.first().uploaded_file, "documents/example.pdf")
self.assertEqual(self.document.history.first().license, "MIT")
self.assertEqual(self.document.history.first().mime_type, "pdf")
self.assertEqual(self.document.history.first().document_type, "Glossary")
self.assertEqual(self.document.institution, self.institution)
self.assertIn(self.subject, self.document.subjects.all())
self.assertIn(self.language, self.document.languages.all())

def tearDown(self):
if self.document.uploaded_file:
self.document.uploaded_file.delete()
Expand Down
10 changes: 9 additions & 1 deletion app/general/tests/tests_institution.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from django.test import TestCase

from general.models import Institution, Project
from general.models import Institution


class TestInstitution(TestCase):
Expand Down Expand Up @@ -34,6 +34,14 @@ def test_institution_email(self):
def test_institution_logo(self):
self.assertEqual(self.institution.logo, "testuni.png")

def test_history_records_creation(self):
self.assertEqual(self.institution.history.count(), 1)
self.assertEqual(self.institution.history.first().name, "Test University")
self.assertEqual(self.institution.history.first().abbreviation, "tu")
self.assertEqual(self.institution.history.first().url, "http://www.testuni.com")
self.assertEqual(self.institution.history.first().email, "[email protected]")
self.assertEqual(self.institution.history.first().logo, "testuni.png")


if __name__ == "__main__":
unittest.main()
25 changes: 17 additions & 8 deletions app/general/tests/tests_language.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,30 @@

from django.test import TestCase

from general.models import Subject
from general.models import Language


class TestSubject(TestCase):
class TestLanguage(TestCase):
def setUp(self):
self.subject = Subject.objects.create(name="Maths")
self.subject2 = Subject.objects.create(name="Science")
self.language = Language.objects.create(name="English", iso_code="EN")
self.language2 = Language.objects.create(name="Afrikaans", iso_code="AF")

def test_subject_creation(self):
self.assertEqual(self.subject.name, "Maths")
self.assertEqual(self.subject.__str__(), "Maths")
self.assertEqual(self.language.name, "English")
self.assertEqual(self.language.iso_code, "EN")

self.assertEqual(self.language2.name, "Afrikaans")
self.assertEqual(self.language2.iso_code, "AF")

def test_subject_name_uniqueness(self):
duplicate_subject = Subject(name="Maths")
self.assertRaises(Exception, duplicate_subject.save)
with self.assertRaises(Exception):
Language.objects.create(name="English")

#
def test_history_records_creation(self):
self.assertEqual(self.language.history.count(), 1)
self.assertEqual(self.language.history.first().name, "English")
self.assertEqual(self.language.history.first().iso_code, "EN")


if __name__ == "__main__":
Expand Down
9 changes: 9 additions & 0 deletions app/general/tests/tests_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ def test_project_language(self):
def test_str(self):
self.assertEqual(str(self.project), "Test Project")

def test_history_records_creation(self):
self.assertEqual(self.project.history.count(), 1)
self.assertEqual(self.project.history.first().name, "Test Project")
self.assertEqual(self.project.history.first().url, "http://test.com")
self.assertEqual(self.project.history.first().logo, "http://test.com/logo.png")
self.assertEqual(self.project.history.first().start_date.strftime("%Y-%m-%d"), "2023-01-01")
self.assertEqual(self.project.history.first().end_date.strftime("%Y-%m-%d"), "2023-12-31")
self.assertEqual(self.project.history.first().institution, self.institution)


if __name__ == "__main__":
unittest.main()
4 changes: 4 additions & 0 deletions app/general/tests/tests_subject.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ def test_subject_name_uniqueness(self):
with self.assertRaises(Exception):
Subject.objects.create(name="Mathematics")

def test_history_records_creation(self):
self.assertEqual(self.subject1.history.count(), 1)
self.assertEqual(self.subject1.history.first().name, "Mathematics")


if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit 835f688

Please sign in to comment.