|
3 | 3 | from unittest.mock import mock_open, patch |
4 | 4 |
|
5 | 5 | import pytest |
| 6 | +from django.conf import settings |
6 | 7 | from django.contrib.admin.models import ADDITION, CHANGE, LogEntry |
| 8 | +from django.contrib.auth.models import User |
7 | 9 | from django.contrib.contenttypes.models import ContentType |
8 | 10 | from django.core.management import call_command |
9 | 11 | from django.core.management.base import CommandError |
10 | 12 |
|
11 | 13 | from geniza.corpus.models import Document, DocumentType |
12 | | -from geniza.footnotes.models import Creator, Footnote, Source, SourceType |
| 14 | +from geniza.footnotes.models import ( |
| 15 | + Creator, |
| 16 | + Footnote, |
| 17 | + Source, |
| 18 | + SourceLanguage, |
| 19 | + SourceType, |
| 20 | +) |
13 | 21 |
|
14 | 22 | # full set of column headers is irrelevant (the command indexes by position), |
15 | 23 | # but the row must be wide enough to reach the Posen URL column (index 11) |
@@ -243,6 +251,96 @@ def test_skips_bad_rows_and_reports(): |
243 | 251 | assert Footnote.objects.count() == 0 |
244 | 252 |
|
245 | 253 |
|
| 254 | +@pytest.mark.django_db |
| 255 | +def test_creator_ids_tolerate_blank_parts(): |
| 256 | + # separators with empty segments (e.g. a trailing ";") are ignored |
| 257 | + doc = make_document() |
| 258 | + ashur = Creator.objects.create(first_name_en="Amir", last_name_en="Ashur") |
| 259 | + outhwaite = Creator.objects.create(first_name_en="Ben", last_name_en="Outhwaite") |
| 260 | + |
| 261 | + run_command( |
| 262 | + [ |
| 263 | + make_row( |
| 264 | + pgpid=doc.pk, |
| 265 | + reassign="no", |
| 266 | + creator_ids="%d; ; %d;" % (ashur.pk, outhwaite.pk), |
| 267 | + ) |
| 268 | + ] |
| 269 | + ) |
| 270 | + |
| 271 | + source = Source.objects.get() |
| 272 | + assert list( |
| 273 | + source.authorship_set.order_by("sort_order").values_list( |
| 274 | + "creator_id", flat=True |
| 275 | + ) |
| 276 | + ) == [ashur.pk, outhwaite.pk] |
| 277 | + |
| 278 | + |
| 279 | +@pytest.mark.django_db |
| 280 | +def test_reassign_non_digital_translation_and_rerun(): |
| 281 | + # a footnote that is not a Digital Translation is still reassigned (with a |
| 282 | + # warning), and reassigning again is a no-op |
| 283 | + doc = make_document() |
| 284 | + creator = Creator.objects.create(first_name_en="Amir", last_name_en="Ashur") |
| 285 | + old_source = Source.objects.create( |
| 286 | + title="Wrong Source", source_type=SourceType.objects.get(type="Book") |
| 287 | + ) |
| 288 | + footnote = Footnote.objects.create( |
| 289 | + source=old_source, |
| 290 | + content_object=doc, |
| 291 | + doc_relation=[Footnote.EDITION], |
| 292 | + ) |
| 293 | + rows = [ |
| 294 | + make_row(reassign="yes", footnote_id=footnote.pk, creator_ids=str(creator.pk)) |
| 295 | + ] |
| 296 | + |
| 297 | + run_command(rows) |
| 298 | + footnote.refresh_from_db() |
| 299 | + new_source = Source.objects.get(title="A Test Chapter") |
| 300 | + assert footnote.source_id == new_source.pk |
| 301 | + |
| 302 | + # second run: footnote is already on the new source, so nothing changes |
| 303 | + run_command(rows) |
| 304 | + assert ( |
| 305 | + LogEntry.objects.filter(action_flag=CHANGE, object_id=footnote.pk).count() == 1 |
| 306 | + ) |
| 307 | + |
| 308 | + |
| 309 | +@pytest.mark.django_db |
| 310 | +def test_skip_indexing_disconnects_signal_handler(): |
| 311 | + doc = make_document() |
| 312 | + creator = Creator.objects.create(first_name_en="Amir", last_name_en="Ashur") |
| 313 | + |
| 314 | + with patch( |
| 315 | + "geniza.footnotes.management.commands.import_posen_translations." |
| 316 | + "IndexableSignalHandler.disconnect" |
| 317 | + ) as mock_disconnect: |
| 318 | + run_command( |
| 319 | + [make_row(pgpid=doc.pk, reassign="no", creator_ids=str(creator.pk))], |
| 320 | + "--skip-indexing", |
| 321 | + ) |
| 322 | + mock_disconnect.assert_called_once() |
| 323 | + |
| 324 | + |
| 325 | +@pytest.mark.django_db |
| 326 | +@pytest.mark.parametrize( |
| 327 | + # the command checks these records in order and raises on the first missing |
| 328 | + # one, so each must be deleted in its own run to cover all three branches |
| 329 | + "records", |
| 330 | + [ |
| 331 | + User.objects.filter(username=settings.SCRIPT_USERNAME), |
| 332 | + SourceType.objects.filter(type="Book Section"), |
| 333 | + SourceLanguage.objects.filter(name="English"), |
| 334 | + ], |
| 335 | + ids=["script_user", "book_section", "english"], |
| 336 | +) |
| 337 | +def test_errors_when_required_records_missing(records): |
| 338 | + # command should raise error if required records are missing |
| 339 | + records.delete() |
| 340 | + with pytest.raises(CommandError): |
| 341 | + run_command([make_row(pgpid=1, reassign="no", creator_ids="1")]) |
| 342 | + |
| 343 | + |
246 | 344 | @pytest.mark.django_db |
247 | 345 | def test_file_not_found(): |
248 | 346 | with pytest.raises(CommandError): |
|
0 commit comments