@@ -333,16 +333,28 @@ def unapply(self) -> None:
333
333
affected_deltas = self .delta_urls .all ()
334
334
affected_curated = self .curated_urls .all ()
335
335
336
+ # Get all other patterns of same type for this collection
337
+ pattern_class = self .__class__
338
+ other_patterns = pattern_class .objects .filter (collection = self .collection ).exclude (id = self .id )
339
+
336
340
# Process each affected delta URL
337
341
for delta in affected_deltas :
338
342
curated = CuratedUrl .objects .filter (collection = self .collection , url = delta .url ).first ()
339
343
340
- if not curated :
341
- # Scenario 1: Delta only - new URL
342
- setattr (delta , field , None )
344
+ # Find next most specific matching pattern if any
345
+ matching_patterns = [p for p in other_patterns if re .search (p .get_regex_pattern (), delta .url )]
346
+
347
+ next_pattern = None
348
+ if matching_patterns :
349
+ # Sort by number of URLs matched (ascending) to find most specific
350
+ next_pattern = min (matching_patterns , key = lambda p : p .get_url_match_count ())
351
+
352
+ if next_pattern :
353
+ # Apply next most specific pattern's value
354
+ setattr (delta , field , next_pattern .get_new_value ())
343
355
delta .save ()
344
- else :
345
- # Scenario 2: Both exist
356
+ elif curated :
357
+ # No other patterns match, revert to curated value
346
358
setattr (delta , field , getattr (curated , field ))
347
359
delta .save ()
348
360
@@ -354,17 +366,36 @@ def unapply(self) -> None:
354
366
)
355
367
if fields_match :
356
368
delta .delete ()
369
+ else :
370
+ # No curated URL or other patterns, set to None
371
+ setattr (delta , field , None )
372
+ delta .save ()
357
373
358
374
# Handle curated URLs that don't have deltas
359
375
for curated in affected_curated :
360
376
if not DeltaUrl .objects .filter (url = curated .url ).exists ():
361
- # Scenario 3: Curated only
362
- # Copy all fields from curated except the one we're nulling
363
- fields = {
364
- f .name : getattr (curated , f .name ) for f in curated ._meta .fields if f .name not in ["id" , "collection" ]
365
- }
366
- fields [field ] = None # Set the pattern's field to None
367
- delta = DeltaUrl .objects .create (collection = self .collection , ** fields )
377
+ # Find any matching patterns
378
+ matching_patterns = [p for p in other_patterns if re .search (p .get_regex_pattern (), curated .url )]
379
+
380
+ if matching_patterns :
381
+ # Apply most specific pattern's value
382
+ next_pattern = min (matching_patterns , key = lambda p : p .get_url_match_count ())
383
+ fields = {
384
+ f .name : getattr (curated , f .name )
385
+ for f in curated ._meta .fields
386
+ if f .name not in ["id" , "collection" ]
387
+ }
388
+ fields [field ] = next_pattern .get_new_value ()
389
+ DeltaUrl .objects .create (collection = self .collection , ** fields )
390
+ else :
391
+ # No other patterns, create delta with None
392
+ fields = {
393
+ f .name : getattr (curated , f .name )
394
+ for f in curated ._meta .fields
395
+ if f .name not in ["id" , "collection" ]
396
+ }
397
+ fields [field ] = None
398
+ DeltaUrl .objects .create (collection = self .collection , ** fields )
368
399
369
400
# Clear pattern relationships
370
401
self .delta_urls .clear ()
@@ -523,11 +554,12 @@ def apply(self) -> None:
523
554
524
555
def unapply (self ) -> None :
525
556
"""
526
- Remove title modifications:
527
- 1. Create Delta URLs for affected Curated URLs to explicitly clear titles
528
- 2. Remove generated titles from affected Delta URLs
529
- 3. Clean up Delta URLs that become identical to their Curated URL
530
- 4. Clear resolution tracking
557
+ Remove title modifications, maintaining pattern precedence:
558
+ 1. Find any remaining patterns that match each URL
559
+ 2. Apply most specific matching pattern's title if one exists
560
+ 3. Otherwise revert to curated title or clear title
561
+ 4. Update title resolution tracking
562
+ 5. Clean up redundant deltas
531
563
"""
532
564
DeltaUrl = apps .get_model ("sde_collections" , "DeltaUrl" )
533
565
CuratedUrl = apps .get_model ("sde_collections" , "CuratedUrl" )
@@ -538,16 +570,36 @@ def unapply(self) -> None:
538
570
affected_deltas = self .delta_urls .all ()
539
571
affected_curated = self .curated_urls .all ()
540
572
573
+ # Get all other title patterns for this collection
574
+ other_patterns = DeltaTitlePattern .objects .filter (collection = self .collection ).exclude (id = self .id )
575
+
541
576
# Process each affected delta URL
542
577
for delta in affected_deltas :
543
578
curated = CuratedUrl .objects .filter (collection = self .collection , url = delta .url ).first ()
544
579
545
- if not curated :
546
- # Scenario 1: Delta only - clear generated title
547
- delta .generated_title = ""
548
- delta .save ()
549
- else :
550
- # Scenario 2: Both exist - revert to curated title
580
+ # Find next most specific matching pattern if any
581
+ matching_patterns = [p for p in other_patterns if re .search (p .get_regex_pattern (), delta .url )]
582
+
583
+ next_pattern = None
584
+ if matching_patterns :
585
+ # Sort by number of URLs matched (ascending) to find most specific
586
+ next_pattern = min (matching_patterns , key = lambda p : p .get_url_match_count ())
587
+
588
+ if next_pattern :
589
+ # Apply next most specific pattern's title
590
+ new_title , error = next_pattern .generate_title_for_url (delta )
591
+ if error :
592
+ DeltaResolvedTitleError .objects .update_or_create (
593
+ delta_url = delta , defaults = {"title_pattern" : next_pattern , "error_string" : error }
594
+ )
595
+ else :
596
+ delta .generated_title = new_title
597
+ delta .save ()
598
+ DeltaResolvedTitle .objects .update_or_create (
599
+ delta_url = delta , defaults = {"title_pattern" : next_pattern , "resolved_title" : new_title }
600
+ )
601
+ elif curated :
602
+ # No other patterns match, revert to curated title
551
603
delta .generated_title = curated .generated_title
552
604
delta .save ()
553
605
@@ -559,18 +611,47 @@ def unapply(self) -> None:
559
611
)
560
612
if fields_match :
561
613
delta .delete ()
614
+ else :
615
+ # No curated URL or other patterns, clear title
616
+ delta .generated_title = ""
617
+ delta .save ()
562
618
563
619
# Handle curated URLs that don't have deltas
564
620
for curated in affected_curated :
565
621
if not DeltaUrl .objects .filter (url = curated .url ).exists ():
566
- # Scenario 3: Curated only - create delta with cleared title
567
- fields = {
568
- f .name : getattr (curated , f .name ) for f in curated ._meta .fields if f .name not in ["id" , "collection" ]
569
- }
570
- fields ["generated_title" ] = ""
571
- DeltaUrl .objects .create (collection = self .collection , ** fields )
572
-
573
- # Clear resolution tracking
622
+ # Find any matching patterns
623
+ matching_patterns = [p for p in other_patterns if re .search (p .get_regex_pattern (), curated .url )]
624
+
625
+ if matching_patterns :
626
+ # Apply most specific pattern's title
627
+ next_pattern = min (matching_patterns , key = lambda p : p .get_url_match_count ())
628
+
629
+ # Copy all fields from curated
630
+ fields = {
631
+ f .name : getattr (curated , f .name )
632
+ for f in curated ._meta .fields
633
+ if f .name not in ["id" , "collection" ]
634
+ }
635
+
636
+ # Generate and apply new title
637
+ new_title , error = next_pattern .generate_title_for_url (curated )
638
+ if not error :
639
+ fields ["generated_title" ] = new_title
640
+ delta = DeltaUrl .objects .create (collection = self .collection , ** fields )
641
+ DeltaResolvedTitle .objects .create (
642
+ title_pattern = next_pattern , delta_url = delta , resolved_title = new_title
643
+ )
644
+ else :
645
+ # No other patterns, create delta with cleared title
646
+ fields = {
647
+ f .name : getattr (curated , f .name )
648
+ for f in curated ._meta .fields
649
+ if f .name not in ["id" , "collection" ]
650
+ }
651
+ fields ["generated_title" ] = ""
652
+ DeltaUrl .objects .create (collection = self .collection , ** fields )
653
+
654
+ # Clear resolution tracking for this pattern
574
655
DeltaResolvedTitle .objects .filter (title_pattern = self ).delete ()
575
656
DeltaResolvedTitleError .objects .filter (title_pattern = self ).delete ()
576
657
0 commit comments