1212from django .shortcuts import render
1313from django .urls import reverse
1414from django .utils .html import format_html
15+ from django .utils .safestring import mark_safe
1516from modeltranslation .admin import TabbedTranslationAdmin , TranslationStackedInline
1617from ordered_model .admin import OrderedInlineModelAdminMixin , OrderedStackedInline
1718
1819from backend .apps .api .v1 .filters import (
1920 AreaAdministrativeLevelFilter ,
2021 AreaParentFilter ,
21- DatasetOrganizationListFilter ,
2222 OrganizationImageListFilter ,
2323 TableCoverageListFilter ,
2424 TableDirectoryListFilter ,
@@ -611,35 +611,22 @@ class DatasetAdmin(OrderedInlineModelAdminMixin, TabbedTranslationAdmin):
611611 "full_slug" ,
612612 "spatial_coverage" ,
613613 "temporal_coverage" ,
614- "contains_tables" ,
615- "contains_raw_data_sources" ,
616- "contains_information_requests" ,
617- "contains_closed_data" ,
618- "contains_direct_download_free" ,
619- "contains_direct_download_paid" ,
620- "contains_temporalcoverage_free" ,
621- "contains_temporalcoverage_paid" ,
622614 "page_views" ,
623615 "created_at" ,
624616 "updated_at" ,
625- "related_objects" ,
626617 ]
627618 search_fields = ["name" , "slug" , "organizations__name" ]
628619 filter_horizontal = [
629620 "tags" ,
630621 "themes" ,
631622 "organizations" ,
632623 ]
633- list_filter = [
634- DatasetOrganizationListFilter ,
635- ]
636624 list_display = [
637625 "name" ,
638626 "get_organizations" ,
639- "spatial_coverage" ,
640627 "temporal_coverage" ,
641- "related_objects " ,
642- "created_at " ,
628+ "related_tables " ,
629+ "related_raw_data_sources " ,
643630 "updated_at" ,
644631 ]
645632 ordering = ["-updated_at" ]
@@ -650,7 +637,7 @@ def get_organizations(self, obj):
650637
651638 get_organizations .short_description = "Organizations"
652639
653- def related_objects (self , obj ):
640+ def related_tables (self , obj ):
654641 return format_html (
655642 "<a class='related-widget-wrapper-link add-related' "
656643 "href='/admin/v1/table/add/?dataset={0}&_to_field=id&_popup=1'>{1} {2}</a>" ,
@@ -659,7 +646,18 @@ def related_objects(self, obj):
659646 "tables" if obj .tables .count () > 1 else "table" ,
660647 )
661648
662- related_objects .short_description = "Tables"
649+ related_tables .short_description = "Tables"
650+
651+ def related_raw_data_sources (self , obj ):
652+ return format_html (
653+ "<a class='related-widget-wrapper-link add-related' "
654+ "href='/admin/v1/table/add/?dataset={0}&_to_field=id&_popup=1'>{1} {2}</a>" ,
655+ obj .id ,
656+ obj .raw_data_sources .count (),
657+ "sources" if obj .raw_data_sources .count () > 1 else "sources" ,
658+ )
659+
660+ related_raw_data_sources .short_description = "Sources"
663661
664662
665663class CustomUserAdmin (UserAdmin ):
@@ -673,6 +671,31 @@ class CustomUserAdmin(UserAdmin):
673671
674672class TableAdmin (OrderedInlineModelAdminMixin , TabbedTranslationAdmin ):
675673 form = TableForm
674+ fieldsets = (
675+ (
676+ None ,
677+ {
678+ "fields" : (
679+ "dataset" ,
680+ "get_table_url" ,
681+ "status" ,
682+ "name" ,
683+ "slug" ,
684+ "description" ,
685+ "get_datetime_ranges_display" ,
686+ "number_columns" ,
687+ "number_rows" ,
688+ "get_update_display" ,
689+ "raw_data_source" ,
690+ "published_by" ,
691+ "data_cleaned_by" ,
692+ "auxiliary_files_url" ,
693+ "created_at" ,
694+ "updated_at" ,
695+ )
696+ },
697+ ),
698+ )
676699 actions = [
677700 reorder_columns ,
678701 reset_column_order ,
@@ -689,7 +712,8 @@ class TableAdmin(OrderedInlineModelAdminMixin, TabbedTranslationAdmin):
689712 UpdateInline ,
690713 ]
691714 readonly_fields = [
692- "id" ,
715+ "get_table_url" ,
716+ "get_datetime_ranges_display" ,
693717 "partitions" ,
694718 "created_at" ,
695719 "updated_at" ,
@@ -700,21 +724,15 @@ class TableAdmin(OrderedInlineModelAdminMixin, TabbedTranslationAdmin):
700724 "number_columns" ,
701725 "uncompressed_file_size" ,
702726 "compressed_file_size" ,
703- "contains_open_data" ,
704- "contains_direct_download_free" ,
705- "contains_direct_download_paid" ,
706- "contains_temporalcoverage_free" ,
707- "contains_temporalcoverage_paid" ,
708- "contains_closed_data" ,
709727 "page_views" ,
728+ "get_update_display" ,
710729 ]
711730 search_fields = [
712731 "name" ,
713732 "dataset__name" ,
714733 ]
715734 autocomplete_fields = [
716735 "dataset" ,
717- "partner_organization" ,
718736 "published_by" ,
719737 "data_cleaned_by" ,
720738 ]
@@ -757,6 +775,188 @@ def get_data_cleaners(self, obj):
757775
758776 get_data_cleaners .short_description = "Data Cleaners"
759777
778+ def get_table_url (self , obj ):
779+ """Get the clickable URL for the table"""
780+ website_url = f"https://basedosdados.org/dataset/{ obj .dataset .id } ?table={ obj .id } "
781+ website_html = format_html (
782+ '<a href="{}" target="_blank">🖥️ Ver tabela no site</a>' , website_url
783+ )
784+
785+ cloud_tables = obj .cloud_tables .all ()
786+
787+ if len (cloud_tables ) == 0 :
788+ add_cloud_table_url = reverse ("admin:v1_cloudtable_add" ) + f"?table={ obj .id } "
789+ gcp_html = format_html (
790+ 'No cloud table found. <a href="{}">Create here</a>' , add_cloud_table_url
791+ )
792+
793+ elif len (cloud_tables ) > 1 :
794+ cloud_table_tab = reverse ("admin:v1_table_change" ) + "/#cloud-tables-tab"
795+ gcp_html = format_html (
796+ 'More than 1 cloud table found. <a href="{}">Fix it here</a>' , cloud_table_tab
797+ )
798+
799+ else :
800+ cloud_table = cloud_tables [0 ]
801+ gcp_dev_url = f"https://console.cloud.google.com/bigquery?p=basedosdados-dev&d={ cloud_table .gcp_dataset_id } &t={ cloud_table .gcp_table_id } &page=table"
802+ gcp_prod_url = f"https://console.cloud.google.com/bigquery?p=basedosdados&d={ cloud_table .gcp_dataset_id } &t={ cloud_table .gcp_table_id } &page=table"
803+
804+ # Gerando o HTML
805+ gcp_html = format_html (
806+ '<a href="{}" target="_blank">🧩 Ver tabela em BigQuery-dev</a><br>'
807+ '<a href="{}" target="_blank">🧊 Ver tabela em BigQuery-prod</a>' ,
808+ gcp_dev_url ,
809+ gcp_prod_url ,
810+ )
811+
812+ return format_html ("{}<br>{}" , website_html , gcp_html )
813+
814+ get_table_url .short_description = "Table URLs"
815+
816+ def get_datetime_ranges_display (self , obj ):
817+ """Display datetime ranges with links to their admin pages"""
818+ coverages = list (obj .coverages .all ())
819+ links = []
820+
821+ if len (coverages ) == 0 :
822+ add_coverage_url = reverse ("admin:v1_coverage_add" ) + f"?table={ obj .id } "
823+ return format_html ("No coverages found. <a href='{}'>Create here</a>" , add_coverage_url )
824+
825+ for cov in coverages :
826+ url_coverage = reverse ("admin:v1_coverage_change" , args = [cov .id ])
827+ add_date_time_range_url = reverse ("admin:v1_datetimerange_add" ) + f"?coverage={ cov .id } "
828+ status = "Closed" if cov .is_closed else "Open"
829+
830+ if cov .datetime_ranges .count () == 0 :
831+ links .append (
832+ format_html (
833+ "⚠️ <a href='{}'>{} coverage</a> found, but no Datetime Range. <a href='{}'>Create here</a>" ,
834+ add_date_time_range_url ,
835+ status ,
836+ add_date_time_range_url ,
837+ )
838+ )
839+
840+ ranges = sorted (cov .datetime_ranges .all (), key = lambda dt : str (dt ))
841+ for dt_range in ranges :
842+ url_dt_range = reverse ("admin:v1_datetimerange_change" , args = [dt_range .id ])
843+ links .append (
844+ format_html (
845+ '<a href="{}">{}</a> - <a href="{}">{} coverage</a>' ,
846+ url_dt_range ,
847+ str (dt_range ),
848+ url_coverage ,
849+ status ,
850+ )
851+ )
852+
853+ return format_html ("<br>" .join (links ))
854+
855+ get_datetime_ranges_display .short_description = "DateTime Ranges"
856+
857+ def get_update_display (self , obj ):
858+ """Display update info"""
859+
860+ def check_if_there_is_only_one_object_connected (
861+ object_label , attr_label , tab_label , connection_obj
862+ ):
863+ print (f"\t \t \t { object_label = } " )
864+ print (f"\t \t \t { attr_label = } " )
865+ print (f"\t \t \t { tab_label = } " )
866+ print (f"\t \t \t { connection_obj = } " )
867+
868+ campos = [f .name for f in connection_obj ._meta .get_fields ()]
869+ print (f"\t \t \t { campos = } " )
870+
871+ if attr_label not in campos :
872+ print (f"\t \t \t { attr_label } not in { campos } " )
873+ return None , format_html (
874+ "No {0} found. <a href='{1}'>Create one</a>" ,
875+ object_label ,
876+ connection_obj .admin_url ,
877+ )
878+
879+ obj_list = getattr (connection_obj , attr_label ).all ()
880+
881+ print (f"\t \t \t { list (obj_list ) = } " )
882+
883+ connection_label = connection_obj ._meta .model_name
884+
885+ print (f"\t \t \t { connection_label = } " )
886+
887+ change_url = connection_obj .admin_url + ("#" + tab_label if tab_label else "" )
888+
889+ print (f"\t \t \t { change_url = } " )
890+
891+ # Se não houver objetos
892+ if len (obj_list ) == 0 :
893+ return None , format_html (
894+ "No {0} found. <a href='{1}'>Create one</a>" , object_label , change_url
895+ )
896+
897+ # Se houver mais de 1 objeto
898+ elif len (obj_list ) > 1 :
899+ return None , format_html (
900+ "More than 1 {0} found. <a href='{1}'>Fix it</a>" , object_label , change_url
901+ )
902+
903+ # Se houver exatamente 1 objeto
904+ else :
905+ selected_obj = obj_list [0 ]
906+ html = format_html (
907+ "<a href='{}'>{}</a> {} found in <a href='{}'>{}</a> " ,
908+ selected_obj .admin_url ,
909+ str (selected_obj ),
910+ object_label ,
911+ change_url ,
912+ connection_label ,
913+ )
914+ return obj_list [0 ], html
915+
916+ def check_if_there_is_only_one_raw_data_source_connected (table_object ):
917+ obj_list = getattr (table_object , "raw_data_source" ).all ()
918+ if len (obj_list ) == 0 :
919+ return None , format_html ("No Raw Data Source found. Add one in the box bellow" )
920+
921+ # Se houver mais de 1 objeto
922+ elif len (obj_list ) > 1 :
923+ return None , format_html (
924+ "More than 1 Raw Data Source found. Fix one in the box bellow"
925+ )
926+
927+ # Se houver exatamente 1 objeto
928+ else :
929+ selected_obj = obj_list [0 ]
930+ html = format_html (
931+ "<a href='{}'>Raw Data Source</a> found" ,
932+ selected_obj .admin_url ,
933+ str (selected_obj ),
934+ )
935+ return obj_list [0 ], html
936+
937+ _ , update_html = check_if_there_is_only_one_object_connected (
938+ "update" , "updates" , "updates-tab" , obj
939+ )
940+ (
941+ raw_data_source_obj ,
942+ raw_data_source_html ,
943+ ) = check_if_there_is_only_one_raw_data_source_connected (obj )
944+ print (f"{ raw_data_source_obj .admin_url = } " )
945+ if raw_data_source_obj :
946+ _ , raw_data_source_update_html = check_if_there_is_only_one_object_connected (
947+ "update" , "updates" , "updates-tab" , raw_data_source_obj
948+ )
949+ print (f"{ raw_data_source_update_html = } " )
950+ _ , poll_raw_data_source_html = check_if_there_is_only_one_object_connected (
951+ "poll" , "polls" , "polls-tab" , raw_data_source_obj
952+ )
953+
954+ raw_data_source_html = format_html (
955+ raw_data_source_update_html + "<br>" + poll_raw_data_source_html
956+ )
957+
958+ return format_html (update_html + "<br>" + raw_data_source_html )
959+
760960
761961class TableNeighborAdmin (admin .ModelAdmin ):
762962 search_fields = [
@@ -1054,9 +1254,9 @@ def datetime_ranges_display(self, obj):
10541254
10551255 # Add link to add new datetime range
10561256 add_url = reverse ("admin:v1_datetimerange_add" ) + f"?coverage={ obj .id } "
1057- links .append (format_html ( '<a class="addlink" href="{}">Add DateTime Range</a>' , add_url ))
1257+ links .append (mark_safe ( f '<a class="addlink" href="{ add_url } ">Add DateTime Range</a>' ))
10581258
1059- return format_html ("<br>" .join (links ))
1259+ return mark_safe ("<br>" .join (links ))
10601260
10611261 datetime_ranges_display .short_description = "DateTime Ranges"
10621262
0 commit comments