@@ -959,6 +959,11 @@ class BaseDataSource(Analytics):
959
959
allow_updates : bool = attr_field ()
960
960
default_data_type : Optional [str ] = attr_field ()
961
961
defaults : JSON = attr_field ()
962
+ auto_update_enabled : auto
963
+ auto_import_enabled : auto
964
+ remote_name : Optional [str ] = fn_field ()
965
+ remote_url : Optional [str ] = fn_field ()
966
+ healthcheck : bool = fn_field ()
962
967
963
968
field_definitions : Optional [List [FieldDefinition ]] = strawberry_django .field (
964
969
resolver = lambda self : self .field_definitions ()
@@ -1063,11 +1068,6 @@ class ExternalDataSource(BaseDataSource):
1063
1068
strawberry_django_dataloaders .fields .auto_dataloader_field ()
1064
1069
)
1065
1070
update_mapping : Optional [List ["AutoUpdateConfig" ]]
1066
- auto_update_enabled : auto
1067
- auto_import_enabled : auto
1068
- remote_name : Optional [str ] = fn_field ()
1069
- remote_url : Optional [str ] = fn_field ()
1070
- healthcheck : bool = fn_field ()
1071
1071
orgs_with_access : List [Organisation ]
1072
1072
1073
1073
@strawberry_django .field
@@ -1222,6 +1222,7 @@ def get_queryset(cls, queryset, info, **kwargs):
1222
1222
@strawberry .enum
1223
1223
class InspectorDisplayType (Enum ):
1224
1224
BigNumber = "BigNumber"
1225
+ BigRecord = "BigRecord"
1225
1226
ElectionResult = "ElectionResult"
1226
1227
List = "List"
1227
1228
Properties = "Properties"
@@ -1239,9 +1240,7 @@ class MapLayer:
1239
1240
icon_image : Optional [str ] = dict_key_field ()
1240
1241
mapbox_paint : Optional [JSON ] = dict_key_field ()
1241
1242
mapbox_layout : Optional [JSON ] = dict_key_field ()
1242
- inspector_type : Optional [InspectorDisplayType ] = dict_key_field (
1243
- default = InspectorDisplayType .Table
1244
- )
1243
+ inspector_type : Optional [str ] = dict_key_field (default = InspectorDisplayType .Table )
1245
1244
inspector_config : Optional [JSON ] = dict_key_field ()
1246
1245
1247
1246
@strawberry_django .field
@@ -1679,6 +1678,12 @@ class MapBounds:
1679
1678
south : float
1680
1679
west : float
1681
1680
1681
+ @strawberry .enum
1682
+ class ChoroplethMode (Enum ):
1683
+ Count = "Count"
1684
+ Field = "Field"
1685
+ Formula = "Formula"
1686
+
1682
1687
1683
1688
@strawberry_django .field ()
1684
1689
def choropleth_data_for_source (
@@ -1687,8 +1692,10 @@ def choropleth_data_for_source(
1687
1692
analytical_area_key : AnalyticalAreaType ,
1688
1693
# Field could be a column name or a Pandas formulaic expression
1689
1694
# or, if not provided, a count of records
1695
+ mode : Optional [ChoroplethMode ] = ChoroplethMode .Count ,
1690
1696
field : Optional [str ] = None ,
1691
1697
map_bounds : Optional [MapBounds ] = None ,
1698
+ formula : Optional [str ] = None ,
1692
1699
) -> List [GroupedDataCount ]:
1693
1700
# Check user can access the external data source
1694
1701
user = get_current_user (info )
@@ -1728,7 +1735,7 @@ def choropleth_data_for_source(
1728
1735
)
1729
1736
combined_area = areas .aggregate (union = GisUnion ("polygon" ))["union" ]
1730
1737
# all geocoded GenericData should have `point` set
1731
- qs = qs .filter (point__within = combined_area )
1738
+ qs = qs .filter (point__within = combined_area or bbox )
1732
1739
1733
1740
qs = qs .values ("json" , "label" , "gss" )
1734
1741
@@ -1753,12 +1760,47 @@ def choropleth_data_for_source(
1753
1760
# TODO: maybe make this explicit via an argument?
1754
1761
# is_data_source_statistical = external_data_source.data_type == models.ExternalDataSource.DataSourceType.AREA_STATS
1755
1762
# check that field is in DF
1756
- field_is_set = field and field is not None and len (field )
1757
- is_explicit_row_count = field_is_set and field == "__COUNT__"
1758
- is_valid_statistical_field = field_is_set and not is_explicit_row_count
1759
- is_valid_row_counter = is_explicit_row_count or not field_is_set
1763
+ is_valid_field = field and field is not None and len (field ) and field in df .columns
1764
+ is_row_count = mode is ChoroplethMode .Count
1765
+ is_valid_formula = formula and formula is not None and len (formula )
1766
+
1767
+ if mode is ChoroplethMode .Field and not is_valid_field :
1768
+ raise ValueError ("Field not found in data source" )
1769
+
1770
+ if mode is ChoroplethMode .Formula and not is_valid_formula :
1771
+ raise ValueError ("Formula is invalid" )
1760
1772
1761
- if is_valid_statistical_field :
1773
+ if is_row_count :
1774
+ # Simple count of data points per area
1775
+
1776
+ # Count the number of rows per GSS
1777
+ df_count = (
1778
+ df .drop (columns = ["label" ]).groupby ("gss" ).size ().reset_index (name = "count" )
1779
+ )
1780
+
1781
+ # Calculate the mode for the 'label' column
1782
+ def get_mode (series ):
1783
+ try :
1784
+ return series .mode ()[0 ]
1785
+ except KeyError :
1786
+ return None
1787
+
1788
+ df_mode = df .groupby ("gss" )["label" ].agg (get_mode ).reset_index ()
1789
+
1790
+ # Merge the summed DataFrame with the mode DataFrame
1791
+ df = pd .merge (df_count , df_mode , on = "gss" )
1792
+
1793
+ # Convert DF to GroupedDataCount(label=label, gss=gss, count=count) list
1794
+ return [
1795
+ GroupedDataCount (
1796
+ label = row .label ,
1797
+ gss = row .gss ,
1798
+ count = row .count ,
1799
+ formatted_count = f"{ row .count :,.0f} " ,
1800
+ )
1801
+ for row in df .itertuples ()
1802
+ ]
1803
+ elif is_valid_field or is_valid_formula :
1762
1804
# Convert any stringified JSON numbers to floats
1763
1805
for column in df :
1764
1806
if all (df [column ].apply (check_numeric )):
@@ -1818,16 +1860,16 @@ def get_mode(series):
1818
1860
1819
1861
# Now fetch the requested series from the dataframe
1820
1862
# If the field is a column name, we can just return that column
1821
- if field in df . columns :
1863
+ if is_valid_field :
1822
1864
df ["count" ] = df [field ]
1823
1865
# If the field is a formula, we need to evaluate it
1824
- else :
1866
+ elif is_valid_formula :
1825
1867
try :
1826
- df ["count" ] = df .eval (field )
1868
+ df ["count" ] = df .eval (formula )
1827
1869
except ValueError :
1828
1870
# In case "where" is used, which pandas doesn't support
1829
1871
# https://github.com/pandas-dev/pandas/issues/34834
1830
- df ["count" ] = ne .evaluate (field , local_dict = df )
1872
+ df ["count" ] = ne .evaluate (formula , local_dict = df )
1831
1873
1832
1874
# Check if count is between 0 and 1: if so, it's a percentage
1833
1875
is_percentage = df ["count" ].between (0 , 2 ).all () or False
@@ -1852,35 +1894,6 @@ def get_mode(series):
1852
1894
)
1853
1895
for row in df .itertuples ()
1854
1896
]
1855
- elif is_valid_row_counter :
1856
- # Simple count of data points per area
1857
-
1858
- # Count the number of rows per GSS
1859
- df_count = (
1860
- df .drop (columns = ["label" ]).groupby ("gss" ).size ().reset_index (name = "count" )
1861
- )
1862
-
1863
- # Calculate the mode for the 'label' column
1864
- def get_mode (series ):
1865
- try :
1866
- return series .mode ()[0 ]
1867
- except KeyError :
1868
- return None
1869
-
1870
- df_mode = df .groupby ("gss" )["label" ].agg (get_mode ).reset_index ()
1871
-
1872
- # Merge the summed DataFrame with the mode DataFrame
1873
- df = pd .merge (df_count , df_mode , on = "gss" )
1874
-
1875
- # Convert DF to GroupedDataCount(label=label, gss=gss, count=count) list
1876
- return [
1877
- GroupedDataCount (
1878
- label = row .label ,
1879
- gss = row .gss ,
1880
- count = row .count ,
1881
- formatted_count = f"{ row .count :,.0f} " ,
1882
- )
1883
- for row in df .itertuples ()
1884
- ]
1897
+
1885
1898
else :
1886
1899
raise ValueError ("Incorrect configuration for choropleth" )
0 commit comments