Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix cases not handled in previous supplemental attribute PRs #321

Merged
merged 1 commit into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions src/component.jl
Original file line number Diff line number Diff line change
Expand Up @@ -307,14 +307,43 @@
if !haskey(attribute_container, T)
attribute_container[T] = Dict{Base.UUID, T}()
end
attribute_container[T][get_uuid(attribute)] = attribute
@debug "SupplementalAttribute type $T with UUID $(get_uuid(attribute)) stored in component $(summary(component))" _group =

uuid = get_uuid(attribute)
if haskey(attribute_container[T], uuid)
throw(

Check warning on line 313 in src/component.jl

View check run for this annotation

Codecov / codecov/patch

src/component.jl#L313

Added line #L313 was not covered by tests
ArgumentError(
"Supplemental attribute $uuid is already attached to $(summary(component))",
),
)
end
attribute_container[T][uuid] = attribute
@debug "SupplementalAttribute type $T with UUID $uuid) stored in component $(summary(component))" _group =
LOG_GROUP_SYSTEM
return
end

"""
Return true if the component has attributes.
Return true if the component has supplemental attributes of the given type.
"""
function has_supplemental_attributes(
::Type{T},
component::InfrastructureSystemsComponent,
) where {T <: InfrastructureSystemsSupplementalAttribute}
supplemental_attributes = get_supplemental_attributes_container(component)
if !isconcretetype(T)
for (k, v) in supplemental_attributes
if !isempty(v) && k <: T
return true

Check warning on line 336 in src/component.jl

View check run for this annotation

Codecov / codecov/patch

src/component.jl#L334-L336

Added lines #L334 - L336 were not covered by tests
end
end

Check warning on line 338 in src/component.jl

View check run for this annotation

Codecov / codecov/patch

src/component.jl#L338

Added line #L338 was not covered by tests
end
supplemental_attributes = get_supplemental_attributes_container(component)
!haskey(supplemental_attributes, T) && return false
return !isempty(supplemental_attributes[T])
end

"""
Return true if the component has supplemental attributes.
"""
function has_supplemental_attributes(component::InfrastructureSystemsComponent)
container = get_supplemental_attributes_container(component)
Expand Down
7 changes: 7 additions & 0 deletions src/supplemental_attribute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ function detach_component!(
return
end

"""
Return true if the attribute is attached to at least one component.
"""
function is_attached_to_component(attribute::InfrastructureSystemsSupplementalAttribute)
return !isempty(get_component_uuids(attribute))
end

"""
Return true if the attribute has time series data.
"""
Expand Down
32 changes: 12 additions & 20 deletions src/supplemental_attributes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,6 @@
return
end

"""
Check to see if supplemental_attribute exists.
"""
function has_supplemental_attributes(
::Type{T},
component::InfrastructureSystemsComponent,
) where {T <: InfrastructureSystemsSupplementalAttribute}
supplemental_attributes = get_supplemental_attributes_container(component)
if !isconcretetype(T)
for (k, v) in supplemental_attributes
if !isempty(v) && k <: T
return true
end
end
end
supplemental_attributes = get_supplemental_attributes_container(component)
!haskey(supplemental_attributes, T) && return false
return !isempty(supplemental_attributes[T])
end

"""
Iterates over all supplemental_attributes.

Expand Down Expand Up @@ -149,6 +129,7 @@
if isempty(supplemental_attributes.data[T])
pop!(supplemental_attributes.data, T)
end
clear_time_series_storage!(supplemental_attribute)
return
end

Expand Down Expand Up @@ -226,6 +207,17 @@
return iter
end

function get_supplemental_attribute(attributes::SupplementalAttributes, uuid::Base.UUID)
for attr_dict in values(attributes.data)
attribute = get(attr_dict, uuid, nothing)
if !isnothing(attribute)
return attribute

Check warning on line 214 in src/supplemental_attributes.jl

View check run for this annotation

Codecov / codecov/patch

src/supplemental_attributes.jl#L210-L214

Added lines #L210 - L214 were not covered by tests
end
end

Check warning on line 216 in src/supplemental_attributes.jl

View check run for this annotation

Codecov / codecov/patch

src/supplemental_attributes.jl#L216

Added line #L216 was not covered by tests

throw(ArgumentError("No attribute with UUID=$uuid is stored"))

Check warning on line 218 in src/supplemental_attributes.jl

View check run for this annotation

Codecov / codecov/patch

src/supplemental_attributes.jl#L218

Added line #L218 was not covered by tests
end

function serialize(attributes::SupplementalAttributes)
return [serialize(y) for x in values(attributes.data) for y in values(x)]
end
Expand Down
96 changes: 72 additions & 24 deletions src/system_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
mutable struct SystemData <: InfrastructureSystemsType
components::Components
masked_components::Components
"Contains all attached component UUIDs, regular and masked."
component_uuids::Dict{Base.UUID, <:InfrastructureSystemsComponent}
attributes::SupplementalAttributes
time_series_params::TimeSeriesParameters
time_series_storage::TimeSeriesStorage
Expand Down Expand Up @@ -69,6 +71,7 @@
return SystemData(
components,
masked_components,
Dict{Base.UUID, InfrastructureSystemsComponent}(),
attributes,
TimeSeriesParameters(),
ts_storage,
Expand All @@ -89,6 +92,7 @@
return SystemData(
components,
masked_components,
Dict{Base.UUID, InfrastructureSystemsComponent}(),
attributes,
time_series_params,
time_series_storage,
Expand Down Expand Up @@ -178,22 +182,22 @@
# Arguments

- `data::SystemData`: SystemData
- `component::InfrastructureSystemsComponent`: will store the time series reference
- `attribute::InfrastructureSystemsSupplementalAttribute`: will store the time series reference
- `time_series::TimeSeriesData`: Any object of subtype TimeSeriesData

Throws ArgumentError if the component is not stored in the system.
Throws ArgumentError if the attribute is not stored in the system.
"""
function add_time_series!(
data::SystemData,
component::InfrastructureSystemsSupplementalAttribute,
attribute::InfrastructureSystemsSupplementalAttribute,
time_series::TimeSeriesData;
skip_if_present = false,
)
metadata_type = time_series_data_to_metadata(typeof(time_series))
ts_metadata = metadata_type(time_series)
attach_time_series_and_serialize!(
data,
component,
attribute,
ts_metadata,
time_series;
skip_if_present = skip_if_present,
Expand Down Expand Up @@ -327,6 +331,11 @@
function compare_values(x::SystemData, y::SystemData; compare_uuids = false)
match = true
for name in fieldnames(SystemData)
if name == :component_uuids
# These are not serialized. They get rebuilt when the parent package adds
# the components.
continue
end
val_x = getfield(x, name)
val_y = getfield(y, name)
if name == :time_series_storage && typeof(val_x) != typeof(val_y)
Expand All @@ -348,15 +357,34 @@
end

function remove_component!(::Type{T}, data::SystemData, name) where {T}
return remove_component!(T, data.components, name)
component = remove_component!(T, data.components, name)
_handle_component_removal!(data, component)
return component

Check warning on line 362 in src/system_data.jl

View check run for this annotation

Codecov / codecov/patch

src/system_data.jl#L360-L362

Added lines #L360 - L362 were not covered by tests
end

function remove_component!(data::SystemData, component)
return remove_component!(data.components, component)
component = remove_component!(data.components, component)
_handle_component_removal!(data, component)
return component
end

function remove_components!(::Type{T}, data::SystemData) where {T}
return remove_components!(T, data.components)
components = remove_components!(T, data.components)
for component in components
_handle_component_removal!(data, component)
end

return components
end

function _handle_component_removal!(data::SystemData, component)
uuid = get_uuid(component)
if !haskey(data.component_uuids, uuid)
error("Bug: component = $(summary(component)) did not have its uuid stored $uuid")

Check warning on line 383 in src/system_data.jl

View check run for this annotation

Codecov / codecov/patch

src/system_data.jl#L383

Added line #L383 was not covered by tests
end

pop!(data.component_uuids, uuid)
return
end

"""
Expand Down Expand Up @@ -491,6 +519,10 @@
return first_initial_timestamp, first_len
end

"""
Provides counts of time series including attachments to components and supplemental
attributes.
"""
struct TimeSeriesCounts
components_with_time_series::Int
supplemental_attributes_with_time_series::Int
Expand Down Expand Up @@ -764,30 +796,43 @@

# Redirect functions to Components and TimeSeriesContainer

add_component!(data::SystemData, component; kwargs...) =
function add_component!(data::SystemData, component; kwargs...)
_check_duplicate_component_uuid(data, component)
add_component!(data.components, component; kwargs...)
data.component_uuids[get_uuid(component)] = component
return
end

add_masked_component!(data::SystemData, component; kwargs...) = add_component!(
data.masked_components,
component;
allow_existing_time_series = true,
kwargs...,
)
function add_masked_component!(data::SystemData, component; kwargs...)
add_component!(
data.masked_components,
component;
allow_existing_time_series = true,
kwargs...,
)
data.component_uuids[get_uuid(component)] = component
return
end

function _check_duplicate_component_uuid(data::SystemData, component)
uuid = get_uuid(component)
if haskey(data.component_uuids, uuid)
throw(ArgumentError("Component $(summary(component)) uuid=$uuid is already stored"))
end
end

iterate_components(data::SystemData) = iterate_components(data.components)

get_component(::Type{T}, data::SystemData, args...) where {T} =
get_component(T, data.components, args...)

function get_component(data::SystemData, uuid::Base.UUID)
for component in get_components(InfrastructureSystemsComponent, data)
if get_uuid(component) == uuid
return component
end
component = get(data.component_uuids, uuid, nothing)
if isnothing(component)
throw(ArgumentError("No component with uuid = $uuid is stored."))

Check warning on line 832 in src/system_data.jl

View check run for this annotation

Codecov / codecov/patch

src/system_data.jl#L832

Added line #L832 was not covered by tests
end

@error "no component with UUID $uuid is stored"
return nothing
return component
end

function get_components(filter_func::Function, ::Type{T}, data::SystemData) where {T}
Expand Down Expand Up @@ -908,6 +953,10 @@
return get_supplemental_attributes(T, data.attributes)
end

function get_supplemental_attribute(data::SystemData, uuid::Base.UUID)
return get_supplemental_attributes(data.attributes, uuid)

Check warning on line 957 in src/system_data.jl

View check run for this annotation

Codecov / codecov/patch

src/system_data.jl#L956-L957

Added lines #L956 - L957 were not covered by tests
end

function iterate_supplemental_attributes(data::SystemData)
return iterate_supplemental_attributes(data.attributes)
end
Expand All @@ -919,8 +968,9 @@
)
detach_component!(attribute, component)
detach_supplemental_attribute!(component, attribute)
clear_time_series_storage!(attribute)
remove_supplemental_attribute!(data.attributes, attribute)
if !is_attached_to_component(attribute)
remove_supplemental_attribute!(data.attributes, attribute)
end
return
end

Expand All @@ -934,7 +984,6 @@
detach_component!(attribute, component)
detach_supplemental_attribute!(component, attribute)
end
clear_time_series_storage!(attribute)
return remove_supplemental_attribute!(data.attributes, attribute)
end

Expand All @@ -950,7 +999,6 @@
detach_supplemental_attribute!(component, attribute)
end
remove_supplemental_attribute!(data.attributes, attribute)
clear_time_series_storage!(attribute)
end
return
end
15 changes: 15 additions & 0 deletions test/test_supplemental_attributes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ end
@test isempty(IS.get_component_uuids(geo_supplemental_attribute))
end

@testset "Test supplemental attribute attached to multiple components" begin
data = IS.SystemData()
geo_supplemental_attribute = IS.GeographicInfo()
component1 = IS.TestComponent("component1", 5)
component2 = IS.TestComponent("component2", 7)
IS.add_supplemental_attribute!(data, component1, geo_supplemental_attribute)
IS.add_supplemental_attribute!(data, component2, geo_supplemental_attribute)
@test IS.get_num_supplemental_attributes(data.attributes) == 1

IS.remove_supplemental_attribute!(data, component1, geo_supplemental_attribute)
@test IS.get_num_supplemental_attributes(data.attributes) == 1
IS.remove_supplemental_attribute!(data, component2, geo_supplemental_attribute)
@test IS.get_num_supplemental_attributes(data.attributes) == 0
end

@testset "Test iterate_SupplementalAttributes" begin
container = IS.SupplementalAttributes(IS.InMemoryTimeSeriesStorage())
geo_supplemental_attribute = IS.GeographicInfo()
Expand Down
3 changes: 3 additions & 0 deletions test/test_system_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
IS.remove_component!(data, collect(components)[1])
components = IS.get_components(IS.TestComponent, data)
@test length(components) == 0
@test isempty(data.component_uuids)

IS.add_component!(data, component)
components = IS.get_components_by_name(IS.InfrastructureSystemsComponent, data, name)
Expand Down Expand Up @@ -228,6 +229,7 @@ end
IS.check_components(data, IS.TestComponent)
component = IS.get_component(IS.TestComponent, data, "component_3")
IS.check_component(data, component)
@test component === IS.get_component(data, IS.get_uuid(component))
end

@testset "Test component and time series counts" begin
Expand Down Expand Up @@ -272,6 +274,7 @@ end
end

for c in IS.get_components(IS.TestComponent, data)
@test IS.has_supplemental_attributes(c)
@test IS.has_supplemental_attributes(IS.GeographicInfo, c)
end

Expand Down
6 changes: 4 additions & 2 deletions test/test_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ struct FakeTimeSeries <: InfrastructureSystems.TimeSeriesData end
Base.length(::FakeTimeSeries) = 42

@testset "Test TimeSeriesData printing" begin
@test sprint(show, MIME("text/plain"), FakeTimeSeries()) ==
"FakeTimeSeries time_series (42):"
@test occursin(
"FakeTimeSeries time_series (42)",
sprint(show, MIME("text/plain"), FakeTimeSeries()),
)
end
Loading