diff --git a/TEKDB/.coveragerc b/TEKDB/.coveragerc
index d19f2f43..766cf9b2 100644
--- a/TEKDB/.coveragerc
+++ b/TEKDB/.coveragerc
@@ -1,3 +1,4 @@
[run]
omit =
- */migrations/*
\ No newline at end of file
+ */migrations/*
+ */tests/*
\ No newline at end of file
diff --git a/TEKDB/TEKDB/admin.py b/TEKDB/TEKDB/admin.py
index 497cbc54..04c5bbb5 100644
--- a/TEKDB/TEKDB/admin.py
+++ b/TEKDB/TEKDB/admin.py
@@ -104,6 +104,10 @@ def __init__(self, *args, **kwargs):
class CitationsForm(forms.ModelForm):
+ class Meta:
+ model = Citations
+ fields = "__all__"
+
def __init__(self, *args, **kwargs):
super(CitationsForm, self).__init__(*args, **kwargs)
self.fields["referencetype"].queryset = LookupReferenceType.objects.order_by(
@@ -138,6 +142,10 @@ class Meta:
class ResourcesForm(forms.ModelForm):
+ class Meta:
+ model = Resources
+ fields = "__all__"
+
def __init__(self, *args, **kwargs):
super(ResourcesForm, self).__init__(*args, **kwargs)
self.fields[
@@ -632,7 +640,6 @@ def save_model(self, request, obj, form, change):
mediatype__startswith="other"
).first()
mediatype = media_type_instance
-
filename = file.name.split(".")[0]
media_instance = Media(
@@ -965,10 +972,10 @@ class PlacesAdmin(NestedRecordAdminProxy, RecordModelAdmin):
change_list_template = "admin/TEKDB/places/change_list.html"
def changelist_view(self, request, extra_context=None):
- from .views import getPlacesGeoJSON
+ from .views import get_places_geojson
extra_context = extra_context or {}
- extra_context["results_geojson"] = getPlacesGeoJSON(request)
+ extra_context["results_geojson"] = get_places_geojson(request)
return super(PlacesAdmin, self).changelist_view(
request, extra_context=extra_context
)
diff --git a/TEKDB/TEKDB/context_processors.py b/TEKDB/TEKDB/context_processors.py
index 80510962..ee0a3357 100644
--- a/TEKDB/TEKDB/context_processors.py
+++ b/TEKDB/TEKDB/context_processors.py
@@ -32,24 +32,20 @@ def search_settings(request=None):
pass
if configs:
- try:
- min_search_rank = (
- configs.min_search_rank
- if configs.min_search_rank
- else search_config["MIN_SEARCH_RANK"]
- )
- min_search_similarity = (
- configs.min_search_similarity
- if configs.min_search_similarity
- else search_config["MIN_SEARCH_SIMILARITY"]
- )
- search_config = {
- "MIN_SEARCH_RANK": min_search_rank,
- "MIN_SEARCH_SIMILARITY": min_search_similarity,
- }
- except Exception:
- print("No min_search_rank or min_search_similarity in Configuration")
- pass
+ min_search_rank = (
+ configs.min_search_rank
+ if configs.min_search_rank
+ else search_config["MIN_SEARCH_RANK"]
+ )
+ min_search_similarity = (
+ configs.min_search_similarity
+ if configs.min_search_similarity
+ else search_config["MIN_SEARCH_SIMILARITY"]
+ )
+ search_config = {
+ "MIN_SEARCH_RANK": min_search_rank,
+ "MIN_SEARCH_SIMILARITY": min_search_similarity,
+ }
return search_config
diff --git a/TEKDB/TEKDB/models.py b/TEKDB/TEKDB/models.py
index 3ccdb002..a4dd8d3a 100644
--- a/TEKDB/TEKDB/models.py
+++ b/TEKDB/TEKDB/models.py
@@ -71,6 +71,8 @@ def run_keyword_search(model, keyword, fields, fk_fields, weight_lookup, sort_fi
annotations[f"headline_{relationship_name}"] = SearchHeadline(
relationship_name, query, start_sel="", stop_sel=""
)
+ # vector is only false if we have no fields to look through.
+ # Thus far we don't have any models that only search through fk fields.
if not vector:
vector = SearchVector(relationship_name, weight=weight_lookup[val[0]])
else:
@@ -715,11 +717,6 @@ def get_response_format(self):
"feature": feature,
}
- # def get_record_dict(self, user, srid=3857):
- # record_dict = super(Places, self).get_record_dict(user, srid)
- # record_dict['map_pin'] = settings.RECORD_ICONS['map_pin']
- # return record_dict
-
def get_related_objects(self, object_id):
# place = Places.objects.get(pk=object_id)
alt_names = self.placealtindigenousname_set.all()
@@ -869,38 +866,6 @@ def keyword_search(
return run_keyword_search(
Resources, keyword, fields, fk_fields, weight_lookup, sort_field
)
- ################################
- # NEW APPROACH #################
- ################################
- # vector = SearchVector('commonname', weight='A') + \
- # SearchVector('indigenousname', weight='A') + \
- # SearchVector('resourceclassificationgroup__resourceclassificationgroup', weight='B') + \
- # SearchVector('genus', weight='C') + \
- # SearchVector('species', weight='C')
- # #HoW TO GET Alt Names?)
- # query = SearchQuery(keyword)
- # similarity=TrigramSimilarity('commonname', keyword, weight='A') + \
- # TrigramSimilarity('indigenousname', keyword, weight='A') + \
- # TrigramSimilarity('resourceclassificationgroup__resourceclassificationgroup', keyword, weight='B') + \
- # TrigramSimilarity('genus', keyword, weight='C') + \
- # TrigramSimilarity('species', keyword, weight='C')
-
- ################################
- # OLD APPROACH #################
- ################################
- # group_qs = LookupResourceGroup.objects.filter(resourceclassificationgroup__icontains=keyword)
- # group_loi = [group.pk for group in group_qs]
- # alt_name_qs = ResourceAltIndigenousName.objects.filter(altindigenousname__icontains=keyword)
- # alt_name_loi = [ran.resourceid.pk for ran in alt_name_qs]
- #
- # return Resources.objects.filter(
- # Q(commonname__icontains=keyword) |
- # Q(indigenousname__icontains=keyword) |
- # Q(genus__icontains=keyword) |
- # Q(species__icontains=keyword) |
- # Q(resourceclassificationgroup__in=group_loi) |
- # Q(pk__in=alt_name_loi)
- # )
def image(self):
return settings.RECORD_ICONS["resource"]
@@ -1179,10 +1144,8 @@ class Meta:
def keyword_search(keyword):
resource_qs = Resources.keyword_search(keyword)
resource_loi = [resource.pk for resource in resource_qs]
-
place_qs = Places.keyword_search(keyword)
place_loi = [place.pk for place in place_qs]
-
part_qs = LookupPartUsed.objects.filter(partused__icontains=keyword)
part_loi = [part.pk for part in part_qs]
@@ -1194,7 +1157,6 @@ def keyword_search(keyword):
timing_qs = LookupTiming.objects.filter(timing__icontains=keyword)
timing_loi = [timing.pk for timing in timing_qs]
-
return PlacesResourceEvents.objects.filter(
Q(resourceid__in=resource_loi)
| Q(placeid__in=place_loi)
@@ -1555,42 +1517,6 @@ def keyword_search(
sort_field,
)
- # def keyword_search(keyword):
- # placeresource_qs = PlacesResourceEvents.keyword_search(keyword)
- # placeresource_loi = [placeresource.pk for placeresource in placeresource_qs]
-
- # part_qs = LookupPartUsed.objects.filter(partused__icontains=keyword)
- # part_loi = [part.pk for part in part_qs]
-
- # activity_qs = LookupActivity.objects.filter(activity__icontains=keyword)
- # activity_loi = [activity.pk for activity in activity_qs]
-
- # participant_qs = LookupParticipants.objects.filter(participants__icontains=keyword)
- # participant_loi = [participant.pk for participant in participant_qs]
-
- # technique_qs = LookupTechniques.objects.filter(techniques__icontains=keyword)
- # technique_loi = [technique.pk for technique in technique_qs]
-
- # use_qs = LookupCustomaryUse.objects.filter(usedfor__icontains=keyword)
- # use_loi = [use.pk for use in use_qs]
-
- # timing_qs = LookupTiming.objects.filter(timing__icontains=keyword)
- # timing_loi = [timing.pk for timing in timing_qs]
-
- # return ResourcesActivityEvents.objects.filter(
- # Q(placeresourceid__in=placeresource_loi) |
- # Q(relationshipdescription__icontains=keyword) |
- # Q(partused__in=part_loi) |
- # Q(activityshortdescription__in=activity_loi) |
- # Q(activitylongdescription__icontains=keyword) |
- # Q(participants__in=participant_loi) |
- # Q(technique__in=technique_loi) |
- # Q(gear__icontains=keyword) |
- # Q(customaryuse__in=use_loi) |
- # Q(timing__in=timing_loi) |
- # Q(timingdescription__icontains=keyword)
- # )
-
def image(self):
return settings.RECORD_ICONS["activity"]
@@ -1776,7 +1702,7 @@ def relationships(self):
relationship_list = []
interviewee_citations = [x.get_query_json() for x in self.interviewee.all()]
interviewer_citations = [x.get_query_json() for x in self.interviewer.all()]
- # citations = list(set(interviewee_citations) | set(interviewer_citations))
+
if len(interviewee_citations) > 0:
relationship_list.append(
{"key": "Sources as interviewee", "value": interviewee_citations}
diff --git a/TEKDB/TEKDB/tests/test_admin.py b/TEKDB/TEKDB/tests/test_admin.py
index a5de7147..8a713b86 100644
--- a/TEKDB/TEKDB/tests/test_admin.py
+++ b/TEKDB/TEKDB/tests/test_admin.py
@@ -1,11 +1,11 @@
# from django.conf import settings
from django.test import RequestFactory
+from unittest.mock import patch
from django.core.files.uploadedfile import SimpleUploadedFile
from django.contrib.auth import get_user_model
from django.contrib.admin.sites import AdminSite
from django.urls import reverse
import os
-from TEKDB.admin import MediaBulkUploadAdmin
from TEKDB.forms import MediaBulkUploadForm
from TEKDB.models import MediaBulkUpload, Media
from TEKDB.tests.test_models import ITKTestCase
@@ -13,6 +13,140 @@
User = get_user_model()
+class MediaFormAdminTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin1", password="password", email="admin1@example.com"
+ )
+
+ def test_media_admin_add(self):
+ from TEKDB.admin import MediaForm
+
+ form = MediaForm()
+
+ self.assertIn("mediatype", form.fields)
+ queryset = form.fields["mediatype"].queryset
+ self.assertTrue(queryset.exists())
+
+
+class ResourcesActivityEventsFormAdminTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin3", password="password", email="admin3@example.com"
+ )
+
+ def test_resources_activity_events_admin_add(self):
+ from TEKDB.admin import ResourcesActivityEventsForm
+
+ form = ResourcesActivityEventsForm()
+
+ self.assertIn("participants", form.fields)
+ queryset = form.fields["participants"].queryset
+ self.assertTrue(queryset.exists())
+
+ self.assertIn("technique", form.fields)
+ queryset = form.fields["technique"].queryset
+ self.assertTrue(queryset.exists())
+
+ self.assertIn("activityshortdescription", form.fields)
+ queryset = form.fields["activityshortdescription"].queryset
+ self.assertTrue(queryset.exists())
+
+
+class CitationsFormAdminTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin4", password="password", email="admin4@example.com"
+ )
+
+ def test_citations_admin_add(self):
+ from TEKDB.admin import CitationsForm
+
+ form = CitationsForm()
+
+ self.assertIn("referencetype", form.fields)
+ queryset = form.fields["referencetype"].queryset
+ self.assertTrue(queryset.exists())
+
+ self.assertIn("intervieweeid", form.fields)
+ queryset = form.fields["intervieweeid"].queryset
+ self.assertTrue(queryset.exists())
+
+ self.assertIn("interviewerid", form.fields)
+ queryset = form.fields["interviewerid"].queryset
+ self.assertTrue(queryset.exists())
+
+
+class PlacesFormAdminTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin5", password="password", email="admin5@example.com"
+ )
+
+ def test_places_admin_add(self):
+ from TEKDB.admin import PlacesForm
+
+ form = PlacesForm()
+
+ self.assertIn("planningunitid", form.fields)
+ queryset = form.fields["planningunitid"].queryset
+ self.assertTrue(queryset.exists())
+
+ self.assertIn("primaryhabitat", form.fields)
+ queryset = form.fields["primaryhabitat"].queryset
+ self.assertTrue(queryset.exists())
+
+ self.assertIn("tribeid", form.fields)
+ queryset = form.fields["tribeid"].queryset
+ self.assertTrue(queryset.exists())
+
+
+class ResourcesFormAdminTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin6", password="password", email="admin6@example.com"
+ )
+
+ def test_resources_admin_add(self):
+ from TEKDB.admin import ResourcesForm
+
+ form = ResourcesForm()
+
+ self.assertIn("resourceclassificationgroup", form.fields)
+ queryset = form.fields["resourceclassificationgroup"].queryset
+ self.assertTrue(queryset.exists())
+
+
+class PlacesResourceEventFormAdminTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin7", password="password", email="admin7@example.com"
+ )
+
+ def test_places_resource_event_admin_add(self):
+ from TEKDB.admin import PlacesResourceEventForm
+
+ form = PlacesResourceEventForm()
+
+ self.assertIn("partused", form.fields)
+ queryset = form.fields["partused"].queryset
+ self.assertTrue(queryset.exists())
+
+ self.assertIn("season", form.fields)
+ queryset = form.fields["season"].queryset
+ self.assertTrue(queryset.exists())
+
+ self.assertIn("timing", form.fields)
+ queryset = form.fields["timing"].queryset
+ self.assertTrue(queryset.exists())
+
+
class MediaBulkUploadAdminTest(ITKTestCase):
def setUp(self):
self.factory = RequestFactory()
@@ -21,21 +155,89 @@ def setUp(self):
)
def test_media_bulk_upload_admin_add(self):
+ from TEKDB.models import (
+ Places,
+ Resources,
+ Citations,
+ ResourcesActivityEvents,
+ PlacesResourceEvents,
+ )
+ from TEKDB.admin import MediaBulkUploadAdmin
+
url = reverse("admin:TEKDB_mediabulkupload_add")
test_image = SimpleUploadedFile(
"./test_image.jpg", b"\x00\x00\x00\x00", content_type="image"
)
- # TODO: Associate the images with 1+ Places, Resources, Citations, Activities, and PlacesResources
+ place = Places.objects.create(indigenousplacename="Test Place")
+ resource = Resources.objects.create(commonname="Test Resource")
+ citation = Citations.objects.create(
+ referencetext="Test Citation", referencetype_id=1
+ )
+ placeresource = PlacesResourceEvents.objects.create(
+ placeid=place, resourceid=resource
+ )
+ activity = ResourcesActivityEvents.objects.create(placeresourceid=placeresource)
+
+ post_data = {
+ "files": [test_image, test_image],
+ "places": [place.pk],
+ "resources": [resource.pk],
+ "citations": [citation.pk],
+ "activities": [activity.pk],
+ "placesresources": [placeresource.pk],
+ }
+
+ request = self.factory.post(url, post_data)
+ request.user = self.user
+ bulk_admin = MediaBulkUploadAdmin(model=MediaBulkUpload, admin_site=AdminSite())
+ bulk_form = MediaBulkUploadForm(request.POST)
+ bulk_form.is_valid()
+ bulk_admin.save_model(
+ obj=MediaBulkUpload(), request=request, form=bulk_form, change=None
+ )
+
+ self.assertTrue(Media.objects.filter(medianame="test_image").exists())
+ self.assertEqual(Media.objects.filter(medianame="test_image").count(), 2)
+
+ self.assertTrue(Places.objects.filter(pk=place.pk).exists())
+ self.assertTrue(Resources.objects.filter(pk=resource.pk).exists())
+ self.assertTrue(Citations.objects.filter(pk=citation.pk).exists())
+ self.assertTrue(ResourcesActivityEvents.objects.filter(pk=activity.pk).exists())
+ self.assertTrue(
+ PlacesResourceEvents.objects.filter(pk=placeresource.pk).exists()
+ )
+
+ for media in Media.objects.filter(medianame="test_image"):
+ self.assertTrue(os.path.exists(media.mediafile.path))
+ os.remove(
+ media.mediafile.path
+ ) # Clean up the uploaded files after the test
+ self.assertFalse(os.path.exists(media.mediafile.path))
+ media.delete()
+
+ # Clean up related objects
+ activity.delete()
+ placeresource.delete()
+ citation.delete()
+ resource.delete()
+ place.delete()
+
+ def test_media_bulk_upload_admin_other_types(self):
+ from TEKDB.admin import MediaBulkUploadAdmin
+
+ url = reverse("admin:TEKDB_mediabulkupload_add")
+ test_other_type = SimpleUploadedFile(
+ "./test_thing.shp", b"\x00\x00\x00\x00", content_type="other"
+ )
request = self.factory.post(
url,
{
- # 'mediabulkname': 'Test Bulk Upload',
- # 'mediabulkdate': '2024-12-12',
- "files": [test_image, test_image],
+ "files": [test_other_type, test_other_type],
},
)
+
request.user = self.user
bulk_admin = MediaBulkUploadAdmin(model=MediaBulkUpload, admin_site=AdminSite())
bulk_form = MediaBulkUploadForm(request.POST)
@@ -44,10 +246,7 @@ def test_media_bulk_upload_admin_add(self):
obj=MediaBulkUpload(), request=request, form=bulk_form, change=None
)
- self.assertTrue(Media.objects.filter(medianame="test_image").exists())
- self.assertTrue(Media.objects.filter(medianame="test_image").count() == 2)
-
- for media in Media.objects.filter(medianame="test_image"):
+ for media in Media.objects.filter(medianame="test_thing"):
self.assertTrue(os.path.exists(media.mediafile.path))
os.remove(
media.mediafile.path
@@ -55,21 +254,355 @@ def test_media_bulk_upload_admin_add(self):
self.assertFalse(os.path.exists(media.mediafile.path))
media.delete()
- ### We don't need to test the admin change and delete views for now
- # def test_media_bulk_upload_admin_change(self):
- # media_bulk_upload = MediaBulkUpload.objects.create(mediabulkname='Test Bulk Upload', mediabulkdate='2023-10-01')
- # url = reverse('admin:TEKDB_mediabulkupload_change', args=[media_bulk_upload.id])
- # response = self.client.post(url, {
- # 'mediabulkname': 'Updated Bulk Upload',
- # 'mediabulkdate': '2023-10-01',
- # })
- # self.assertEqual(response.status_code, 302)
- # media_bulk_upload.refresh_from_db()
- # self.assertEqual(media_bulk_upload.mediabulkname, 'Updated Bulk Upload')
-
- # def test_media_bulk_upload_admin_delete(self):
- # media_bulk_upload = MediaBulkUpload.objects.create(mediabulkname='Test Bulk Upload', mediabulkdate='2023-10-01')
- # url = reverse('admin:TEKDB_mediabulkupload_delete', args=[media_bulk_upload.id])
- # response = self.client.post(url, {'post': 'yes'})
- # self.assertEqual(response.status_code, 302)
- # self.assertFalse(MediaBulkUpload.objects.filter(id=media_bulk_upload.id).exists())
+ def test_media_bulk_upload_admin_thumbnail_gallery(self):
+ from TEKDB.admin import MediaBulkUploadAdmin
+ from TEKDB.models import MediaBulkUpload, Media
+
+ url = reverse("admin:TEKDB_mediabulkupload_add")
+ test_image = SimpleUploadedFile(
+ "./thumbnail_test_image.jpg", b"\x00\x00\x00\x00", content_type="image"
+ )
+ test_video = SimpleUploadedFile(
+ "./thumbnail_test_video.mp4", b"\x00\x00\x00\x00", content_type="video"
+ )
+ test_audio = SimpleUploadedFile(
+ "./thumbnail_test_audio.mp3", b"\x00\x00\x00\x00", content_type="audio"
+ )
+ test_text = SimpleUploadedFile(
+ "./thumbnail_test_text.txt", b"\x00\x00\x00\x00", content_type="text"
+ )
+ test_other = SimpleUploadedFile(
+ "./thumbnail_test_thing.shp", b"\x00\x00\x00\x00", content_type="other"
+ )
+ test_unknown_type = SimpleUploadedFile(
+ "./thumbnail_test_unknown.xyz", b"\x00\x00\x00\x00", content_type="unknown"
+ )
+
+ post_data = {
+ "files": [
+ test_image,
+ test_video,
+ test_audio,
+ test_text,
+ test_other,
+ test_unknown_type,
+ ],
+ }
+
+ request = self.factory.post(url, post_data)
+ request.user = self.user
+ bulk_admin = MediaBulkUploadAdmin(model=MediaBulkUpload, admin_site=AdminSite())
+ bulk_form = MediaBulkUploadForm(request.POST)
+ bulk_form.is_valid()
+ bulk_upload_obj = MediaBulkUpload()
+ bulk_admin.save_model(
+ obj=bulk_upload_obj, request=request, form=bulk_form, change=None
+ )
+
+ html = bulk_admin.thumbnail_gallery(bulk_upload_obj)
+
+ self.assertIn("test_image", html)
+ self.assertIn("img", html)
+ self.assertIn("test_video", html)
+ self.assertIn("Your browser does not support the video tag.", html)
+ self.assertIn("test_audio", html)
+ self.assertIn("audio-x-generic.svg", html)
+ self.assertIn("test_text", html)
+ self.assertIn("doc-text.svg", html)
+ self.assertIn("test_thing", html)
+ self.assertIn("unknown-mail.png", html)
+ self.assertIn("test_unknown", html)
+
+ # Clean up
+ for media in Media.objects.filter(medianame__icontains="thumbnail_test"):
+ if hasattr(media, "mediafile") and media.mediafile:
+ if os.path.exists(media.mediafile.path):
+ os.remove(media.mediafile.path)
+ media.delete()
+
+ def test_media_bulk_upload_admin_get_readonly_fields(self):
+ from TEKDB.models import MediaBulkUpload
+ from TEKDB.admin import MediaBulkUploadAdmin
+
+ bulk_admin = MediaBulkUploadAdmin(model=MediaBulkUpload, admin_site=AdminSite())
+ readonly_fields_obj_exists = bulk_admin.get_readonly_fields(
+ request=None, obj=MediaBulkUpload()
+ )
+ self.assertNotIn("thumbnail_gallery", readonly_fields_obj_exists)
+ self.assertIn("id", readonly_fields_obj_exists)
+
+ readonly_fields_obj_not_exists = bulk_admin.get_readonly_fields(
+ request=None, obj=None
+ )
+ self.assertIn("thumbnail_gallery", readonly_fields_obj_not_exists)
+ self.assertNotIn("id", readonly_fields_obj_not_exists)
+
+ def test_media_bulk_upload_admin_has_change_permission(self):
+ from TEKDB.models import MediaBulkUpload
+ from TEKDB.admin import MediaBulkUploadAdmin
+
+ bulk_admin = MediaBulkUploadAdmin(model=MediaBulkUpload, admin_site=AdminSite())
+ has_change_perm = bulk_admin.has_change_permission(
+ request=None, obj=MediaBulkUpload()
+ )
+ self.assertFalse(has_change_perm)
+
+ def test_media_bulk_upload_admin_has_delete_permission(self):
+ from TEKDB.models import MediaBulkUpload
+ from TEKDB.admin import MediaBulkUploadAdmin
+
+ bulk_admin = MediaBulkUploadAdmin(model=MediaBulkUpload, admin_site=AdminSite())
+ has_delete_perm = bulk_admin.has_delete_permission(
+ request=None, obj=MediaBulkUpload()
+ )
+ self.assertTrue(has_delete_perm)
+
+ def test_media_bulk_upload_admin_has_add_permission(self):
+ from TEKDB.models import MediaBulkUpload
+ from TEKDB.admin import MediaBulkUploadAdmin
+
+ bulk_admin = MediaBulkUploadAdmin(model=MediaBulkUpload, admin_site=AdminSite())
+ has_add_perm = bulk_admin.has_add_permission(request=None)
+ self.assertTrue(has_add_perm)
+
+
+class RecordAdminProxyTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin8", password="password", email="admin8@example.com"
+ )
+
+ def test_save_model_sets_enteredby_fields(self):
+ from TEKDB.admin import RecordAdminProxy
+ from TEKDB.models import Media
+ from django import forms
+
+ class MediaForm(forms.ModelForm):
+ class Meta:
+ model = Media
+ fields = "__all__"
+
+ # Create a minimal Media instance
+ media = Media.objects.create(medianame="test_media")
+ request = self.factory.post("/test/")
+ request.user = self.user
+ form = MediaForm(instance=media)
+ admin = RecordAdminProxy(Media, AdminSite())
+ admin.save_model(request, media, form, change=False)
+ self.assertEqual(media.enteredbyname, self.user.username)
+ self.assertEqual(media.enteredbytribe, getattr(self.user, "affiliation", None))
+ self.assertEqual(media.enteredbytitle, getattr(self.user, "title", None))
+ self.assertEqual(media.modifiedbyname, self.user.username)
+ self.assertEqual(media.modifiedbytribe, getattr(self.user, "affiliation", None))
+ self.assertEqual(media.modifiedbytitle, getattr(self.user, "title", None))
+ media.delete()
+
+ def test_save_formset_sets_enteredby_fields(self):
+ from TEKDB.admin import RecordAdminProxy
+ from TEKDB.models import Media
+ from django import forms
+
+ class MediaForm(forms.ModelForm):
+ class Meta:
+ model = Media
+ fields = "__all__"
+
+ media1 = Media.objects.create(medianame="test_media1")
+ media2 = Media.objects.create(medianame="test_media2")
+ request = self.factory.post("/test/")
+ request.user = self.user
+
+ class MediaFormset:
+ def save(self, commit):
+ return [media1, media2]
+
+ form = MediaForm()
+ formset = MediaFormset()
+ admin = RecordAdminProxy(Media, AdminSite())
+ admin.save_formset(request, form, formset, change=False)
+ for media in [media1, media2]:
+ self.assertEqual(media.enteredbyname, self.user.username)
+ self.assertEqual(
+ media.enteredbytribe, getattr(self.user, "affiliation", None)
+ )
+ self.assertEqual(media.enteredbytitle, getattr(self.user, "title", None))
+ self.assertEqual(media.modifiedbyname, self.user.username)
+ self.assertEqual(
+ media.modifiedbytribe, getattr(self.user, "affiliation", None)
+ )
+ self.assertEqual(media.modifiedbytitle, getattr(self.user, "title", None))
+ media1.delete()
+ media2.delete()
+
+
+class NestedRecordAdminProxyTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin9", password="password", email="admin9@example.com"
+ )
+
+ def test_save_model(self):
+ from TEKDB.admin import NestedRecordAdminProxy
+ from TEKDB.models import Places
+ from django import forms
+
+ class PlacesForm(forms.ModelForm):
+ class Meta:
+ model = Places
+ fields = "__all__"
+
+ place = Places.objects.create(indigenousplacename="test_place")
+ request = self.factory.post("/test/")
+ request.user = self.user
+ form = PlacesForm(instance=place)
+ admin = NestedRecordAdminProxy(Places, AdminSite())
+ admin.save_model(request, place, form, change=False)
+ self.assertEqual(place.enteredbyname, self.user.username)
+ self.assertEqual(place.enteredbytribe, getattr(self.user, "affiliation", None))
+ self.assertEqual(place.enteredbytitle, getattr(self.user, "title", None))
+ self.assertEqual(place.modifiedbyname, self.user.username)
+ self.assertEqual(place.modifiedbytribe, getattr(self.user, "affiliation", None))
+ self.assertEqual(place.modifiedbytitle, getattr(self.user, "title", None))
+ place.delete()
+
+ def test_save_formset(self):
+ from TEKDB.admin import NestedRecordAdminProxy
+ from TEKDB.models import Places
+ from django import forms
+
+ class PlacesForm(forms.ModelForm):
+ class Meta:
+ model = Places
+ fields = "__all__"
+
+ place1 = Places.objects.create(indigenousplacename="test_place1")
+ place2 = Places.objects.create(indigenousplacename="test_place2")
+ request = self.factory.post("/test/")
+ request.user = self.user
+
+ class PlacesFormset:
+ def save(self, commit):
+ return [place1, place2]
+
+ form = PlacesForm()
+ formset = PlacesFormset()
+ admin = NestedRecordAdminProxy(Places, AdminSite())
+ admin.save_formset(request, form, formset, change=False)
+ for place in [place1, place2]:
+ self.assertEqual(place.enteredbyname, self.user.username)
+ self.assertEqual(
+ place.enteredbytribe, getattr(self.user, "affiliation", None)
+ )
+ self.assertEqual(place.enteredbytitle, getattr(self.user, "title", None))
+ self.assertEqual(place.modifiedbyname, self.user.username)
+ self.assertEqual(
+ place.modifiedbytribe, getattr(self.user, "affiliation", None)
+ )
+ self.assertEqual(place.modifiedbytitle, getattr(self.user, "title", None))
+ place1.delete()
+ place2.delete()
+
+ def test_needs_review_true(self):
+ from TEKDB.admin import NestedRecordAdminProxy
+ from TEKDB.models import Places
+
+ place = Places.objects.create(indigenousplacename="test_place")
+ request = self.factory.post("/test/")
+ request.user = self.user
+
+ admin = NestedRecordAdminProxy(Places, AdminSite())
+ result_true = admin.needs_Review(place)
+ self.assertIn("icon-alert.svg", result_true)
+ place.delete()
+
+ def test_needs_review_false(self):
+ from TEKDB.admin import NestedRecordAdminProxy
+ from TEKDB.models import Places
+
+ place = Places.objects.create(
+ indigenousplacename="test_place", needsReview=False
+ )
+ request = self.factory.post("/test/")
+ request.user = self.user
+
+ admin = NestedRecordAdminProxy(Places, AdminSite())
+ result_false = admin.needs_Review(place)
+ self.assertIn("", result_false)
+ place.delete()
+
+
+class RecordModelAdminTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin10", password="password", email="admin10@example.com"
+ )
+
+ def test_needs_review_true(self):
+ from TEKDB.admin import RecordModelAdmin
+ from TEKDB.models import Places
+
+ place = Places.objects.create(indigenousplacename="test_place")
+ request = self.factory.post("/test/")
+ request.user = self.user
+
+ admin = RecordModelAdmin(Places, AdminSite())
+ result_true = admin.needs_Review(place)
+ self.assertIn("icon-alert.svg", result_true)
+ place.delete()
+
+ def test_needs_review_false(self):
+ from TEKDB.admin import RecordModelAdmin
+ from TEKDB.models import Places
+
+ place = Places.objects.create(
+ indigenousplacename="test_place", needsReview=False
+ )
+ request = self.factory.post("/test/")
+ request.user = self.user
+
+ admin = RecordModelAdmin(Places, AdminSite())
+ result_false = admin.needs_Review(place)
+ self.assertIn("", result_false)
+ place.delete()
+
+ def test_change_view(self):
+ from TEKDB.admin import RecordModelAdmin
+ from TEKDB.models import Places
+
+ place = Places.objects.create(indigenousplacename="test_place")
+ url = reverse("admin:TEKDB_places_change", args=[place.placeid])
+ request = self.factory.get(url)
+ request.user = self.user
+
+ admin = RecordModelAdmin(Places, AdminSite())
+ with patch.object(RecordModelAdmin, "has_change_permission", return_value=True):
+ response = admin.change_view(request, str(place.placeid))
+ self.assertEqual(response.status_code, 200)
+ place.delete()
+
+
+class PlacesAdminTest(ITKTestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+ self.user = User.objects.create_superuser(
+ username="admin11", password="password", email="admin11@example.com"
+ )
+
+ def test_places_admin_changelist_view(self):
+ from TEKDB.admin import PlacesAdmin
+ from TEKDB.models import Places
+
+ place = Places.objects.create(indigenousplacename="test_place")
+ url = reverse("admin:TEKDB_places_changelist")
+ request = self.factory.get(url)
+ request.user = self.user
+
+ admin = PlacesAdmin(Places, AdminSite())
+ with patch.object(PlacesAdmin, "has_change_permission", return_value=True):
+ response = admin.changelist_view(request)
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("results_geojson", response.context_data)
+ place.delete()
diff --git a/TEKDB/TEKDB/tests/test_context_processors.py b/TEKDB/TEKDB/tests/test_context_processors.py
new file mode 100644
index 00000000..eb36d925
--- /dev/null
+++ b/TEKDB/TEKDB/tests/test_context_processors.py
@@ -0,0 +1,119 @@
+from django.test import TestCase, RequestFactory, override_settings
+from TEKDB.context_processors import search_settings
+
+
+class TestSearchSettingsContextProcessor(TestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+
+ def test_search_settings_no_settings(self):
+ from unittest import mock
+ import builtins
+
+ request = self.factory.get("/")
+
+ real_import = builtins.__import__
+
+ def mock_import(name, globals=None, locals=None, fromlist=(), level=0):
+ if (
+ name == "django.conf"
+ or name.startswith("django.conf")
+ or name == "TEKDB"
+ ):
+ raise ImportError("Mock missing settings module")
+ return real_import(name, globals, locals, fromlist, level)
+
+ with mock.patch("builtins.__import__", side_effect=mock_import):
+ context = search_settings(request)
+
+ self.assertIn("MIN_SEARCH_RANK", context)
+ self.assertIn("MIN_SEARCH_SIMILARITY", context)
+ self.assertEqual(context["MIN_SEARCH_RANK"], 0.01)
+ self.assertEqual(context["MIN_SEARCH_SIMILARITY"], 0.1)
+
+ @override_settings()
+ def test_search_settings_missing_in_settings(self):
+ from django.conf import settings
+
+ del settings.MIN_SEARCH_RANK
+ del settings.MIN_SEARCH_SIMILARITY
+ request = self.factory.get("/")
+ context = search_settings(request)
+ self.assertIn("MIN_SEARCH_RANK", context)
+ self.assertIn("MIN_SEARCH_SIMILARITY", context)
+ self.assertEqual(context["MIN_SEARCH_RANK"], 0.01) # Default
+ self.assertEqual(context["MIN_SEARCH_SIMILARITY"], 0.1) # Default
+
+ @override_settings(MIN_SEARCH_RANK=0.05, MIN_SEARCH_SIMILARITY=0.2)
+ def test_search_settings_from_settings(self):
+ request = self.factory.get("/")
+ context = search_settings(request)
+ self.assertIn("MIN_SEARCH_RANK", context)
+ self.assertIn("MIN_SEARCH_SIMILARITY", context)
+ self.assertEqual(context["MIN_SEARCH_RANK"], 0.05)
+ self.assertEqual(context["MIN_SEARCH_SIMILARITY"], 0.2)
+
+ def test_search_settings_configs_overrides_settings(self):
+ from configuration.models import Configuration
+
+ Configuration.objects.create(min_search_rank=0.03, min_search_similarity=0.15)
+
+ request = self.factory.get("/")
+ context = search_settings(request)
+ self.assertIn("MIN_SEARCH_RANK", context)
+ self.assertIn("MIN_SEARCH_SIMILARITY", context)
+ self.assertEqual(context["MIN_SEARCH_RANK"], 0.03)
+ self.assertEqual(context["MIN_SEARCH_SIMILARITY"], 0.15)
+
+ @override_settings(
+ MIN_SEARCH_RANK=0.05,
+ )
+ def test_search_settings_configs_missing_search_rank(self):
+ from configuration.models import Configuration
+
+ Configuration.objects.create(min_search_rank=None, min_search_similarity=0.2)
+
+ request = self.factory.get("/")
+ context = search_settings(request)
+ self.assertIn("MIN_SEARCH_RANK", context)
+ self.assertIn("MIN_SEARCH_SIMILARITY", context)
+ self.assertEqual(context["MIN_SEARCH_RANK"], 0.05) # From settings
+ self.assertEqual(context["MIN_SEARCH_SIMILARITY"], 0.2) # From config
+
+
+class TestAddMapDefaultContextProcessor(TestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+
+ @override_settings(
+ DATABASE_GEOGRAPHY={
+ "default_lon": 13839794,
+ "default_lat": 5171449,
+ "default_zoom": 5,
+ }
+ )
+ def test_add_map_default_context(self):
+ from TEKDB.context_processors import add_map_default_context
+
+ request = self.factory.get("/")
+ context = add_map_default_context(request)
+ self.assertIn("default_lon", context)
+ self.assertIn("default_lat", context)
+ self.assertIn("default_zoom", context)
+ self.assertEqual(context["default_lat"], 5171449)
+ self.assertEqual(context["default_lon"], 13839794)
+ self.assertEqual(context["default_zoom"], 5)
+
+ def test_add_map_default_context_override_settings_with_configs(self):
+ from configuration.models import Configuration
+ from django.contrib.gis.geos import Polygon
+ from TEKDB.context_processors import add_map_default_context
+
+ Configuration.objects.create(
+ geometry=Polygon.from_bbox([2000000, 1000000, 3000000, 2000000])
+ )
+
+ request = self.factory.get("/")
+ context = add_map_default_context(request)
+ self.assertIn("map_extent", context)
+ self.assertEqual(context["map_extent"], [2000000, 1000000, 3000000, 2000000])
diff --git a/TEKDB/TEKDB/tests/test_files/test_wrong_import.txt b/TEKDB/TEKDB/tests/test_files/test_wrong_import.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/TEKDB/TEKDB/tests/test_models.py b/TEKDB/TEKDB/tests/test_models.py
index 277eabe2..394ddab7 100644
--- a/TEKDB/TEKDB/tests/test_models.py
+++ b/TEKDB/TEKDB/tests/test_models.py
@@ -38,6 +38,8 @@
LookupTiming,
LookupTribe,
LookupUserInfo,
+ Locality,
+ Users,
)
# from .forms import *
@@ -148,6 +150,46 @@ def test_empty_string_search(self):
self.assertTrue(len(resultlist) == model.objects.count())
+ def test_all_search_results(self):
+ """
+ Test that a model_type of 'all' returns results from specific categories
+ """
+ categories = [
+ "resources",
+ "places",
+ "citations",
+ "media",
+ "resourcesactivityevents",
+ ]
+
+ from explore.views import get_model_by_type
+
+ query_models = get_model_by_type("all")
+ lowercase_query_model_names = [model.__name__.lower() for model in query_models]
+
+ for category in categories:
+ self.assertIn(category, lowercase_query_model_names)
+
+ def test_no_keyword_get_results(self):
+ """
+ Test that an empty string search returns all objects
+ """
+ keyword = None
+
+ from explore.views import get_results
+
+ search_results = get_results(
+ keyword,
+ categories=["resources"],
+ )
+ all_ranks = set([x["rank"] for x in search_results])
+ all_similarities = set([x["similarity"] for x in search_results])
+ all_headlines = set([x["headline"] for x in search_results])
+
+ self.assertEqual(all_ranks, {0})
+ self.assertEqual(all_similarities, {0})
+ self.assertEqual(all_headlines, {None})
+
def test_phrase_search(self):
"""
Test that a phrase search returns all objects that contain the phrase
@@ -317,6 +359,14 @@ def test_get_greatest_similarity_attribute_multiple_matches(self):
####################################################
+class RecordTest(ITKTestCase):
+ def test_get_related_objects(self):
+ from TEKDB.models import Record
+
+ related_objects = Record.get_related_objects(Record, 1)
+ self.assertEqual(len(related_objects), 0)
+
+
# Places
class PlacesTest(ITKSearchTest):
def test_placess(self):
@@ -392,6 +442,32 @@ def test_place_id_collision(self):
collision_result = test_model_id_collision(Places, insertion_object, self)
self.assertTrue(collision_result)
+ def test_relationships(self):
+ """
+ Test that place relationships are correctly identified
+ """
+ place = Places.objects.get(pk=21) # Place with all types of relationships
+ relationships = place.relationships()
+ self.assertEqual(len(relationships), 4)
+
+ def test_link(self):
+ """
+ Test that place link is correctly generated
+ """
+ place = Places.objects.get(pk=21)
+ link = place.link()
+ self.assertEqual(link, "/explore/places/21/")
+
+ def test_get_response_format_no_feature(self):
+ """
+ Test that get_response_format works when no feature is provided
+ """
+ place = Places.objects.get(pk=28) # Place with no geometry
+ response = place.get_response_format()
+ self.assertIn("id", response)
+ self.assertIn("feature", response)
+ self.assertIsNone(response["feature"])
+
# Resources
class ResourcesTest(ITKSearchTest):
@@ -489,6 +565,31 @@ def test_resource_id_collision(self):
collision_result = test_model_id_collision(Resources, insertion_object, self)
self.assertTrue(collision_result)
+ def test_subtitle(self):
+ """
+ Test that resource subtitle is correctly generated
+ """
+ resource = Resources.objects.get(pk=151) # abalone, Black
+ subtitle = resource.subtitle()
+ self.assertEqual(subtitle, "cracherdoii")
+
+ def test_relationships(self):
+ """
+ Test that resource relationships are correctly identified
+ """
+ ResourcesMediaEvents.objects.create(
+ **{
+ "resourceid": Resources.objects.get(pk=207),
+ "mediaid": Media.objects.get(pk=6),
+ }
+ )
+ resource = Resources.objects.get(
+ pk=207
+ ) # Resource with multiple types of relationships
+ relationships = resource.relationships()
+
+ self.assertEqual(len(relationships), 4)
+
# ResourcesActivityEvents ('Activities')
class ResourcesActivityEventsTest(ITKSearchTest):
@@ -528,6 +629,64 @@ def test_activity_id_collision(self):
)
self.assertTrue(collision_result)
+ def test_relationships(self):
+ """
+ Test that activity relationships are correctly returned
+ """
+ activity = ResourcesActivityEvents.objects.get(pk=31)
+ ResourceActivityMediaEvents.objects.create(
+ **{
+ "pk": 2,
+ "resourceactivityid": activity,
+ "mediaid": Media.objects.all()[0],
+ }
+ )
+ relationships = activity.relationships()
+
+ self.assertEqual(len(relationships), 3)
+
+ def test_data(self):
+ """
+ Test that activity data is correctly generated with all fields
+ """
+ activity = ResourcesActivityEvents.objects.create(
+ **{
+ "placeresourceid": PlacesResourceEvents.objects.all()[0],
+ "relationshipdescription": "This is a test activity.",
+ "partused": LookupPartUsed.objects.all()[0],
+ "activityshortdescription": LookupActivity.objects.all()[0],
+ "activitylongdescription": "This is a long description of the activity.",
+ "participants": LookupParticipants.objects.all()[0],
+ "technique": LookupTechniques.objects.all()[0],
+ "gear": "Test gear",
+ "customaryuse": "customary use",
+ "timing": LookupTiming.objects.all()[0],
+ "timingdescription": "This is a description of the timing.",
+ }
+ )
+ activity.save()
+ retrieved_activity = ResourcesActivityEvents.objects.get(pk=activity.pk)
+ data = retrieved_activity.data()
+
+ keys = [
+ "place",
+ "resource",
+ "excerpt",
+ "part used",
+ "activity type",
+ "full description",
+ "participants",
+ "technique",
+ "gear",
+ "customary use",
+ "timing",
+ "timing description",
+ ]
+
+ data_keys = [item["key"].lower() for item in data]
+ for key in keys:
+ self.assertIn(key, data_keys)
+
# Citations (Bibliographic 'Sources')
class CitationsTest(ITKSearchTest):
@@ -600,6 +759,72 @@ def test_citation_id_collision(self):
collision_result = test_model_id_collision(Citations, insertion_object, self)
self.assertTrue(collision_result)
+ def test_relationships(self):
+ """
+ Test that citation relationships are correctly identified
+ """
+ interviewee = People.objects.create(firstname="Test", lastname="Interviewee")
+ interviewer = People.objects.create(firstname="Test", lastname="Interviewer")
+ citation = Citations.objects.create(
+ referencetext="Test Citation for Relationships",
+ referencetype=LookupReferenceType.objects.get(
+ pk=3
+ ), # interview documenttype
+ intervieweeid=interviewee,
+ interviewerid=interviewer,
+ )
+
+ # create one of each relationship type
+ PlacesCitationEvents.objects.create(
+ placeid=Places.objects.first(), citationid=citation
+ )
+ ResourcesCitationEvents.objects.create(
+ resourceid=Resources.objects.first(), citationid=citation
+ )
+ MediaCitationEvents.objects.create(
+ mediaid=Media.objects.first(), citationid=citation
+ )
+ ResourceActivityCitationEvents.objects.create(
+ resourceactivityid=ResourcesActivityEvents.objects.first(),
+ citationid=citation,
+ )
+ PlacesResourceCitationEvents.objects.create(
+ placeresourceid=PlacesResourceEvents.objects.first(), citationid=citation
+ )
+ relationships = citation.relationships()
+ self.assertEqual(len(relationships), 6)
+
+ def test_title_text_interview(self):
+ """
+ Test that citation title_text is correctly generated for interview type
+ """
+ citation = Citations.objects.get(pk=12) # Citation with interview referencetype
+ title_text = citation.title_text
+ self.assertEqual(title_text, "Jay Tosanic: interviewed by Manahe Herman")
+
+ def test_title_text_non_interview(self):
+ """
+ Test that citation title_text is correctly generated
+ """
+ citation = Citations.objects.get(
+ pk=11
+ ) # Citation of non interview referencetype
+ title_text = citation.title_text
+ self.assertEqual(
+ title_text, "Traditional Marine Harvesting of Native Americans"
+ )
+
+ def test_description_text(self):
+ """
+ Test that citation description_text is correctly generated
+ """
+ citation = Citations.objects.get(
+ pk=11
+ ) # Citation of non interview referencetype
+ description_text = citation.description_text
+ expected_description = "Text on marine flora and fauna"
+ self.assertEqual(description_text, expected_description)
+
# Media
class MediaTest(ITKSearchTest):
@@ -1080,6 +1305,56 @@ def test_places_resource_get_query_json_no_map(self):
self.assertNotIn("map", query_json)
+ def test_keyword_search(self):
+ """
+ Test that keyword_search returns expected results
+ """
+ place = Places.objects.create(
+ indigenousplacename="Keyword Place",
+ englishplacename="Keyword Place English",
+ )
+ resource = Resources.objects.create(
+ commonname="Keyword Resource", indigenousname="Keyword Indigenous Resource"
+ )
+ timing = LookupTiming.objects.create(timing="Seasonal")
+ event = PlacesResourceEvents.objects.create(
+ placeid=place,
+ resourceid=resource,
+ timing=timing,
+ )
+ keyword = "keyword"
+ results = PlacesResourceEvents.keyword_search(keyword)
+ self.assertIn(event, results)
+
+ def test_relationships(self):
+ place = Places.objects.get(pk=19)
+ resource = Resources.objects.create(
+ commonname="Test Resource", indigenousname="Test Resource Indigenous"
+ )
+
+ citation = Citations.objects.create(
+ referencetype=LookupReferenceType.objects.get(pk=1),
+ referencetext="Test Citation",
+ )
+
+ event = PlacesResourceEvents.objects.create(
+ placeid=place,
+ resourceid=resource,
+ relationshipdescription="Test Relationship",
+ )
+ PlacesResourceCitationEvents.objects.create(
+ placeresourceid=event,
+ citationid=citation,
+ relationshipdescription="Test Place-Resource-Citation Relationship",
+ )
+ ResourcesActivityEvents.objects.create(
+ placeresourceid=event,
+ relationshipdescription="Test Activity",
+ )
+ relationships = event.relationships()
+
+ self.assertEqual(len(relationships), 4)
+
class PlacesResourceEventsCascadeTest(ITKTestCase):
# fixtures = ['/usr/local/apps/TEKDB/TEKDB/TEKDB/fixtures/all_dummy_data.json',]
@@ -1160,6 +1435,72 @@ def test_place_citation_relationship_id_collision(self):
)
self.assertTrue(collision_result)
+ def test_keyword_search(self):
+ """
+ Test that keyword_search returns expected results
+ """
+ place = Places.objects.create(
+ indigenousplacename="Keyword Place",
+ englishplacename="Keyword Place English",
+ )
+ citation = Citations.objects.create(
+ referencetext="Keyword Citation",
+ referencetype=LookupReferenceType.objects.create(documenttype="Book"),
+ )
+ event = PlacesCitationEvents.objects.create(
+ placeid=place,
+ citationid=citation,
+ relationshipdescription="Keyword Relationship",
+ )
+ keyword = "keyword"
+ results = PlacesCitationEvents.keyword_search(keyword)
+ self.assertIn(event, results)
+
+ def test_property_and_methods(self):
+ """
+ Test that description_text is correctly generated
+ """
+ place = Places.objects.create(
+ indigenousplacename="Description Place",
+ englishplacename="Description Place English",
+ )
+ citation = Citations.objects.create(
+ referencetext="Description Citation",
+ referencetype=LookupReferenceType.objects.create(documenttype="Book"),
+ )
+ relationship_description = "Description Relationship"
+ event = PlacesCitationEvents.objects.create(
+ placeid=place,
+ citationid=citation,
+ relationshipdescription=relationship_description,
+ )
+ description_text = event.description_text
+
+ self.assertEqual(description_text, relationship_description)
+ self.assertEqual(event.image(), settings.RECORD_ICONS["activity"])
+ self.assertEqual(event.subtitle(), event.relationshipdescription)
+ self.assertEqual(event.link(), f"/explore/placescitationevents/{event.pk}/")
+
+ def test_relationships(self):
+ place = Places.objects.create(
+ indigenousplacename="Relationship Place",
+ englishplacename="Relationship Place English",
+ )
+ citation = Citations.objects.create(
+ referencetext="Relationship Citation",
+ referencetype=LookupReferenceType.objects.create(documenttype="Book"),
+ )
+ event = PlacesCitationEvents.objects.create(
+ placeid=place,
+ citationid=citation,
+ relationshipdescription="Relationship Description",
+ )
+ relationships = event.relationships()
+
+ self.assertEqual(
+ len(relationships), 2
+ ) # should have place and citation relationships
+
class PlacesCitationEventsCascadeTest(ITKTestCase):
def setUp(self):
@@ -1517,6 +1858,119 @@ def test_people_id_collision(self):
collision_result = test_model_id_collision(People, insertion_object, self)
self.assertTrue(collision_result)
+ def test_image(self):
+ """
+ Test that the image is from the settings
+ """
+ person = People.objects.create(
+ firstname="Image",
+ lastname="Tester",
+ )
+ image_data = person.image()
+
+ self.assertEqual(settings.RECORD_ICONS["person"], image_data)
+
+ def test_link(self):
+ """
+ Test that the link is correctly generated
+ """
+ person = People.objects.create(
+ firstname="Link",
+ lastname="Tester",
+ )
+ link = person.link()
+
+ expected_link = f"/explore/people/{person.pk}"
+ self.assertEqual(expected_link, link)
+
+ def test_data(self):
+ """
+ Test that data method returns correct structure
+ """
+ person = People.objects.create(
+ firstname="John",
+ lastname="Doe",
+ yearborn=1980,
+ village="Sample Village",
+ relationshiptootherpeople="Sample Relationship",
+ )
+ data = person.data()
+
+ keys = [
+ "first name",
+ "last name",
+ "year born",
+ "village",
+ "relationship to others",
+ ]
+
+ for item in data:
+ key = item["key"]
+ val = item["value"]
+ self.assertIn(key, keys)
+ if key == "first name":
+ self.assertEqual(val, "John")
+ elif key == "last name":
+ self.assertEqual(val, "Doe")
+ elif key == "year born":
+ self.assertEqual(val, "1980")
+ elif key == "village":
+ self.assertEqual(val, "Sample Village")
+ elif key == "relationship to others":
+ self.assertEqual(val, "Sample Relationship")
+
+ def test_relationships(self):
+ """
+ Test that relationships method returns correct sources
+ """
+ person = People.objects.create(
+ firstname="John",
+ lastname="Doe",
+ )
+ Citations.objects.create(
+ referencetype=LookupReferenceType.objects.get(pk=1),
+ referencetext="Interview Citation",
+ intervieweeid=person,
+ )
+ Citations.objects.create(
+ referencetype=LookupReferenceType.objects.get(pk=1),
+ referencetext="Interviewer Citation",
+ interviewerid=person,
+ )
+
+ relationships = person.relationships()
+ self.assertEqual(len(relationships), 2)
+
+ def test_get_query_json(self):
+ """
+ Test that get_query_json method returns correct structure
+ """
+ person = People.objects.create(
+ firstname="John",
+ lastname="Doe",
+ )
+ query_json = person.get_query_json()
+
+ self.assertEqual(query_json["name"], "John Doe")
+ self.assertEqual(query_json["link"], f"/explore/people/{person.pk}")
+ self.assertEqual(query_json["image"], settings.RECORD_ICONS["person"])
+
+ def test_get_record_dict(self):
+ """
+ Test that get_record_dict method returns correct structure
+ """
+ person = People.objects.create(
+ firstname="John",
+ lastname="Doe",
+ village="Sample Village",
+ )
+ user = Users.objects.get(username="admin")
+ record_dict = person.get_record_dict(user=user)
+
+ self.assertEqual(record_dict["name"], "John Doe")
+ self.assertEqual(record_dict["subtitle"], "Sample Village")
+ self.assertFalse(record_dict["map"])
+
# LookupPlanningUnit
class LookupPlanningUnitTest(ITKTestCase):
@@ -1693,3 +2147,38 @@ def test_lookup_user_inf_id_collision(self):
####################################################
# Locality
+
+
+class LocalityTest(ITKTestCase):
+ def test_keyword_search(self):
+ """
+ Test that keyword_search returns expected results
+ """
+ place = Places.objects.create(
+ indigenousplacename="Keyword Place",
+ englishplacename="Keyword Place English",
+ )
+ locality = Locality.objects.create(
+ englishname="Keyword Locality",
+ placeid=place,
+ )
+ keyword = "keyword"
+ results = Locality.keyword_search(keyword)
+ self.assertIn(locality, results)
+
+ def test_methods_properties(self):
+ """
+ Test that methods and properties return expected results
+ """
+ place = Places.objects.create(
+ indigenousplacename="Place",
+ englishplacename="Place English",
+ )
+ locality = Locality.objects.create(
+ englishname="Locality",
+ indigenousname="Indigenous Locality",
+ placeid=place,
+ )
+ self.assertEqual(locality.image(), settings.RECORD_ICONS["place"])
+ self.assertEqual(locality.subtitle(), locality.indigenousname)
+ self.assertEqual(locality.link(), f"/explore/locality/{locality.pk}/")
diff --git a/TEKDB/TEKDB/tests/test_views.py b/TEKDB/TEKDB/tests/test_views.py
index 25a63112..47fbf429 100644
--- a/TEKDB/TEKDB/tests/test_views.py
+++ b/TEKDB/TEKDB/tests/test_views.py
@@ -474,52 +474,53 @@ def test_valid_admin_export(self):
self.assertEqual(response.cookies["export_status"].value, "done")
-class ImportTest(TransactionTestCase):
- # fixtures = ['TEKDB/fixtures/all_dummy_data.json',]
-
- @classmethod
- def setUp(cls):
+class ImportDatabaseTest(TransactionTestCase):
+ def setUp(self):
import_fixture_file(
join(settings.BASE_DIR, "TEKDB", "fixtures", "all_dummy_data.json")
)
+ self.factory = RequestFactory()
+ self.credentials = b64encode(b"admin:admin").decode("ascii")
+ self.dummy_1_name = "Dummy Record 1"
+ self.zipname = join(
+ settings.BASE_DIR, "TEKDB", "tests", "test_files", "exported_db.zip"
+ )
+ self.tempmediadir = tempfile.TemporaryDirectory()
+ self.wrong_file_name = join(
+ settings.BASE_DIR,
+ "TEKDB",
+ "tests",
+ "test_files",
+ "test_wrong_import.txt",
+ )
- cls.factory = RequestFactory()
-
- cls.credentials = b64encode(b"admin:admin").decode("ascii")
- cls.dummy_1_name = "Dummy Record 1"
-
- cls.old_resources_count = Resources.objects.all().count()
- new_record = Resources.objects.create(commonname=cls.dummy_1_name)
+ def test_successful_import(self):
+ self.old_resources_count = Resources.objects.all().count()
+ new_record = Resources.objects.create(commonname=self.dummy_1_name)
new_record.save()
# Resources.moderated_object.fget(new_record).approve()
- cls.zipname = join(
- settings.BASE_DIR, "TEKDB", "tests", "test_files", "exported_db.zip"
- )
- cls.tempmediadir = tempfile.TemporaryDirectory()
- cls.import_request = cls.factory.post(
+ self.import_request = self.factory.post(
reverse("import_database"),
{
- "MEDIA_DIR": cls.tempmediadir.name,
+ "MEDIA_DIR": self.tempmediadir.name,
"content_type": "application/zip",
"content_disposition": "attachment; filename=uploaded.dump",
},
- headers={"Authorization": f"Basic {cls.credentials}"},
+ headers={"Authorization": f"Basic {self.credentials}"},
)
- with open(cls.zipname, "rb") as z:
+ with open(self.zipname, "rb") as z:
import_file = InMemoryUploadedFile(
z,
"import_file",
"exported_db.zip",
"application/zip",
- getsize(cls.zipname),
+ getsize(self.zipname),
None,
)
- cls.import_request.FILES["import_file"] = import_file
- cls.import_request.user = Users.objects.get(username="admin")
- response = ImportDatabase(cls.import_request) # noqa: F841
-
- def test_import(self):
+ self.import_request.FILES["import_file"] = import_file
+ self.import_request.user = Users.objects.get(username="admin")
+ ImportDatabase(self.import_request)
self.assertEqual(Resources.objects.all().count(), self.old_resources_count)
self.assertEqual(
Resources.objects.filter(commonname=self.dummy_1_name).count(), 0
@@ -533,117 +534,87 @@ def test_import(self):
self.assertTrue(media_name in listdir(self.tempmediadir.name))
shutil.rmtree(self.tempmediadir.name)
+ def test_failed_import_wrong_method(self):
+ self.import_request = self.factory.get(
+ reverse("import_database"),
+ {
+ "MEDIA_DIR": self.tempmediadir.name,
+ "content_type": "application/zip",
+ "content_disposition": "attachment; filename=uploaded.dump",
+ },
+ headers={"Authorization": f"Basic {self.credentials}"},
+ )
+ with open(self.zipname, "rb") as z:
+ import_file = InMemoryUploadedFile(
+ z,
+ "import_file",
+ "exported_db.zip",
+ "application/zip",
+ getsize(self.zipname),
+ None,
+ )
+ self.import_request.FILES["import_file"] = import_file
+ self.import_request.user = Users.objects.get(username="admin")
+ response = ImportDatabase(self.import_request)
+ self.assertEqual(response.status_code, 405)
+
+ def test_failed_import_incorrect_file_type(self):
+ self.import_request = self.factory.post(
+ reverse("import_database"),
+ {
+ "MEDIA_DIR": self.tempmediadir.name,
+ "content_type": "application/zip",
+ "content_disposition": "attachment; filename=uploaded.dump",
+ },
+ headers={"Authorization": f"Basic {self.credentials}"},
+ )
+ # Upload a non-zip file
+ with open(
+ self.wrong_file_name,
+ "rb",
+ ) as f:
+ import_file = InMemoryUploadedFile(
+ f,
+ "import_file",
+ "test_wrong_import.txt",
+ "text/plain",
+ getsize(self.wrong_file_name),
+ None,
+ )
+ self.import_request.FILES["import_file"] = import_file
+ self.import_request.user = Users.objects.get(username="admin")
+ response = ImportDatabase(self.import_request)
+ self.assertEqual(response.status_code, 400)
+ response.data = json.loads(response.content)
+ self.assertEqual(
+ "Uploaded file is not recognized as a zipfile. Be sure you have a valid backup file and try again.",
+ response.data["status_message"],
+ )
+
+
+class GetPlacesGeoJSONTest(TestCase):
+ def setUp(self):
+ import_fixture_file(
+ join(settings.BASE_DIR, "TEKDB", "fixtures", "all_dummy_data.json")
+ )
+ self.factory = RequestFactory()
+ self.credentials = b64encode(b"admin:admin").decode("ascii")
+
+ def test_get_places_geojson(self):
+ from TEKDB.views import get_places_geojson
+
+ user = Users.objects.get(username="admin")
+ self.client.force_login(user)
+ self.import_request = self.factory.get(
+ "/admin/TEKDB/places/",
+ {
+ "content_type": "application/zip",
+ },
+ headers={"Authorization": f"Basic {self.credentials}"},
+ )
+ self.import_request.user = user
+
+ places_geojson = get_places_geojson(self.import_request)
-### 2025-05-09: No one has any idea what a 'placeMap' is.
-# class PlaceMapTest(TestCase):
-# # fixtures = ['TEKDB/fixtures/all_dummy_data.json',]
-
-# @classmethod
-# def setUp(cls):
-# import_fixture_file(join(settings.BASE_DIR, 'TEKDB', 'fixtures', 'all_dummy_data.json'))
-
-# cls.factory = RequestFactory()
-# cls.credentials = b64encode(b"admin:admin").decode("ascii")
-# cls.dummy_1_name = "Dummy Record 1"
-
-# new_record = Places.objects.create(englishplacename="Dummy Record 1")
-# new_record.save()
-
-# def test_placeMap(self):
-# request = self.factory.get(reverse('placeMap'))
-# request.user = Users.objects.get(username='admin')
-# response = placeMap(request)
-# self.assertEqual(response.status_code, 200)
-
-
-# class ExportImportTest(TestCase):
-# # fixtures = ['TEKDB/fixtures/all_dummy_data.json',]
-#
-# @classmethod
-# def setUpTestData(cls):
-# cls.factory = RequestFactory()
-# cls.credentials = b64encode(b"admin:admin").decode("ascii")
-#
-# cls.dummy_1_name = "Dummy Record 1"
-# cls.dummy_2_name = "Dummy Record 2"
-#
-# import ipdb; ipdb.set_trace()
-#
-# new_record = Resources.objects.create(commonname=cls.dummy_1_name)
-# new_record.save()
-#
-# # Count records
-# cls.original_media_count = Media.objects.all().count()
-# cls.original_places_count = Places.objects.all().count()
-# cls.original_resources_count = Resources.objects.all().count()
-# cls.original_citations_count = Citations.objects.all().count()
-# cls.original_activities_count = ResourcesActivityEvents.objects.all().count()
-#
-# export_request = create_export_request(cls)
-# export_request.user = Users.objects.get(username='admin')
-# response = ExportDatabase(export_request)
-# cls.tempdir = tempfile.gettempdir()
-# zipname = get_export_file_from_response(response, cls.tempdir)
-#
-# new_record2 = Resources.objects.create(commonname=cls.dummy_2_name)
-# new_record2.save()
-#
-# cls.tempmediadir = tempfile.gettempdir()
-# cls.import_request = cls.factory.post(
-# reverse('import_database'),
-# {
-# 'MEDIA_DIR': cls.tempmediadir,
-# },
-# headers = {
-# "Authorization": f"Basic {cls.credentials}"
-# },
-# )
-# # Attach .zip to request
-# with open(zipname, 'rb') as z:
-# cls.import_request.FILES['import_file'] = z
-# cls.import_request.FILES['import_file'].read()
-# z.seek(0)
-#
-# cls.import_request.user = Users.objects.get(username='admin')
-# response = ImportDatabase(cls.import_request)
-#
-# remove(zipname)
-#
-# def test_export_import(self):
-# ############################################################
-# ### IMPORT
-# ############################################################
-# tempdir = self.tempdir
-# # zipname = self.zipname
-#
-# # Prep for test import:
-# # Create temp dir to write media to
-#
-# # view must take optional MEDIA_DIR location
-# # MAKE SURE TEST DOESN"T OVERRIDE LIVE DB!!!
-# self.assertEqual(Resources.objects.all().count(), self.original_resources_count )
-# self.assertEqual(Media.objects.all().count(), self.original_media_count)
-# self.assertEqual(Places.objects.all().count(), self.original_places_count)
-# self.assertEqual(Resources.objects.all().count(), self.original_resources_count)
-# self.assertEqual(Citations.objects.all().count(), self.original_citations_count)
-# self.assertEqual(ResourcesActivityEvents.objects.all().count(), self.original_activities_count)
-# # test for added record
-# dummy1_q = Resources.objects.filter(commonname=self.dummy_1_name)
-# dummy2_q = Resources.objects.filter(commonname=self.dummy_2_name)
-# self.assertEqual(dummy1_q.count(), 1)
-# self.assertEqual(dummy2_q.count(), 0)
-# self.assertEqual(dummy1_q[0].pk, first_resource.pk)
-# #
-# # import .zip
-# # Create Request
-#
-# self.import_request.user = AnonymousUser()
-# anon_response = ImportDatabase(self.import_request)
-# # Users w/out adequate permissions should be redirected (302)
-# self.assertEqual(anon_response.status_code, 302)
-# self.import_request.user = Users.objects.get(username='readonly')
-# bad_response = ImportDatabase(self.import_request)
-# self.assertEqual(bad_response.status_code, 302)
-#
-# # self.assertEqual(response.status_code, 200)
-# # Specify target MEDIA_DIR in request
+ self.assertTrue(isinstance(places_geojson, dict))
+ self.assertIn("features", places_geojson)
diff --git a/TEKDB/TEKDB/views.py b/TEKDB/TEKDB/views.py
index 915b48cd..0da6aec9 100644
--- a/TEKDB/TEKDB/views.py
+++ b/TEKDB/TEKDB/views.py
@@ -88,7 +88,7 @@ def ExportDatabase(request, test=False):
zip.write(media_file)
response = FileResponse(open(tmp_zip.name, "rb"))
-
+
finally:
try:
if not test:
@@ -97,7 +97,7 @@ def ExportDatabase(request, test=False):
except (PermissionError, NotADirectoryError):
response.set_cookie("export_status", "error")
pass
- return response
+ return response
def getDBTruncateCommand():
@@ -117,6 +117,10 @@ def ImportDatabase(request):
if not request.method == "POST":
status_code = 405
status_message = "Request method not allowed. Must be a post."
+ return JsonResponse(
+ {"status_code": status_code, "status_message": status_message},
+ status=status_code,
+ )
else:
if "MEDIA_DIR" in request.POST.keys() and os.path.exists(
request.POST["MEDIA_DIR"]
@@ -135,13 +139,15 @@ def ImportDatabase(request):
for chunk in request.FILES["import_file"].chunks():
tmp_zip_file.write(chunk)
tmp_zip_file.seek(0)
+
except Exception as e:
status_code = 500
status_message = 'Unable to read the provided file. Be sure it is a zipped file containing a .json representing the database and a "media" directory containing any static files. {}'.format(
e
)
return JsonResponse(
- {"status_code": status_code, "status_message": status_message}
+ {"status_code": status_code, "status_message": status_message},
+ status=status_code,
)
if zipfile.is_zipfile(tmp_zip_file):
zip = zipfile.ZipFile(tmp_zip_file, "r")
@@ -156,7 +162,8 @@ def ImportDatabase(request):
{
"status_code": status_code,
"status_message": status_message,
- }
+ },
+ status=status_code,
)
fixture_name = non_media[0]
try:
@@ -170,7 +177,8 @@ def ImportDatabase(request):
{
"status_code": status_code,
"status_message": status_message,
- }
+ },
+ status=status_code,
)
try:
# Emptying DB tables
@@ -187,7 +195,8 @@ def ImportDatabase(request):
{
"status_code": status_code,
"status_message": status_message,
- }
+ },
+ status=status_code,
)
try:
@@ -216,7 +225,8 @@ def ImportDatabase(request):
{
"status_code": status_code,
"status_message": status_message,
- }
+ },
+ status=status_code,
)
try:
@@ -235,7 +245,8 @@ def ImportDatabase(request):
{
"status_code": status_code,
"status_message": status_message,
- }
+ },
+ status=status_code,
)
status_code = 200
status_message = "Database import completed successfully."
@@ -248,18 +259,20 @@ def ImportDatabase(request):
"Request must have an attached zipfile to restore the database from"
)
- return JsonResponse({"status_code": status_code, "status_message": status_message})
+ return JsonResponse(
+ {"status_code": status_code, "status_message": status_message},
+ status=status_code,
+ )
# Only Authenticated Users!
@permission_required("TEKDB.change_list")
-def getPlacesGeoJSON(request):
+def get_places_geojson(request):
from .models import Places
import json
# Get all places
places = Places.objects.exclude(geometry__isnull=True)
-
# GeoJSON to store all places
geojson = {"type": "FeatureCollection", "features": []}
diff --git a/TEKDB/explore/tests/test_context_processors.py b/TEKDB/explore/tests/test_context_processors.py
new file mode 100644
index 00000000..f1fb29cf
--- /dev/null
+++ b/TEKDB/explore/tests/test_context_processors.py
@@ -0,0 +1,135 @@
+from django.test import TestCase, RequestFactory, override_settings
+from explore.context_processors import explore_context
+
+
+class TestExploreContext(TestCase):
+ def setUp(self):
+ self.factory = RequestFactory()
+
+ def test_search_settings_no_settings(self):
+ from unittest import mock
+ import builtins
+
+ request = self.factory.get("/")
+
+ real_import = builtins.__import__
+
+ def mock_import(name, globals=None, locals=None, fromlist=(), level=0):
+ if (
+ name == "django.conf"
+ or name.startswith("django.conf")
+ or name == "TEKDB"
+ ):
+ raise ImportError("Mock missing settings module")
+ return real_import(name, globals, locals, fromlist, level)
+
+ with mock.patch("builtins.__import__", side_effect=mock_import):
+ context = explore_context(request)
+
+ self.assertIn("proj_logo_text", context)
+ self.assertIn("proj_text_placement", context)
+
+ @override_settings(
+ PROJ_LOGO_TEXT="",
+ PROJ_CSS={},
+ PROJ_ICONS={},
+ PROJ_IMAGE_SELECT="",
+ PROJ_IMAGE_ATTR="",
+ HOME_FONT_COLOR="#FFFFFF",
+ HOME_LEFT_BACKGROUND="#000000",
+ HOME_RIGHT_BACKGROUND="#000000",
+ MEDIA_ROOT="/media/",
+ MEDIA_URL="/media/",
+ )
+ def test_explore_context_with_default_values(self):
+ request = self.factory.get("/")
+ context = explore_context(request)
+
+ # Assertions for default values
+ self.assertEqual(context["proj_logo_text"], "ITK")
+ self.assertEqual(context["proj_text_placement"], "default")
+ self.assertEqual(context["proj_css"]["primary_a"], "#8f371c")
+ self.assertEqual(
+ context["proj_icons"]["logo"], "/static/explore/img/logos/logo_weave.svg"
+ )
+ self.assertEqual(
+ context["proj_image_select"],
+ "/static/explore/img/homepage/5050508427_ec55eed5f4_o.jpg",
+ )
+ self.assertEqual(
+ context["home_image_attribution"],
+ 'Image courtesy of Monteregina and used under the CC BY-NC-SA 2.0 Licence. No changes were made.',
+ )
+ self.assertEqual(context["home_font_color"], "#FFFFFF")
+ self.assertEqual(context["homepage_left_background"], "#000000")
+ self.assertEqual(context["homepage_right_background"], "#000000")
+ self.assertEqual(
+ context["map_pin"], "/static/explore/img/icons/explore_map_pin.svg"
+ )
+ self.assertEqual(
+ context["map_pin_selected"],
+ "/static/explore/img/icons/explore_map_pin_selected.svg",
+ )
+
+ @override_settings(
+ PROJ_LOGO_TEXT="Custom Logo",
+ PROJ_CSS={"primary_a": "#123456"},
+ PROJ_ICONS={"logo": "/custom/logo.svg"},
+ PROJ_IMAGE_SELECT="/custom/image.jpg",
+ PROJ_IMAGE_ATTR="Custom Attribution",
+ HOME_FONT_COLOR="#ABCDEF",
+ HOME_LEFT_BACKGROUND="#123123",
+ HOME_RIGHT_BACKGROUND="#321321",
+ MEDIA_ROOT="/media/",
+ MEDIA_URL="/media/",
+ )
+ def test_explore_context_with_custom_settings(self):
+ request = self.factory.get("/")
+ context = explore_context(request)
+
+ # Assertions for custom settings
+ self.assertEqual(context["proj_logo_text"], "Custom Logo")
+ self.assertEqual(context["proj_css"]["primary_a"], "#123456")
+ self.assertEqual(context["proj_icons"]["logo"], "/custom/logo.svg")
+ self.assertEqual(context["proj_image_select"], "/custom/image.jpg")
+ self.assertEqual(context["home_image_attribution"], "Custom Attribution")
+ self.assertEqual(context["home_font_color"], "#ABCDEF")
+ self.assertEqual(context["homepage_left_background"], "#123123")
+ self.assertEqual(context["homepage_right_background"], "#321321")
+ self.assertEqual(
+ context["map_pin"], "/static/explore/img/icons/explore_map_pin.svg"
+ )
+ self.assertEqual(
+ context["map_pin_selected"],
+ "/static/explore/img/icons/explore_map_pin_selected.svg",
+ )
+
+ @override_settings(
+ PROJ_LOGO_TEXT="",
+ PROJ_CSS={},
+ PROJ_ICONS={},
+ MEDIA_ROOT="/media/",
+ MEDIA_URL="/media/",
+ )
+ def test_explore_context_with_configuration_model(self):
+ from configuration.models import Configuration
+
+ Configuration.objects.create(
+ preferredInitialism="Config Logo",
+ preferredInitialismPlacement="top",
+ homepage_image_attribution="Config Attribution",
+ homepage_font_color="#FFFFFF",
+ homepage_left_background="#000000",
+ homepage_right_background="#467011",
+ )
+
+ request = self.factory.get("/")
+ context = explore_context(request)
+
+ # Assertions for configuration model
+ self.assertEqual(context["proj_logo_text"], "Config Logo")
+ self.assertEqual(context["proj_text_placement"], "top")
+ self.assertEqual(context["home_image_attribution"], "Config Attribution")
+ self.assertEqual(context["home_font_color"], "#FFFFFF")
+ self.assertEqual(context["homepage_left_background"], "#000000")
+ self.assertEqual(context["homepage_right_background"], "#467011")
diff --git a/TEKDB/explore/tests/test_views.py b/TEKDB/explore/tests/test_views.py
index 798e9a3f..5358ac57 100644
--- a/TEKDB/explore/tests/test_views.py
+++ b/TEKDB/explore/tests/test_views.py
@@ -5,6 +5,8 @@
from django.test.client import RequestFactory
from django.urls import reverse
from os.path import join
+from unittest.mock import patch, MagicMock
+
from TEKDB.tests.test_views import import_fixture_file
#########################################################################
@@ -13,6 +15,145 @@
#########################################################################
+class HomeViewTest(TestCase):
+ def test_home_view_html_content(self):
+ with patch("explore.views.PageContent.objects.get") as mock_get:
+ mock_obj = MagicMock()
+ mock_obj.is_html = True
+ mock_obj.html_content = "Test HTML Home"
+ mock_get.return_value = mock_obj
+ response = self.client.get("")
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("Test HTML Home", response.content.decode())
+
+ def test_home_view_not_html_content(self):
+ with patch("explore.views.PageContent.objects.get") as mock_get:
+ mock_obj = MagicMock()
+ mock_obj.is_html = False
+ mock_obj.content = "Test Text Home"
+ mock_get.return_value = mock_obj
+ response = self.client.get("")
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("Test Text Home", response.content.decode())
+
+ def test_home_view(self):
+ url = ""
+ response = self.client.get(url)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["page"], "home")
+ self.assertEqual(response.context["pageTitle"], "Welcome")
+ self.assertEqual(
+ response.context["pageContent"],
+ "Welcome
Set Welcome Page Content In Admin
",
+ )
+
+
+class AboutViewTest(TestCase):
+ def test_about_view_html_content(self):
+ with patch("explore.views.PageContent.objects.get") as mock_get:
+ mock_obj = MagicMock()
+ mock_obj.is_html = True
+ mock_obj.html_content = "Test HTML About"
+ mock_get.return_value = mock_obj
+ response = self.client.get("/about/")
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("Test HTML About", response.content.decode())
+
+ def test_about_view_not_html_content(self):
+ with patch("explore.views.PageContent.objects.get") as mock_get:
+ mock_obj = MagicMock()
+ mock_obj.is_html = False
+ mock_obj.content = "Test Text About"
+ mock_get.return_value = mock_obj
+ response = self.client.get("/about/")
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("Test Text About", response.content.decode())
+
+ def test_about_view(self):
+ url = "/about/"
+ response = self.client.get(url)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["page"], "about")
+ self.assertEqual(response.context["pageTitle"], "About")
+ self.assertIn("Set About Page Content In Admin", response.content.decode())
+
+
+class HelpViewTest(TestCase):
+ def test_help_view_html_content(self):
+ with patch("explore.views.PageContent.objects.get") as mock_get:
+ mock_obj = MagicMock()
+ mock_obj.is_html = True
+ mock_obj.html_content = "Test HTML Help"
+ mock_get.return_value = mock_obj
+ response = self.client.get("/help/")
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("Test HTML Help", response.content.decode())
+
+ def test_help_view_not_html_content(self):
+ with patch("explore.views.PageContent.objects.get") as mock_get:
+ mock_obj = MagicMock()
+ mock_obj.is_html = False
+ mock_obj.content = "Test Text Help"
+ mock_get.return_value = mock_obj
+ response = self.client.get("/help/")
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("Test Text Help", response.content.decode())
+
+ def test_help_view(self):
+ url = "/help/"
+ response = self.client.get(url)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["page"], "help")
+ self.assertEqual(response.context["pageTitle"], "Help")
+
+
+class ExploreViewTest(TestCase):
+ def setUp(self):
+ import_fixture_file(
+ join(settings.BASE_DIR, "TEKDB", "fixtures", "all_dummy_data.json")
+ )
+
+ self.factory = RequestFactory()
+ self.credentials = b64encode(b"admin:admin").decode("ascii")
+
+ def test_explore_view(self):
+ from TEKDB.models import Users
+
+ user = Users.objects.get(username="admin")
+ self.client.force_login(user)
+ url = "/explore/"
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["page"], "explore")
+ self.assertEqual(response.context["pageTitle"], "Search")
+ self.assertEqual(response.context["user"], user)
+
+
+class GetByModelTypeTest(TestCase):
+ def setUp(self):
+ import_fixture_file(
+ join(settings.BASE_DIR, "TEKDB", "fixtures", "all_dummy_data.json")
+ )
+
+ self.factory = RequestFactory()
+ self.credentials = b64encode(b"admin:admin").decode("ascii")
+
+ def test_get_by_model_type(self):
+ # Test that a valid model_type returns the expected records
+ from TEKDB.models import Users
+
+ user = Users.objects.get(username="admin")
+ self.client.force_login(user)
+ url = "/explore/Places/"
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["category"], "Places")
+ self.assertEqual(response.context["page"], "Results")
+ self.assertEqual(response.context["pageTitle"], "Results")
+
+
class SearchTest(TestCase):
# fixtures = ['TEKDB/fixtures/all_dummy_data.json',]
@@ -52,16 +193,60 @@ def setUp(self):
def test_get_by_model_id(self):
# Test that a valid model_type and id returns the expected record
- from TEKDB.models import Users, Places
+ from TEKDB.models import (
+ Users,
+ Places,
+ Resources,
+ Media,
+ PlacesResourceEvents,
+ Citations,
+ ResourcesActivityEvents,
+ )
- place = Places.objects.first()
user = Users.objects.get(username="admin")
self.client.force_login(user)
+
+ place = Places.objects.first()
url = f"/explore/Places/{place.pk}/"
- response = self.client.get(url)
- model_name = response.context[0]["model_name"]
+ place_response = self.client.get(url)
+ model_name = place_response.context[0]["model_name"]
self.assertEqual(model_name, "Place")
- self.assertEqual(response.status_code, 200)
+ self.assertEqual(place_response.status_code, 200)
+
+ resource = Resources.objects.first()
+ url = f"/explore/resources/{resource.pk}/"
+ resource_response = self.client.get(url)
+ model_name = resource_response.context[0]["model_name"]
+ self.assertEqual(model_name, "Resource")
+ self.assertEqual(resource_response.status_code, 200)
+
+ rae = ResourcesActivityEvents.objects.first()
+ url = f"/explore/resourcesactivityevents/{rae.pk}/"
+ rae_response = self.client.get(url)
+ model_name = rae_response.context[0]["model_name"]
+ self.assertEqual(model_name, "Activity")
+ self.assertEqual(rae_response.status_code, 200)
+
+ media = Media.objects.first()
+ url = f"/explore/media/{media.pk}/"
+ media_response = self.client.get(url)
+ model_name = media_response.context[0]["model_name"]
+ self.assertEqual(model_name, "Media")
+ self.assertEqual(media_response.status_code, 200)
+
+ pre = PlacesResourceEvents.objects.first()
+ url = f"/explore/placesresourceevents/{pre.pk}/"
+ pre_response = self.client.get(url)
+ model_name = pre_response.context[0]["model_name"]
+ self.assertEqual(model_name, "Place-Resource Event")
+ self.assertEqual(pre_response.status_code, 200)
+
+ citation = Citations.objects.first()
+ url = f"/explore/citations/{citation.pk}/"
+ citation_response = self.client.get(url)
+ model_name = citation_response.context[0]["model_name"]
+ self.assertEqual(model_name, "Bibliographic Source")
+ self.assertEqual(citation_response.status_code, 200)
def test_get_by_model_id_invalid_model(self):
# Test that an invalid model_type returns an error message
@@ -108,3 +293,261 @@ def test_get_by_model_id_with_map(self):
]
for key in map_keys:
self.assertIn(key, data)
+
+
+class DownloadMediaFileTest(TestCase):
+ def setUp(self):
+ import_fixture_file(
+ join(settings.BASE_DIR, "TEKDB", "fixtures", "all_dummy_data.json")
+ )
+
+ self.factory = RequestFactory()
+ self.credentials = b64encode(b"admin:admin").decode("ascii")
+
+ def test_download_media_file(self):
+ from TEKDB.models import Users, Media
+
+ user = Users.objects.get(username="admin")
+ self.client.force_login(user)
+
+ media = Media.objects.first()
+ url = f"/explore/Media/{media.pk}/download"
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ response["Content-Disposition"],
+ f"attachment; filename={media.mediafile}",
+ )
+ self.assertEqual(response["Content-Type"], "application/force-download")
+
+ def test_download_media_file_invalid_id(self):
+ from TEKDB.models import Users
+
+ user = Users.objects.get(username="admin")
+ self.client.force_login(user)
+
+ url = "/explore/Media/999999/download"
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 404)
+
+ def test_invalid_model_type(self):
+ from TEKDB.models import Users
+
+ user = Users.objects.get(username="admin")
+ self.client.force_login(user)
+
+ url = "/explore/InvalidModel/1/download"
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 404)
+
+
+class GetSortedKeysTest(TestCase):
+ def test_get_sorted_keys(self):
+ from explore.views import get_sorted_keys
+
+ keys = ["name", "link", "map"]
+ sorted_keys = ["name", "map", "link"]
+ self.assertEqual(sorted_keys, get_sorted_keys(keys))
+
+ def test_get_sorted_keys_unique_key(self):
+ from explore.views import get_sorted_keys
+
+ keys = ["name", "link", "foo", "map", "bar"]
+ # should append keys not in priority list to end
+ sorted_keys = ["name", "map", "link", "foo", "bar"]
+ self.assertEqual(sorted_keys, get_sorted_keys(keys))
+
+
+class ExportRecordCsvTest(TestCase):
+ def setUp(self):
+ from os.path import join
+ from django.conf import settings
+ from TEKDB.tests.test_views import import_fixture_file
+
+ import_fixture_file(
+ join(settings.BASE_DIR, "TEKDB", "fixtures", "all_dummy_data.json")
+ )
+ from TEKDB.models import Users
+
+ self.user = Users.objects.get(username="admin")
+ self.client.force_login(self.user)
+
+ def test_export_record_csv(self):
+ from TEKDB.models import Places
+
+ place = Places.objects.get(pk=31)
+ url = f"/export/Places/{place.pk}/csv/"
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response["Content-Type"], "text/csv")
+
+ self.assertIn(
+ f'attachment; filename="Places_{place.pk}_{str(place)}.csv"',
+ response["Content-Disposition"],
+ )
+ self.assertIn("id", response.content.decode())
+ self.assertIn("name", response.content.decode())
+
+
+class ExportRecordXlsTest(TestCase):
+ def setUp(self):
+ from os.path import join
+ from django.conf import settings
+ from TEKDB.tests.test_views import import_fixture_file
+
+ import_fixture_file(
+ join(settings.BASE_DIR, "TEKDB", "fixtures", "all_dummy_data.json")
+ )
+ from TEKDB.models import Users
+
+ self.user = Users.objects.get(username="admin")
+ self.client.force_login(self.user)
+
+ def test_export_record_xls(self):
+ from TEKDB.models import Places
+
+ place = Places.objects.get(pk=31)
+ url = f"/export/Places/{place.pk}/xls/"
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ response["Content-Type"],
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ )
+
+ self.assertIn(
+ f'attachment; filename="Places_{place.pk}_{str(place)}.xlsx"',
+ response["Content-Disposition"],
+ )
+ self.assertEqual(
+ response.content[:2], b"PK"
+ ) # XLSX files start with 'PK' (zip signature)
+
+
+class SearchViewTest(TestCase):
+ def setUp(self):
+ from os.path import join
+ from django.conf import settings
+ from TEKDB.tests.test_views import import_fixture_file
+ from TEKDB.models import Users
+
+ import_fixture_file(
+ join(settings.BASE_DIR, "TEKDB", "fixtures", "all_dummy_data.json")
+ )
+ self.user = Users.objects.get(username="admin")
+ self.client.force_login(self.user)
+
+ def test_search_view_get_request(self):
+ url = "/search/?query=test&category=places"
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["page"], "Results")
+ self.assertEqual(response.context["pageTitle"], "Results")
+ self.assertEqual(response.context["query"], "test")
+ self.assertEqual(response.context["keyword"], "test")
+ self.assertEqual(response.context["categories"], '["places"]')
+ self.assertIn(
+ '',
+ response.context["category_checkboxes"],
+ )
+ self.assertNotIn(
+ '',
+ response.context["category_checkboxes"],
+ )
+
+ def test_search_view_misspelled_category(self):
+ url = "/search/?query=test&category=placess,resourcess"
+ response = self.client.get(url)
+ # should resort to all
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("test", response.context["query"])
+ self.assertEqual(
+ response.context["categories"],
+ '["places", "resources", "activities", "sources", "media"]',
+ )
+ self.assertNotIn(
+ '',
+ response.context["category_checkboxes"],
+ )
+
+ def test_search_view_no_category(self):
+ url = "/search/?query=test"
+ response = self.client.get(url)
+ # should resort to all
+ self.assertEqual(response.status_code, 200)
+ self.assertIn("test", response.context["query"])
+ self.assertEqual(
+ response.context["categories"],
+ '["places", "resources", "activities", "sources", "media"]',
+ )
+ self.assertNotIn(
+ '',
+ response.context["category_checkboxes"],
+ )
+
+ def test_search_view_post_request(self):
+ url = "/search/"
+ data = {
+ "query": "test",
+ "activities": "on",
+ "citations": "on",
+ "media": "on",
+ }
+ response = self.client.post(url, data)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["page"], "Results")
+ self.assertEqual(response.context["pageTitle"], "Results")
+ self.assertIn("test", response.context["query"])
+ self.assertEqual(
+ response.context["categories"], '["activities", "sources", "media"]'
+ )
+
+
+class DownloadViewTest(TestCase):
+ def setUp(self):
+ from os.path import join
+ from django.conf import settings
+ from TEKDB.tests.test_views import import_fixture_file
+
+ import_fixture_file(
+ join(settings.BASE_DIR, "TEKDB", "fixtures", "all_dummy_data.json")
+ )
+ from TEKDB.models import Users
+
+ self.user = Users.objects.get(username="admin")
+ self.client.force_login(self.user)
+
+ def test_download_csv(self):
+ url = "/export?query=test&places=true&format=csv"
+ response = self.client.get(url, follow=True)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response["Content-Type"], "text/csv")
+ self.assertIn(
+ 'attachment; filename="TEK_RESULTS.csv"', response["Content-Disposition"]
+ )
+ self.assertIn("id", response.content.decode())
+ self.assertIn("name", response.content.decode())
+
+ def test_download_xlsx(self):
+ url = "/export?query=test&places=true&format=xlsx"
+ response = self.client.get(url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ response["Content-Type"],
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ )
+ self.assertIn(
+ "attachment; filename=TEK_RESULTS.xlsx", response["Content-Disposition"]
+ )
+ self.assertEqual(
+ response.content[:2], b"PK"
+ ) # XLSX files start with 'PK' (zip signature)
diff --git a/TEKDB/explore/views.py b/TEKDB/explore/views.py
index 534a428a..24c547f0 100644
--- a/TEKDB/explore/views.py
+++ b/TEKDB/explore/views.py
@@ -244,11 +244,11 @@ def download_media_file(request, model_type, id):
else:
obj = None
- media = obj.media()
- if media:
+ if obj is not None:
import os
from TEKDB.settings import MEDIA_ROOT
+ media = obj.media()
file_path = os.path.join(MEDIA_ROOT, media["file"])
if os.path.exists(file_path):
with open(file_path, "rb") as fh:
@@ -260,9 +260,9 @@ def download_media_file(request, model_type, id):
)
return response
else:
- return Http404
+ raise Http404
else:
- return Http404
+ raise Http404
def get_sorted_keys(keys):