Changelog#
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]#
[0.8.0] - 2026-10-21#
Added#
SGUClient(transport=...)sends requests through a transport of your own, for example canned responses in your tests.Transport,TransportResponseand the defaultRequestsTransportare exported fromsgu_client(#59)FeatureCollectionandTimeSeriesCollection, the shared bases of the six collection models, andFeature, the shared base of their features, exported fromsgu_client.models.TimeSeriesCollection(measurements, modeled levels, analysis results) adds sorting by date andto_series()(#60)The pandas user guide has a table of how the collections’ DataFrames differ (#60)
Changed#
ModeledArea.geometrymay beNone, like every other feature’s geometry; it used to be required (#60)Chemistry lookups raise
SGUNotFoundErrorwhen a site isn’t found, andSGUAmbiguousErrorwhen asite_idorsite_namegiven to a single-site method such asget_results_by_site()matches several sites. They used to raise a plainValueError; both are stillValueErrors (#61)Observed and chemistry lookups word their errors and the extra-request warning the same way, e.g.
Exactly one of 'station_id' or 'station_name' must be provided.instead ofEither 'station_id' or 'station_name' must be provided./Only one of .... Passing more than one ofnational_site_id,site_idandsite_nametoget_results_by_parameter()saysAt most one of ...(#61)
Fixed#
GroundwaterStationCollection.to_dataframe()keeps the feature id instead of overwriting it with the station id (#58)to_dataframe()on an empty measurement collection returns an empty DataFrame instead of raisingKeyError(#60)Modeled area
centroid_longitude/centroid_latitudeare the centre of the grid cell. The ring’s repeated closing corner was counted twice, which put the centroid about 500 m off-centre (#60)Rows with the same date keep their API order when sorted by date, e.g. the parameters of one chemistry sample (#60)
Every collection accepts
totalFeatures: "unknown", not just the modeled ones (#60)
Removed#
BaseClient.post()and thedataand**kwargsarguments of the internalBaseClient; nothing used them (#59)
BREAKING CHANGES#
DataFrame columns are the same shape for every collection:
feature_id, then the collection’s geometry columns, then the properties in model order. An empty collection keeps all these columns. See the pandas user guide for each collection’s geometry columns (#60)Collection
Before
After
Stations
station_idheld the station id (the feature id was lost),geometry_typefeature_id+station_id; nogeometry_typeMeasurements
measurement_id,observation_datesecond;geometry_type/longitude/latitudewhen the response had geometryfeature_id;observation_datein model order; no geometry columnsModeled areas
geometry_typeno
geometry_typeModeled levels
level_idfeature_idSampling sites
geometry_type/longitude/latitudeonly when the response had geometrylongitude/latitudealways, null without geometry; nogeometry_typeAnalysis results
geometry_type/longitude/latitudewhen the response had geometryno geometry columns
SGUResponse.to_dataframe()removed: it only raisedNotImplementedError. Every collection getsto_dataframe()fromFeatureCollection(#60)CRSandLinkare no longer importable fromsgu_client.models.observed, which only re-exported them by accident. Import them fromsgu_client.models.shared(#60)Observed lookups raise for stations that aren’t found instead of leaving them out.
get_stations_by_names()(withstation_idorstation_name) andget_measurements_by_name(s)()withstation_nameraiseSGUNotFoundErrornaming the missing stations, as chemistry already did for sites.get_measurements_by_name(s)()withstation_idis unchanged (#61)
[0.7.0] - 2026-10-20#
Added#
SGUNotFoundErrorandSGUAmbiguousError, raised when a lookup matches no feature or several. Both subclassValueError, so existingexcept ValueErrorcode keeps working (#62)Modeled levels:
get_levels_by_area(),get_levels_by_areas()andget_levels_by_coords()taketminandtmax(ISO strings ordatetimeobjects), filtering on the level’s date like the observed measurement lookups (#62)get_measurements(),get_levels()andget_analysis_results()taketminandtmaxtoo, filtering on the observation, level or sampling date. Docs and examples now usetmin/tmaxinstead of the OGCdatetimeinterval (#62)
Changed#
Filter text: a single value is sent as
field=valueand several asfield in (...)on every dataset. Modeled area filters wereomrade_id = 1/omrade_id IN (1, 2), and observed sent one-element lists asin (...). Same results, different filter text (#62)
Fixed#
get_station_by_name()raisesSGUNotFoundErrorinstead of a bareIndexErrorwhen no station matches (#57)Single quotes in station, site or parameter names are escaped in filters instead of producing an invalid query (#62)
BREAKING CHANGES#
SGUConfig.base_urlis now the root of SGU’s open data APIs (https://api.sgu.se/oppnadata/) and applies to the observed, modeled and chemistry datasets alike. It used to be the full observed-levels URL and was ignored by the other two. If you set it, drop thegrundvattennivaer-observerade/ogc/features/v1/suffix (#62)Internal URL constants removed:
BASE_PATHon the three clients,ModeledGroundwaterLevelClient.MODELED_BASE_URLandGroundwaterChemistryClient.CHEMISTRY_BASE_URL. Dataset URLs now come fromSGUConfig.base_url(#62)
[0.6.0] - 2026-10-19#
Added#
Python 3.14: supported and tested in CI (#48)
SGUClient.close()closes the HTTP session for clients created without awithblockType checking: the package’s type hints are now verified with ty on every supported Python version, so editors and type checkers see accurate types (#45)
Changed#
Documentation: restructured into User Guide, API Reference and About sections; the README is now a short landing page that links to the docs (#44)
Fixed#
Methods wrapped by
optional_pandas_methodkeep their original signature and docstringStale docstring examples (the removed
debugconfig option, an API alias passed as a keyword, an invalid date)Station
active_sinceandactive_untilare no longer raw strings like"2018-12-10Z"(#49)pandas 3: date columns stay
datetime64[ns, UTC]; pandas 3 otherwise picks the resolution per column (us, orswhen every value is missing)
BREAKING CHANGES#
Dates are parsed everywhere (#51): every date field on the observed, modeled and chemistry models, and
timeStampon every collection, is now a timezone-awaredatetimein UTC instead of a string. Date-only values ("1996-04-29Z") become midnight UTC. Values that can’t be parsed, or have no timezone, becomeNoneand log a warningHelper properties removed:
observation_datetime,last_updated_datetime,date_parsed,established_datetimeandsampling_datetime; use the fields themselvespandas: every date column is
datetime64[ns, UTC], even when all values are missing. Modeleddatewas timezone-naive and station dates were strings. Measurement DataFrames no longer have the duplicatelast_updatecolumn; uselast_updatedto_dict()returnsdatetimeobjects for date fields; usemodel_dump_json()for JSON
[0.5.0] - 2026-10-15#
Changed#
Groundwater chemistry API v2:
client.chemistrynow uses SGU’sgrundvattenkvalitet-analysresultat-provplatser-v2dataset; the previous dataset no longer publishes any collections (#42)
BREAKING CHANGES#
Chemistry models:
SamplingSitePropertiesandAnalysisResultPropertiesnow type every v2 field. Fields that kept their meaning keep their attribute names (e.g.parameter_name,sampling_date,analyses_csv_url); fields SGU removed are gone:Sampling sites:
well_depth,well_depth_qualifier,filter_depth_top,filter_depth_bottom,filter_depth_qualifier,decommissioned_date(anddecommissioned_datetime),program_affiliation,local_monitoringAnalysis results:
station_id,sample_id,sample_type,program_name,program_id,monitoring_manual,submission_date(andsubmission_datetime),parameter_short_name,parameter_sequence_number,water_preparation,reporting_limit,last_updated(andlast_updated_datetime),row_numberdetection_limitis now a string, as returned by the API
County codes:
county_codeis a zero-padded two-digit string on both sites and results (the v2 API mixes"04"and4); on analysis results it now maps tolanskodinstead oflan(county name, seecounty)Results by site: analysis results no longer carry
platsbeteckning, soget_results_by_site()andget_results_by_sites()takenational_site_idas their first argument.site_idandsite_namestill work, but cost an extra lookup request and log a warning. Lookups that miss any requested site raiseValueErrorResults by parameter:
get_results_by_parameter()matches SGU’s parameter name (parameternamn, e.g."Bly","pH"), and its site filter acceptsnational_site_id,site_idorsite_namelikeget_results_by_site()Site lookups:
get_sampling_sites_by_names()raisesValueErrornaming any requested sites that are not found, instead of returning a partial collectionpandas: the feature ID column in chemistry
to_dataframe()output is nowfeature_id(previouslysite_idfor sites andresult_idfor results, which clashed with the newresult_idfield);pivot_by_parameter()pivots onparameter_nameby default;to_dataframe()parsessampling_date,sampling_end_date,registration_dateandanalysis_date(results) andestablished_date,first_sample_date,latest_sample_date(sites) as UTC datetimes
Added#
national_site_idoption forget_sampling_site_by_name()andget_sampling_sites_by_names()
[0.2.0] - 2025-09-11#
Added#
Pagination Support: Automatic handling of SGU API pagination for large datasets (#11)
Collection Series Export: New
to_series()convenience method for converting collections to pandas Series (#7)Comprehensive Model Tests: Complete test coverage for all Pydantic data models (#9)
Enhanced#
Developer Experience: Made pytest coverage optional for faster development cycles (#10)
Documentation: Added coverage badge and improved GitFlow-lite workflow documentation (#3)
Fixed#
API Response Handling: Improved pagination handling for SGU API responses to prevent data loss
Test Performance: Streamlined testing workflow for better development experience
[0.1.0] - 2025-09-05#
Added#
Initial release of sgu-client
Complete SGU groundwater data API client with type safety
Hierarchical API structure with
client.levels.observedandclient.levels.modeled
Key Features#
Type Safety: Full type hints with Pydantic validation
API Coverage: Complete implementation of SGU groundwater levels API
Data Analysis: Convert collections to pandas DataFrames
Performance: Automatic warnings for slower operations
Extensibility: Easy to add new SGU API endpoints
Developer Experience: Modern tooling with ruff, pytest, and uv