33import pghistory
44from django .core .files .base import ContentFile as CF
55from django .db import models
6- from django .db .models .signals import post_delete , post_save , pre_save
6+ from django .db .models .signals import m2m_changed , post_delete , post_save , pre_save
77from django .dispatch import receiver
88from django .urls import reverse
99from django .utils .translation import gettext_lazy as _
@@ -75,6 +75,8 @@ class Sound(TimeStampedModel, ContentMixin):
7575 file_extension = models .CharField (
7676 max_length = 10 , db_index = True , choices = SoundExtensions .choices
7777 )
78+ in_use = models .BooleanField (default = False )
79+ is_used_by_other_user = models .BooleanField (default = False )
7880
7981 @property
8082 def date (self ):
@@ -92,27 +94,7 @@ def augmenteds_count(self):
9294 def exhibits_count (self ):
9395 return self .exhibits .count ()
9496
95- def is_used_by_other_user (self ):
96- """
97- Check if the object is used by another user.
98- This is done by checking if there are artworks that reference this object
99- and if the owner of those artworks is not the current user.
100- """
101- return (
102- self .ar_objects .exclude (owner = self .owner ).exists ()
103- or self .artworks .exclude (author = self .owner ).exists ()
104- or self .exhibits .exclude (owner = self .owner ).exists ()
105- )
10697
107- @property
108- def in_use (self ):
109- if self .exhibits_count > 0 :
110- return True
111- if self .augmenteds_count > 0 :
112- return True
113- if self .artworks_count > 0 :
114- return True
115- return False
11698
11799 def used_in_html_string (self ):
118100 used_in = "{} {} {} {} {} {} {}" .format (
@@ -581,7 +563,7 @@ def remove_source_file(sender, instance, **kwargs):
581563
582564@receiver (pre_save , sender = Artwork )
583565def artwork_pre_save (sender , instance , ** kwargs ):
584- """Capture the previous marker/object before an artwork is updated."""
566+ """Capture the previous marker/object/sound before an artwork is updated."""
585567 if not instance .pk :
586568 return
587569 try :
@@ -590,14 +572,17 @@ def artwork_pre_save(sender, instance, **kwargs):
590572 return
591573 instance ._old_marker_id = old .marker_id
592574 instance ._old_augmented_id = old .augmented_id
575+ instance ._old_sound_id = old .sound_id
593576
594577
595578@receiver (post_save , sender = Artwork )
596579def artwork_post_save (sender , instance , ** kwargs ):
597- """Mark the current marker/object as in_use; check if old ones are still used."""
580+ """Mark the current marker/object/sound as in_use; check if old ones are still used."""
598581 # Mark current references as in use
599582 Marker .objects .filter (pk = instance .marker_id , in_use = False ).update (in_use = True )
600583 Object .objects .filter (pk = instance .augmented_id , in_use = False ).update (in_use = True )
584+ if instance .sound_id :
585+ Sound .objects .filter (pk = instance .sound_id , in_use = False ).update (in_use = True )
601586
602587 # Update is_used_by_other_user for current marker/object
603588 marker = Marker .objects .get (pk = instance .marker_id )
@@ -610,6 +595,13 @@ def artwork_post_save(sender, instance, **kwargs):
610595 if not augmented .is_used_by_other_user :
611596 Object .objects .filter (pk = augmented .pk ).update (is_used_by_other_user = True )
612597
598+ # Update is_used_by_other_user for current sound
599+ if instance .sound_id :
600+ sound = Sound .objects .get (pk = instance .sound_id )
601+ if instance .author_id != sound .owner_id :
602+ if not sound .is_used_by_other_user :
603+ Sound .objects .filter (pk = sound .pk ).update (is_used_by_other_user = True )
604+
613605 # If marker changed, check if the old one is still in use
614606 old_marker_id = getattr (instance , "_old_marker_id" , None )
615607 if old_marker_id and old_marker_id != instance .marker_id :
@@ -632,10 +624,15 @@ def artwork_post_save(sender, instance, **kwargs):
632624 if not still_used_by_other :
633625 Object .objects .filter (pk = old_augmented_id ).update (is_used_by_other_user = False )
634626
627+ # If sound changed, check if the old one is still in use
628+ old_sound_id = getattr (instance , "_old_sound_id" , None )
629+ if old_sound_id and old_sound_id != instance .sound_id :
630+ _recalculate_sound_flags (old_sound_id )
631+
635632
636633@receiver (post_delete , sender = Artwork )
637634def artwork_post_delete (sender , instance , ** kwargs ):
638- """When an artwork is deleted, check if its marker/object are still in use."""
635+ """When an artwork is deleted, check if its marker/object/sound are still in use."""
639636 if not Artwork .objects .filter (marker_id = instance .marker_id ).exists ():
640637 Marker .objects .filter (pk = instance .marker_id ).update (in_use = False , is_used_by_other_user = False )
641638 else :
@@ -649,3 +646,78 @@ def artwork_post_delete(sender, instance, **kwargs):
649646 obj = Object .objects .get (pk = instance .augmented_id )
650647 if not obj .artworks .exclude (author = obj .owner ).exists ():
651648 Object .objects .filter (pk = obj .pk ).update (is_used_by_other_user = False )
649+
650+ if instance .sound_id :
651+ _recalculate_sound_flags (instance .sound_id )
652+
653+
654+ def _recalculate_sound_flags (sound_id ):
655+ """Recalculate in_use and is_used_by_other_user for a Sound."""
656+ try :
657+ sound = Sound .objects .get (pk = sound_id )
658+ except Sound .DoesNotExist :
659+ return
660+
661+ is_in_use = (
662+ sound .artworks .exists ()
663+ or sound .ar_objects .exists ()
664+ or sound .exhibits .exists ()
665+ )
666+ used_by_other = (
667+ sound .artworks .exclude (author = sound .owner ).exists ()
668+ or sound .ar_objects .exclude (owner = sound .owner ).exists ()
669+ or sound .exhibits .exclude (owner = sound .owner ).exists ()
670+ )
671+ Sound .objects .filter (pk = sound_id ).update (
672+ in_use = is_in_use , is_used_by_other_user = used_by_other
673+ )
674+
675+
676+ @receiver (pre_save , sender = Object )
677+ def object_pre_save (sender , instance , ** kwargs ):
678+ """Capture the previous sound before an object is updated."""
679+ if not instance .pk :
680+ return
681+ try :
682+ old = Object .objects .get (pk = instance .pk )
683+ except Object .DoesNotExist :
684+ return
685+ instance ._old_sound_id = old .sound_id
686+
687+
688+ @receiver (post_save , sender = Object )
689+ def object_post_save (sender , instance , ** kwargs ):
690+ """Update sound flags when an object's sound FK changes."""
691+ if instance .sound_id :
692+ sound = Sound .objects .get (pk = instance .sound_id )
693+ updates = {}
694+ if not sound .in_use :
695+ updates ["in_use" ] = True
696+ if instance .owner_id != sound .owner_id and not sound .is_used_by_other_user :
697+ updates ["is_used_by_other_user" ] = True
698+ if updates :
699+ Sound .objects .filter (pk = sound .pk ).update (** updates )
700+
701+ old_sound_id = getattr (instance , "_old_sound_id" , None )
702+ if old_sound_id and old_sound_id != instance .sound_id :
703+ _recalculate_sound_flags (old_sound_id )
704+
705+
706+ @receiver (post_delete , sender = Object )
707+ def object_post_delete_sound (sender , instance , ** kwargs ):
708+ """When an object is deleted, recalculate its sound's flags."""
709+ if instance .sound_id :
710+ _recalculate_sound_flags (instance .sound_id )
711+
712+
713+ @receiver (m2m_changed , sender = Exhibit .sounds .through )
714+ def exhibit_sounds_changed (sender , instance , action , pk_set , ** kwargs ):
715+ """Update sound flags when exhibits add/remove sounds."""
716+ if action in ("post_add" , "post_remove" , "post_clear" ):
717+ if pk_set :
718+ for sound_id in pk_set :
719+ _recalculate_sound_flags (sound_id )
720+ elif action == "post_clear" :
721+ # post_clear doesn't provide pk_set; recalculate all sounds
722+ for sound in Sound .objects .filter (in_use = True ):
723+ _recalculate_sound_flags (sound .pk )
0 commit comments