Skip to content
Draft
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
2c23157
Restrict EIA-861 years to 2020 & 2023 in fast ETL.
zaneselvans Aug 25, 2025
37ee8cf
Adjust EIA-861 transforms + dbt cols_not_all_null params to allow lim…
zaneselvans Aug 25, 2025
a2449b8
Update comment in ETL Fast settings wrt EIA-861 years
zaneselvans Aug 26, 2025
4540b26
WIP: rafactor FERC-714 outputs to not depend on all years of EIA-861 …
zaneselvans Aug 26, 2025
c4dac29
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Aug 26, 2025
93258e9
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Aug 27, 2025
fcaec32
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Aug 27, 2025
36cb979
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Aug 28, 2025
9af11c0
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Aug 29, 2025
ce7411c
Merge branch 'limit-eia861-fast-etl-years' of github.com:catalyst-coo…
zaneselvans Aug 29, 2025
4c2d06c
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 1, 2025
4b4d291
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 1, 2025
8b30f10
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 1, 2025
20af392
Add data_maturity col to empty dfs in EIA-861 _pre_process() function.
zaneselvans Sep 2, 2025
cec8be7
Add some missing type annotations.
zaneselvans Sep 2, 2025
a005dd1
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 2, 2025
b885483
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 3, 2025
417eb08
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 5, 2025
4a030ec
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 5, 2025
e51e818
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 6, 2025
aa20ab9
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 7, 2025
db42d90
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 8, 2025
c5ed18c
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 11, 2025
1bdfa2d
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 15, 2025
c63bb5a
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 17, 2025
7787af8
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 19, 2025
8097e72
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Sep 23, 2025
03f5ff5
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 10, 2025
c6d3cf6
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 13, 2025
42ae523
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 13, 2025
bff998c
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 13, 2025
dcdadba
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 14, 2025
0212ad6
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 14, 2025
9025d1d
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 15, 2025
f84c246
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 17, 2025
d4bc91b
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 19, 2025
d491496
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 21, 2025
769f780
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 21, 2025
5d7945f
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 22, 2025
88e826b
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 23, 2025
64c8605
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Oct 28, 2025
b6ce11b
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 4, 2025
0f798e9
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 6, 2025
bff06f9
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 6, 2025
42cdeb5
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 9, 2025
678d720
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 10, 2025
9f50082
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 10, 2025
a2f6833
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 11, 2025
19d2584
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 13, 2025
ebd0707
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 24, 2025
9cc483f
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Nov 25, 2025
cd9ebae
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Dec 4, 2025
aef4e73
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Dec 10, 2025
798fbe5
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Dec 11, 2025
d4e50bc
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Dec 12, 2025
eae4f04
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Dec 14, 2025
627d065
Merge branch 'main' into limit-eia861-fast-etl-years
e-belfer Dec 23, 2025
8013eb8
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Jan 6, 2026
d8b25e9
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Jan 9, 2026
ef63feb
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Jan 10, 2026
7cfab00
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Jan 12, 2026
57de223
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Jan 14, 2026
26bfd8d
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Jan 15, 2026
0606b8c
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Jan 19, 2026
c088d60
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Mar 24, 2026
d66dd60
Merge branch 'main' into limit-eia861-fast-etl-years
zaneselvans Apr 1, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ sources:
tables:
- name: core_eia861__yearly_demand_response
data_tests:
- expect_columns_not_all_null
- expect_columns_not_all_null:
arguments:
row_conditions:
short_form: EXTRACT(year FROM report_date) = 2019
- check_row_counts_per_partition:
arguments:
table_name: core_eia861__yearly_demand_response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ sources:
tables:
- name: core_eia861__yearly_distribution_systems
data_tests:
- expect_columns_not_all_null
- expect_columns_not_all_null:
arguments:
row_conditions:
short_form: EXTRACT(year FROM report_date) = 2019
- check_row_counts_per_partition:
arguments:
table_name: core_eia861__yearly_distribution_systems
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ sources:
tables:
- name: core_eia861__yearly_energy_efficiency
data_tests:
- expect_columns_not_all_null
- expect_columns_not_all_null:
arguments:
row_conditions:
short_form: EXTRACT(year FROM report_date) = 2019
- check_row_counts_per_partition:
arguments:
table_name: core_eia861__yearly_energy_efficiency
Expand Down
7 changes: 6 additions & 1 deletion dbt/models/eia861/core_eia861__yearly_mergers/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ sources:
tables:
- name: core_eia861__yearly_mergers
data_tests:
- expect_columns_not_all_null
- expect_columns_not_all_null:
arguments:
row_conditions:
entity_type: EXTRACT(year FROM report_date) <= 2011
state: EXTRACT(year FROM report_date) <= 2012
zip_code_4: EXTRACT(year FROM report_date) <= 2010
- check_row_counts_per_partition:
arguments:
table_name: core_eia861__yearly_mergers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ sources:
tables:
- name: core_eia861__yearly_net_metering_customer_fuel_class
data_tests:
- expect_columns_not_all_null
- expect_columns_not_all_null:
arguments:
row_conditions:
capacity_mw: EXTRACT(year FROM report_date) >= 2010
energy_capacity_mwh: EXTRACT(year FROM report_date) >= 2023
short_form: EXTRACT(year FROM report_date) = 2019
sold_to_utility_mwh: EXTRACT(year FROM report_date) >= 2007
- check_row_counts_per_partition:
arguments:
table_name: core_eia861__yearly_net_metering_customer_fuel_class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ sources:
tables:
- name: core_eia861__yearly_operational_data_misc
data_tests:
- expect_columns_not_all_null
- expect_columns_not_all_null:
arguments:
row_conditions:
consumed_by_facility_mwh: EXTRACT(year FROM report_date) <= 2003
short_form: EXTRACT(year FROM report_date) = 2019
- check_row_counts_per_partition:
arguments:
table_name: core_eia861__yearly_operational_data_misc
Expand Down
5 changes: 4 additions & 1 deletion dbt/models/eia861/core_eia861__yearly_reliability/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ sources:
tables:
- name: core_eia861__yearly_reliability
data_tests:
- expect_columns_not_all_null
- expect_columns_not_all_null:
arguments:
row_conditions:
short_form: EXTRACT(year FROM report_date) = 2019
- check_row_counts_per_partition:
arguments:
table_name: core_eia861__yearly_reliability
Expand Down
5 changes: 4 additions & 1 deletion dbt/models/eia861/core_eia861__yearly_sales/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ sources:
tables:
- name: core_eia861__yearly_sales
data_tests:
- expect_columns_not_all_null
- expect_columns_not_all_null:
arguments:
row_conditions:
short_form: EXTRACT(year FROM report_date) = 2019
- check_row_counts_per_partition:
arguments:
table_name: core_eia861__yearly_sales
Expand Down
6 changes: 5 additions & 1 deletion dbt/models/eia861/core_eia861__yearly_short_form/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ sources:
tables:
- name: core_eia861__yearly_short_form
data_tests:
- expect_columns_not_all_null
- expect_columns_not_all_null:
arguments:
row_conditions:
entity_type: EXTRACT(year FROM report_date) NOT IN (2012, 2013, 2014)
has_green_pricing: EXTRACT(year FROM report_date) = 2012
- check_row_counts_per_partition:
arguments:
table_name: core_eia861__yearly_short_form
Expand Down
4 changes: 2 additions & 2 deletions src/pudl/extract/eia860.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(self, *args, **kwargs):
self.cols_added = []
super().__init__(*args, **kwargs)

def process_raw(self, df, page, **partition):
def process_raw(self, df: pd.DataFrame, page: str, **partition):
"""Apply necessary pre-processing to the dataframe.

* Rename columns based on our compiled spreadsheet metadata
Expand All @@ -50,7 +50,7 @@ def process_raw(self, df, page, **partition):
return df

@staticmethod
def get_dtypes(page, **partition):
def get_dtypes(page: str, **partition):
"""Returns dtypes for plant id columns."""
return {
"Plant ID": pd.Int64Dtype(),
Expand Down
6 changes: 3 additions & 3 deletions src/pudl/extract/eia861.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(self, *args, **kwargs):
stacklevel=1,
)

def process_raw(self, df, page, **partition):
def process_raw(self, df: pd.DataFrame, page: str, **partition):
"""Rename columns with location."""
column_map_numeric = self._metadata.get_column_map(page, **partition)
df = df.rename(
Expand All @@ -57,14 +57,14 @@ def process_raw(self, df, page, **partition):
return df

@staticmethod
def process_renamed(df, page, **partition):
def process_renamed(df: pd.DataFrame, page: str, **partition):
"""Adds report_year column if missing."""
if "report_year" not in df.columns:
df["report_year"] = list(partition.values())[0]
return df

@staticmethod
def get_dtypes(page, **partition):
def get_dtypes(page: str, **partition):
"""Returns dtypes for plant id columns."""
return {
"Plant ID": pd.Int64Dtype(),
Expand Down
5 changes: 3 additions & 2 deletions src/pudl/extract/excel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import pudl
from pudl.extract.extractor import GenericExtractor, GenericMetadata, PartitionSelection
from pudl.workspace.datastore import Datastore

logger = pudl.logging_helpers.get_logger(__name__)

Expand Down Expand Up @@ -126,11 +127,11 @@ class ExcelExtractor(GenericExtractor):

METADATA: ExcelMetadata = None

def __init__(self, ds):
def __init__(self, ds: Datastore):
"""Create new extractor object and load metadata.

Args:
ds (datastore.Datastore): An initialized datastore, or subclass
ds: An initialized PUDL datastore, or subclass thereof.
"""
super().__init__(ds)
self._metadata = self.METADATA
Expand Down
80 changes: 46 additions & 34 deletions src/pudl/output/ferc714.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@

The changes are applied locally to EIA 861 tables.

* `id` (int): EIA balancing authority identifier (`balancing_authority_id_eia`).
* `from` (int): Reference year, to use as a template for target years.
* `to` (List[int]): Target years, in the closed interval format [minimum, maximum].
Rows in `core_eia861__yearly_balancing_authority` are added (if missing) for every target year
* ``id`` (int): EIA balancing authority identifier (``balancing_authority_id_eia``).
* ``from`` (int): Reference year, to use as a template for target years.
* ``to`` (List[int]): Target years, in the closed interval format [minimum, maximum].
Rows in ``core_eia861__yearly_balancing_authority`` are added (if missing) for every target year
with the attributes from the reference year.
Rows in `core_eia861__assn_balancing_authority` are added (or replaced, if existing)
Rows in ``core_eia861__assn_balancing_authority`` are added (or replaced, if existing)
for every target year with the utility associations from the reference year.
Rows in `core_eia861__yearly_service_territory` are added (if missing) for every target year
Rows in ``core_eia861__yearly_service_territory`` are added (if missing) for every target year
with the nearest year's associated utilities' counties.
* `exclude` (Optional[List[str]]): Utilities to exclude, by state (two-letter code).
Rows are excluded from `core_eia861__assn_balancing_authority` with target year and state.
* ``exclude`` (Optional[List[str]]): Utilities to exclude, by state (two-letter code).
Rows are excluded from ``core_eia861__assn_balancing_authority`` with target year and state.
"""

UTILITIES: list[dict[str, Any]] = [
Expand All @@ -76,14 +76,14 @@

The changes are applied locally to EIA 861 tables.

* `id` (int): EIA balancing authority (BA) identifier (`balancing_authority_id_eia`).
Rows for `id` are removed from `core_eia861__yearly_balancing_authority`.
* `reassign` (Optional[bool]): Whether to reassign utilities to parent BAs.
Rows for `id` as BA in `core_eia861__assn_balancing_authority` are removed.
Utilities assigned to `id` for a given year are reassigned
to the BAs for which `id` is an associated utility.
* `replace` (Optional[bool]): Whether to remove rows where `id` is a utility in
`core_eia861__assn_balancing_authority`. Applies only if `reassign=True`.
* ``id`` (int): EIA balancing authority (BA) identifier (``balancing_authority_id_eia``).
Rows for ``id`` are removed from ``core_eia861__yearly_balancing_authority``.
* ``reassign`` (Optional[bool]): Whether to reassign utilities to parent BAs.
Rows for ``id`` as BA in ``core_eia861__assn_balancing_authority`` are removed.
Utilities assigned to ``id`` for a given year are reassigned
to the BAs for which ``id`` is an associated utility.
* ``replace`` (Optional[bool]): Whether to remove rows where ``id`` is a utility in
``core_eia861__assn_balancing_authority``. Applies only if ``reassign=True``.
"""

################################################################################
Expand Down Expand Up @@ -186,28 +186,35 @@ def filled_core_eia861__yearly_balancing_authority(
"""Modified core_eia861__yearly_balancing_authority table.

This function adds rows for each balancing authority-year pair missing from the
cleaned core_eia861__yearly_balancing_authority table, using a dictionary of manual fixes. It
uses the reference year as a template. The function also removes balancing
authorities that are manually categorized as utilities.
cleaned :ref:`core_eia861__yearly_balancing_authority` table, using a dictionary
of manual fixes. It uses the reference year as a template. The function also removes
balancing authorities that are manually categorized as utilities.
"""
df = core_eia861__yearly_balancing_authority
index = ["balancing_authority_id_eia", "report_date"]
dfi = df.set_index(index)
# Prepare reference rows
keys = [(fix["id"], pd.Timestamp(fix["from"], 1, 1)) for fix in ASSOCIATIONS]
eia861_years = df["report_date"].dt.year.unique()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding an early exit here if eia861_years is empty

keys = [
(fix["id"], pd.Timestamp(fix["from"], 1, 1))
for fix in ASSOCIATIONS
if fix["from"] in eia861_years
]
Comment on lines +198 to +202
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider defining a local version of ASSOCIATIONS to use both here and at line 209

refs = dfi.loc[keys].reset_index().to_dict("records")
# Build table of new rows
# Insert row for each target balancing authority-year pair
# missing from the original table, using the reference year as a template.
rows: list[dict[str, Any]] = []
for ref, fix in zip(refs, ASSOCIATIONS, strict=True):
for ref, fix in zip(
refs, [fx for fx in ASSOCIATIONS if fx["from"] in eia861_years], strict=True
):
for year in range(fix["to"][0], fix["to"][1] + 1):
key = (fix["id"], pd.Timestamp(year, 1, 1))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense to skip iterations here when year is not in eia861_years, instead of filtering at 216?

if key not in dfi.index:
rows.append({**ref, "report_date": key[1]})
df = pd.concat(
[df, apply_pudl_dtypes(pd.DataFrame(rows), group="eia")], axis="index"
)
new_rows = apply_pudl_dtypes(pd.DataFrame(rows), group="eia")
new_rows = new_rows[new_rows["report_date"].dt.year.isin(eia861_years)]
df = pd.concat([df, new_rows], axis="index")
# Remove balancing authorities treated as utilities
mask = df["balancing_authority_id_eia"].isin([util["id"] for util in UTILITIES])
return apply_pudl_dtypes(df[~mask], group="eia")
Expand All @@ -219,10 +226,10 @@ def filled_core_eia861__assn_balancing_authority(
"""Modified core_eia861__assn_balancing_authority table.

This function adds rows for each balancing authority-year pair missing from the
cleaned core_eia861__assn_balancing_authority table, using a dictionary of manual fixes.
It uses the reference year as a template. The function also reassigns balancing
authorities that are manually categorized as utilities to their parent balancing
authorities.
cleaned :ref:`core_eia861__assn_balancing_authority` table, using a dictionary of
manual fixes. It uses the reference year as a template. The function also reassigns
balancing authorities that are manually categorized as utilities to their parent
balancing authorities.
"""
df = core_eia861__assn_balancing_authority
# Prepare reference rows
Expand All @@ -249,7 +256,10 @@ def filled_core_eia861__assn_balancing_authority(
tables.append(ref.assign(report_date=key[1]))
replaced |= mask
# Append to original table with matching rows removed
df = pd.concat([df[~replaced], apply_pudl_dtypes(pd.concat(tables), group="eia")])
new_rows = apply_pudl_dtypes(pd.concat(tables), group="eia")
eia861_years = df["report_date"].dt.year.unique()
new_rows = new_rows[new_rows["report_date"].dt.year.isin(eia861_years)]
df = pd.concat([df[~replaced], new_rows], axis="index")
# Remove balancing authorities treated as utilities
mask = np.zeros(df.shape[0], dtype=bool)
tables = []
Expand Down Expand Up @@ -300,20 +310,22 @@ def filled_service_territory_eia861(
"""Modified core_eia861__yearly_service_territory table.

This function adds rows for each balancing authority-year pair missing from the
cleaned core_eia861__yearly_service_territory table, using a dictionary of manual fixes. It also
drops utility-state combinations which are missing counties across all years of
data, fills records missing counties with the nearest year of county data for the
same utility and state.
cleaned :ref:`core_eia861__yearly_service_territory` table, using a dictionary of
manual fixes. It also drops utility-state combinations which are missing counties
across all years of data, fills records missing counties with the nearest year of
county data for the same utility and state.

"""
index = ["utility_id_eia", "state", "report_date"]
# Select relevant balancing authority-utility associations
assn = filled_core_eia861__assn_balancing_authority(
core_eia861__assn_balancing_authority
)
eia861_years = core_eia861__yearly_service_territory["report_date"].dt.year.unique()
selected = np.zeros(assn.shape[0], dtype=bool)
for fix in ASSOCIATIONS:
years = [fix["from"], *range(fix["to"][0], fix["to"][1] + 1)]
dates = [pd.Timestamp(year, 1, 1) for year in years]
dates = [pd.Timestamp(year, 1, 1) for year in years if year in eia861_years]
mask = assn["balancing_authority_id_eia"].eq(fix["id"]).to_numpy(bool)
mask[mask] = assn["report_date"][mask].isin(dates)
selected |= mask
Expand Down
31 changes: 3 additions & 28 deletions src/pudl/package_data/settings/etl_fast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,34 +53,9 @@ datasets:
eia860m:
year_months: ["2025-05", "2025-06"]
eia861:
# eia861 runs fast. Discontinued tables break single-year ETL.
# This is a temporary hack to make the tests pass!
years:
[
2001,
2002,
2003,
2004,
2005,
2006,
2007,
2008,
2009,
2010,
2011,
2012,
2013,
2014,
2015,
2016,
2017,
2018,
2019,
2020,
2021,
2022,
2023,
]
# These years must overlap with FERC-714 to allow assets that depend on both to
# be materialized.
years: [2020, 2023]
eia930:
half_years: ["2024half2"]
eiaaeo:
Expand Down
Loading
Loading