1
1
from django .contrib .contenttypes .admin import GenericTabularInline
2
+ from django .contrib .contenttypes .models import ContentType
2
3
from ordered_model .admin import OrderedModelAdmin
3
- from polymorphic .admin import PolymorphicInlineSupportMixin , StackedPolymorphicInline
4
+ from polymorphic .admin import PolymorphicInlineSupportMixin , StackedPolymorphicInline , PolymorphicParentModelAdmin , \
5
+ PolymorphicChildModelAdmin
4
6
5
7
from django .db .models import Subquery
6
8
from django .template import Context , Template
13
15
14
16
from mailing .admin import BaseEmailTemplateAdmin
15
17
from sponsors .models import *
18
+ from sponsors .models .benefits import RequiredAssetMixin
16
19
from sponsors import views_admin
17
20
from sponsors .forms import SponsorshipReviewAdminForm , SponsorBenefitAdminInlineForm , RequiredImgAssetConfigurationForm , \
18
21
SponsorshipBenefitAdminForm
@@ -26,14 +29,16 @@ class AssetsInline(GenericTabularInline):
26
29
has_delete_permission = lambda self , request , obj : False
27
30
readonly_fields = ["internal_name" , "user_submitted_info" , "value" ]
28
31
29
- def value (self , request , obj = None ):
32
+ def value (self , obj = None ):
30
33
if not obj or not obj .value :
31
34
return ""
32
35
return obj .value
36
+
33
37
value .short_description = "Submitted information"
34
38
35
- def user_submitted_info (self , request , obj = None ):
36
- return bool (self .value (request , obj ))
39
+ def user_submitted_info (self , obj = None ):
40
+ return bool (self .value (obj ))
41
+
37
42
user_submitted_info .short_description = "Fullfilled data?"
38
43
user_submitted_info .boolean = True
39
44
@@ -348,6 +353,7 @@ def get_queryset(self, *args, **kwargs):
348
353
349
354
def send_notifications (self , request , queryset ):
350
355
return views_admin .send_sponsorship_notifications_action (self , request , queryset )
356
+
351
357
send_notifications .short_description = 'Send notifications to selected'
352
358
353
359
def get_readonly_fields (self , request , obj ):
@@ -431,6 +437,11 @@ def get_urls(self):
431
437
self .admin_site .admin_view (self .rollback_to_editing_view ),
432
438
name = "sponsors_sponsorship_rollback_to_edit" ,
433
439
),
440
+ path (
441
+ "<int:pk>/list-assets" ,
442
+ self .admin_site .admin_view (self .list_uploaded_assets_view ),
443
+ name = "sponsors_sponsorship_list_uploaded_assets" ,
444
+ ),
434
445
]
435
446
return my_urls + urls
436
447
@@ -551,6 +562,9 @@ def approve_sponsorship_view(self, request, pk):
551
562
def approve_signed_sponsorship_view (self , request , pk ):
552
563
return views_admin .approve_signed_sponsorship_view (self , request , pk )
553
564
565
+ def list_uploaded_assets_view (self , request , pk ):
566
+ return views_admin .list_uploaded_assets (self , request , pk )
567
+
554
568
555
569
@admin .register (LegalClause )
556
570
class LegalClauseModelAdmin (OrderedModelAdmin ):
@@ -714,9 +728,178 @@ def nullify_contract_view(self, request, pk):
714
728
715
729
@admin .register (SponsorEmailNotificationTemplate )
716
730
class SponsorEmailNotificationTemplateAdmin (BaseEmailTemplateAdmin ):
731
+
717
732
def get_form (self , request , obj = None , ** kwargs ):
718
733
help_texts = {
719
734
"content" : SPONSOR_TEMPLATE_HELP_TEXT ,
720
735
}
721
736
kwargs .update ({"help_texts" : help_texts })
722
737
return super ().get_form (request , obj , ** kwargs )
738
+
739
+
740
+ class AssetTypeListFilter (admin .SimpleListFilter ):
741
+ title = "Asset Type"
742
+ parameter_name = 'type'
743
+
744
+ @property
745
+ def assets_types_mapping (self ):
746
+ return {asset_type .__name__ : asset_type for asset_type in GenericAsset .all_asset_types ()}
747
+
748
+ def lookups (self , request , model_admin ):
749
+ return [(k , v ._meta .verbose_name_plural ) for k , v in self .assets_types_mapping .items ()]
750
+
751
+ def queryset (self , request , queryset ):
752
+ asset_type = self .assets_types_mapping .get (self .value ())
753
+ if not asset_type :
754
+ return queryset
755
+ return queryset .instance_of (asset_type )
756
+
757
+
758
+ class AssociatedBenefitListFilter (admin .SimpleListFilter ):
759
+ title = "From Benefit Which Requires Asset"
760
+ parameter_name = 'from_benefit'
761
+
762
+ @property
763
+ def benefits_with_assets (self ):
764
+ qs = BenefitFeature .objects .required_assets ().values_list ("sponsor_benefit__sponsorship_benefit" ,
765
+ flat = True ).distinct ()
766
+ benefits = SponsorshipBenefit .objects .filter (id__in = Subquery (qs ))
767
+ return {str (b .id ): b for b in benefits }
768
+
769
+ def lookups (self , request , model_admin ):
770
+ return [(k , b .name ) for k , b in self .benefits_with_assets .items ()]
771
+
772
+ def queryset (self , request , queryset ):
773
+ benefit = self .benefits_with_assets .get (self .value ())
774
+ if not benefit :
775
+ return queryset
776
+ internal_names = [
777
+ cfg .internal_name
778
+ for cfg in benefit .features_config .all ()
779
+ if hasattr (cfg , "internal_name" )
780
+ ]
781
+ return queryset .filter (internal_name__in = internal_names )
782
+
783
+
784
+ class AssetContentTypeFilter (admin .SimpleListFilter ):
785
+ title = "Related Object"
786
+ parameter_name = 'content_type'
787
+
788
+ def lookups (self , request , model_admin ):
789
+ qs = ContentType .objects .filter (model__in = ["sponsorship" , "sponsor" ])
790
+ return [(c_type .pk , c_type .model .title ()) for c_type in qs ]
791
+
792
+ def queryset (self , request , queryset ):
793
+ value = self .value ()
794
+ if not value :
795
+ return queryset
796
+ return queryset .filter (content_type = value )
797
+
798
+
799
+ class AssetWithOrWithoutValueFilter (admin .SimpleListFilter ):
800
+ title = "Value"
801
+ parameter_name = "value"
802
+
803
+ def lookups (self , request , model_admin ):
804
+ return [
805
+ ("with-value" , "With value" ),
806
+ ("no-value" , "Without value" ),
807
+ ]
808
+
809
+ def queryset (self , request , queryset ):
810
+ value = self .value ()
811
+ if not value :
812
+ return queryset
813
+ with_value_id = [asset .pk for asset in queryset if asset .value ]
814
+ if value == "with-value" :
815
+ return queryset .filter (pk__in = with_value_id )
816
+ else :
817
+ return queryset .exclude (pk__in = with_value_id )
818
+
819
+
820
+ @admin .register (GenericAsset )
821
+ class GenericAssetModelAdmin (PolymorphicParentModelAdmin ):
822
+ list_display = ["id" , "internal_name" , "get_value" , "content_type" , "get_related_object" ]
823
+ list_filter = [AssetContentTypeFilter , AssetTypeListFilter , AssetWithOrWithoutValueFilter ,
824
+ AssociatedBenefitListFilter ]
825
+ actions = ["export_assets_as_zipfile" ]
826
+
827
+ def get_child_models (self , * args , ** kwargs ):
828
+ return GenericAsset .all_asset_types ()
829
+
830
+ def get_queryset (self , * args , ** kwargs ):
831
+ classes = self .get_child_models (* args , ** kwargs )
832
+ return self .model .objects .select_related ("content_type" ).instance_of (* classes )
833
+
834
+ def has_delete_permission (self , * args , ** kwargs ):
835
+ return False
836
+
837
+ def has_add_permission (self , * args , ** kwargs ):
838
+ return False
839
+
840
+ @cached_property
841
+ def all_sponsors (self ):
842
+ qs = Sponsor .objects .all ()
843
+ return {sp .id : sp for sp in qs }
844
+
845
+ @cached_property
846
+ def all_sponsorships (self ):
847
+ qs = Sponsorship .objects .all ().select_related ("package" , "sponsor" )
848
+ return {sp .id : sp for sp in qs }
849
+
850
+ def get_value (self , obj ):
851
+ html = obj .value
852
+ if obj .value and getattr (obj .value , "url" , None ):
853
+ html = f"<a href='{ obj .value .url } ' target='_blank'>{ obj .value } </a>"
854
+ return mark_safe (html )
855
+
856
+ get_value .short_description = "Value"
857
+
858
+ def get_related_object (self , obj ):
859
+ """
860
+ Returns the content_object as an URL and performs better because
861
+ of sponsors and sponsorship cached properties
862
+ """
863
+ content_object = None
864
+ if obj .from_sponsorship :
865
+ content_object = self .all_sponsorships [obj .object_id ]
866
+ elif obj .from_sponsor :
867
+ content_object = self .all_sponsors [obj .object_id ]
868
+
869
+ if not content_object : # safety belt
870
+ return obj .content_object
871
+
872
+ html = f"<a href='{ content_object .admin_url } ' target='_blank'>{ content_object } </a>"
873
+ return mark_safe (html )
874
+
875
+ get_related_object .short_description = "Associated with"
876
+
877
+ def export_assets_as_zipfile (self , request , queryset ):
878
+ return views_admin .export_assets_as_zipfile (self , request , queryset )
879
+ export_assets_as_zipfile .short_description = "Export selected"
880
+
881
+
882
+ class GenericAssetChildModelAdmin (PolymorphicChildModelAdmin ):
883
+ """ Base admin class for all GenericAsset child models """
884
+ base_model = GenericAsset
885
+ readonly_fields = ["uuid" , "content_type" , "object_id" , "content_object" , "internal_name" ]
886
+
887
+
888
+ @admin .register (TextAsset )
889
+ class TextAssetModelAdmin (GenericAssetChildModelAdmin ):
890
+ base_model = TextAsset
891
+
892
+
893
+ @admin .register (ImgAsset )
894
+ class ImgAssetModelAdmin (GenericAssetChildModelAdmin ):
895
+ base_model = ImgAsset
896
+
897
+
898
+ @admin .register (FileAsset )
899
+ class ImgAssetModelAdmin (GenericAssetChildModelAdmin ):
900
+ base_model = FileAsset
901
+
902
+
903
+ @admin .register (ResponseAsset )
904
+ class ResponseAssetModelAdmin (GenericAssetChildModelAdmin ):
905
+ base_model = ResponseAsset
0 commit comments