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 django_admin_inline_paginator_plus .admin import StackedInlinePaginated
1617from modeltranslation .admin import TabbedTranslationAdmin , TranslationStackedInline
1718from ordered_model .admin import OrderedInlineModelAdminMixin , OrderedStackedInline
1819
1920from backend .apps .api .v1 .filters import (
2021 AreaAdministrativeLevelFilter ,
2122 AreaParentFilter ,
22- DatasetOrganizationListFilter ,
2323 OrganizationImageListFilter ,
24- TableCoverageListFilter ,
25- TableDirectoryListFilter ,
26- TableObservationListFilter ,
27- TableOrganizationListFilter ,
2824)
2925from backend .apps .api .v1 .forms import (
3026 ColumnInlineForm ,
@@ -617,35 +613,22 @@ class DatasetAdmin(OrderedInlineModelAdminMixin, TabbedTranslationAdmin):
617613 "full_slug" ,
618614 "spatial_coverage" ,
619615 "temporal_coverage" ,
620- "contains_tables" ,
621- "contains_raw_data_sources" ,
622- "contains_information_requests" ,
623- "contains_closed_data" ,
624- "contains_direct_download_free" ,
625- "contains_direct_download_paid" ,
626- "contains_temporalcoverage_free" ,
627- "contains_temporalcoverage_paid" ,
628616 "page_views" ,
629617 "created_at" ,
630618 "updated_at" ,
631- "related_objects" ,
632619 ]
633620 search_fields = ["name" , "slug" , "organizations__name" ]
634621 filter_horizontal = [
635622 "tags" ,
636623 "themes" ,
637624 "organizations" ,
638625 ]
639- list_filter = [
640- DatasetOrganizationListFilter ,
641- ]
642626 list_display = [
643627 "name" ,
644628 "get_organizations" ,
645- "spatial_coverage" ,
646629 "temporal_coverage" ,
647- "related_objects " ,
648- "created_at " ,
630+ "related_tables " ,
631+ "related_raw_data_sources " ,
649632 "updated_at" ,
650633 ]
651634 ordering = ["-updated_at" ]
@@ -656,7 +639,7 @@ def get_organizations(self, obj):
656639
657640 get_organizations .short_description = "Organizations"
658641
659- def related_objects (self , obj ):
642+ def related_tables (self , obj ):
660643 return format_html (
661644 "<a class='related-widget-wrapper-link add-related' "
662645 "href='/admin/v1/table/add/?dataset={0}&_to_field=id&_popup=1'>{1} {2}</a>" ,
@@ -665,7 +648,18 @@ def related_objects(self, obj):
665648 "tables" if obj .tables .count () > 1 else "table" ,
666649 )
667650
668- related_objects .short_description = "Tables"
651+ related_tables .short_description = "Tables"
652+
653+ def related_raw_data_sources (self , obj ):
654+ return format_html (
655+ "<a class='related-widget-wrapper-link add-related' "
656+ "href='/admin/v1/table/add/?dataset={0}&_to_field=id&_popup=1'>{1} {2}</a>" ,
657+ obj .id ,
658+ obj .raw_data_sources .count (),
659+ "sources" if obj .raw_data_sources .count () > 1 else "sources" ,
660+ )
661+
662+ related_raw_data_sources .short_description = "Sources"
669663
670664
671665class CustomUserAdmin (UserAdmin ):
@@ -679,6 +673,31 @@ class CustomUserAdmin(UserAdmin):
679673
680674class TableAdmin (OrderedInlineModelAdminMixin , TabbedTranslationAdmin ):
681675 form = TableForm
676+ fieldsets = (
677+ (
678+ None ,
679+ {
680+ "fields" : (
681+ "dataset" ,
682+ "get_table_url" ,
683+ "status" ,
684+ "name" ,
685+ "slug" ,
686+ "description" ,
687+ "get_datetime_ranges_display" ,
688+ "number_columns" ,
689+ "number_rows" ,
690+ "get_update_display" ,
691+ "raw_data_source" ,
692+ "published_by" ,
693+ "data_cleaned_by" ,
694+ "auxiliary_files_url" ,
695+ "created_at" ,
696+ "updated_at" ,
697+ )
698+ },
699+ ),
700+ )
682701 actions = [
683702 reorder_columns ,
684703 reset_column_order ,
@@ -695,7 +714,8 @@ class TableAdmin(OrderedInlineModelAdminMixin, TabbedTranslationAdmin):
695714 UpdateInline ,
696715 ]
697716 readonly_fields = [
698- "id" ,
717+ "get_table_url" ,
718+ "get_datetime_ranges_display" ,
699719 "partitions" ,
700720 "created_at" ,
701721 "updated_at" ,
@@ -706,21 +726,15 @@ class TableAdmin(OrderedInlineModelAdminMixin, TabbedTranslationAdmin):
706726 "number_columns" ,
707727 "uncompressed_file_size" ,
708728 "compressed_file_size" ,
709- "contains_open_data" ,
710- "contains_direct_download_free" ,
711- "contains_direct_download_paid" ,
712- "contains_temporalcoverage_free" ,
713- "contains_temporalcoverage_paid" ,
714- "contains_closed_data" ,
715729 "page_views" ,
730+ "get_update_display" ,
716731 ]
717732 search_fields = [
718733 "name" ,
719734 "dataset__name" ,
720735 ]
721736 autocomplete_fields = [
722737 "dataset" ,
723- "partner_organization" ,
724738 "published_by" ,
725739 "data_cleaned_by" ,
726740 ]
@@ -730,17 +744,9 @@ class TableAdmin(OrderedInlineModelAdminMixin, TabbedTranslationAdmin):
730744 list_display = [
731745 "name" ,
732746 "dataset" ,
733- "get_publishers" ,
734- "get_data_cleaners" ,
735747 "created_at" ,
736748 "updated_at" ,
737749 ]
738- list_filter = [
739- TableOrganizationListFilter ,
740- TableCoverageListFilter ,
741- TableObservationListFilter ,
742- TableDirectoryListFilter ,
743- ]
744750 ordering = ["-updated_at" ]
745751
746752 def get_queryset (self , request ):
@@ -763,6 +769,176 @@ def get_data_cleaners(self, obj):
763769
764770 get_data_cleaners .short_description = "Data Cleaners"
765771
772+ def get_table_url (self , obj ):
773+ """Get the clickable URL for the table"""
774+ website_url = f"https://basedosdados.org/dataset/{ obj .dataset .id } ?table={ obj .id } "
775+ website_html = format_html (
776+ '<a href="{}" target="_blank">🖥️ Ver tabela no site</a>' , website_url
777+ )
778+
779+ cloud_tables = obj .cloud_tables .all ()
780+
781+ if len (cloud_tables ) == 0 :
782+ add_cloud_table_url = reverse ("admin:v1_cloudtable_add" ) + f"?table={ obj .id } "
783+ gcp_html = format_html (
784+ 'No cloud table found. <a href="{}">Create here</a>' , add_cloud_table_url
785+ )
786+
787+ elif len (cloud_tables ) > 1 :
788+ cloud_table_tab = reverse ("admin:v1_table_change" ) + "/#cloud-tables-tab"
789+ gcp_html = format_html (
790+ 'More than 1 cloud table found. <a href="{}">Fix it here</a>' , cloud_table_tab
791+ )
792+
793+ else :
794+ cloud_table = cloud_tables [0 ]
795+ 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"
796+ 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"
797+
798+ # Gerando o HTML
799+ gcp_html = format_html (
800+ '<a href="{}" target="_blank">🧩 Ver tabela em BigQuery-dev</a><br>'
801+ '<a href="{}" target="_blank">🧊 Ver tabela em BigQuery-prod</a>' ,
802+ gcp_dev_url ,
803+ gcp_prod_url ,
804+ )
805+
806+ return format_html ("{}<br>{}" , website_html , gcp_html )
807+
808+ get_table_url .short_description = "Table URLs"
809+
810+ def get_datetime_ranges_display (self , obj ):
811+ """Display datetime ranges with links to their admin pages"""
812+ coverages = list (obj .coverages .all ())
813+ links = []
814+
815+ if len (coverages ) == 0 :
816+ add_coverage_url = reverse ("admin:v1_coverage_add" ) + f"?table={ obj .id } "
817+ return format_html ("No coverages found. <a href='{}'>Create here</a>" , add_coverage_url )
818+
819+ for cov in coverages :
820+ url_coverage = cov .admin_url
821+ add_date_time_range_url = reverse ("admin:v1_datetimerange_add" ) + f"?coverage={ cov .id } "
822+ status = "Closed" if cov .is_closed else "Open"
823+
824+ if cov .datetime_ranges .count () == 0 :
825+ links .append (
826+ format_html (
827+ "⚠️ <a href='{}'>{} coverage</a> found, but no Datetime Range."
828+ "<a href='{}'>Create here</a>" ,
829+ add_date_time_range_url ,
830+ status ,
831+ add_date_time_range_url ,
832+ )
833+ )
834+
835+ ranges = sorted (cov .datetime_ranges .all (), key = lambda dt : str (dt ))
836+ for dt_range in ranges :
837+ url_dt_range = reverse ("admin:v1_datetimerange_change" , args = [dt_range .id ])
838+ links .append (
839+ format_html (
840+ '<a href="{}">{}</a> - <a href="{}">{} coverage</a>' ,
841+ url_dt_range ,
842+ str (dt_range ),
843+ url_coverage ,
844+ status ,
845+ )
846+ )
847+
848+ return format_html ("<br>" .join (links ))
849+
850+ get_datetime_ranges_display .short_description = "DateTime Ranges"
851+
852+ def get_update_display (self , table_obj ):
853+ """Display update info"""
854+
855+ def check_if_there_is_only_one_object_connected (attr_label , connection_obj ):
856+ campos = [f .name for f in connection_obj ._meta .get_fields ()]
857+
858+ if attr_label not in campos :
859+ return format_html (
860+ "The {} label was not found in {} model" ,
861+ attr_label ,
862+ connection_obj ._meta .verbose_name ,
863+ )
864+
865+ obj_list = getattr (connection_obj , attr_label ).all ()
866+ change_url = connection_obj .admin_url + "#" + attr_label + "-tab"
867+
868+ # Se não houver objetos
869+ if len (obj_list ) == 0 :
870+ return format_html (
871+ "No {} found in {}. <a href='{}'>Create one</a>" ,
872+ attr_label ,
873+ connection_obj ._meta .verbose_name ,
874+ change_url ,
875+ )
876+
877+ # Se houver mais de 1 objeto
878+ elif len (obj_list ) > 1 :
879+ return format_html (
880+ "More than 1 {} found in {}. <a href='{}'>Fix it</a>" ,
881+ obj_list [0 ]._meta .verbose_name ,
882+ connection_obj ._meta .verbose_name ,
883+ change_url ,
884+ )
885+
886+ # Se houver exatamente 1 objeto
887+ else :
888+ selected_obj = obj_list [0 ]
889+ html = format_html (
890+ "<a href='{}'>{}</a> {} found in <a href='{}'>{}</a> " ,
891+ selected_obj .admin_url ,
892+ str (selected_obj ),
893+ selected_obj ._meta .verbose_name ,
894+ change_url ,
895+ connection_obj ._meta .verbose_name ,
896+ )
897+ return html
898+
899+ def check_if_there_is_only_one_raw_data_source_connected (table_object ):
900+ """Specific function to check Raw Data Source
901+ the instructions and conditionals are different from updates and polls"""
902+ raw_data_source_obj_list = getattr (table_object , "raw_data_source" ).all ()
903+ if len (raw_data_source_obj_list ) == 0 :
904+ return None , format_html ("No Raw Data Source found. Add one in the box bellow" )
905+
906+ elif len (raw_data_source_obj_list ) > 1 :
907+ return None , format_html ("More than 1 Raw Data Source found. Fix in the box bellow" )
908+
909+ else :
910+ selected_obj = raw_data_source_obj_list [0 ]
911+ html = format_html (
912+ "<a href='{}'>Raw Data Source</a> found" ,
913+ selected_obj .admin_url ,
914+ str (selected_obj ),
915+ )
916+ return raw_data_source_obj_list [0 ], html
917+
918+ update_html = check_if_there_is_only_one_object_connected ("updates" , table_obj )
919+
920+ (
921+ raw_data_source_obj ,
922+ raw_data_source_html ,
923+ ) = check_if_there_is_only_one_raw_data_source_connected (table_obj )
924+
925+ if raw_data_source_obj :
926+ raw_data_source_update_html = check_if_there_is_only_one_object_connected (
927+ "updates" , raw_data_source_obj
928+ )
929+ print (f"{ raw_data_source_update_html = } " )
930+ poll_raw_data_source_html = check_if_there_is_only_one_object_connected (
931+ "polls" , raw_data_source_obj
932+ )
933+
934+ raw_data_source_html = format_html (
935+ raw_data_source_update_html + "<br>" + poll_raw_data_source_html
936+ )
937+
938+ return format_html (update_html + "<br>" + raw_data_source_html )
939+
940+ get_update_display .short_description = "Update and Poll Info"
941+
766942
767943class TableNeighborAdmin (admin .ModelAdmin ):
768944 search_fields = [
@@ -1060,9 +1236,9 @@ def datetime_ranges_display(self, obj):
10601236
10611237 # Add link to add new datetime range
10621238 add_url = reverse ("admin:v1_datetimerange_add" ) + f"?coverage={ obj .id } "
1063- links .append (format_html ( '<a class="addlink" href="{}">Add DateTime Range</a>' , add_url ))
1239+ links .append (mark_safe ( f '<a class="addlink" href="{ add_url } ">Add DateTime Range</a>' ))
10641240
1065- return format_html ("<br>" .join (links ))
1241+ return mark_safe ("<br>" .join (links ))
10661242
10671243 datetime_ranges_display .short_description = "DateTime Ranges"
10681244
0 commit comments