Skip to content
Open
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
3 changes: 1 addition & 2 deletions analytics/analytics_package/analytics/_report_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,9 @@ def get_one_period_change_series(
series_previous_reindexed = (
series_previous.reindex(combined_index) * current_length / previous_length
)
change = ((series_current_reindexed / series_previous_reindexed) - 1).replace(
return ((series_current_reindexed / series_previous_reindexed) - 1).replace(
{np.inf: np.nan}
)
return change


def get_change_over_time_df(
Expand Down
15 changes: 4 additions & 11 deletions analytics/analytics_package/analytics/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def authenticate(
*other_service_params,
port=None,
):
service_param_sets = (first_service_params,) + other_service_params
service_param_sets = (first_service_params, *other_service_params)

all_scopes = {
scope for service_params in service_param_sets for scope in service_params[0]
Expand All @@ -68,10 +68,7 @@ def authenticate(
global next_port

if port is None:
if next_port is None:
port = 8082
else:
port = next_port
port = 8082 if next_port is None else next_port
next_port = port + 1
elif next_port is None:
next_port = port + 1
Expand Down Expand Up @@ -192,9 +189,7 @@ def get_metrics_by_dimensions_v3_style(
results.append(result)
params[start_index_key] += params[max_results_key]

df = results_to_df(results)

return df
return results_to_df(results)


def get_metrics_by_dimensions_v4_style(
Expand Down Expand Up @@ -258,9 +253,7 @@ def get_metrics_by_dimensions_v4_style(
offset += max_results
params["offset"] = offset

df = v4_results_to_df(results, dimensions, metrics)

return df
return v4_results_to_df(results, dimensions, metrics)


def v4_results_to_df(results, dimensions, metrics):
Expand Down
6 changes: 2 additions & 4 deletions analytics/analytics_package/analytics/report_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ def get_outbound_links_df(analytics_params, ignore_index=True):

if not ignore_index:
return df_all_links.set_index(dimension_aliases_to_keep)
else:
return df_all_links.reset_index(drop=True)
return df_all_links.reset_index(drop=True)


def get_outbound_links_change(
Expand Down Expand Up @@ -288,8 +287,7 @@ def get_one_period_change_df(
)
if ignore_index:
return df_current_with_changes
else:
return df_current_with_changes.reset_index()
return df_current_with_changes.reset_index()


def get_page_views_over_time_df(
Expand Down
4 changes: 2 additions & 2 deletions analytics/analytics_package/analytics/static_site/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from .resolve import enrich_detail_records, fetch_entity_title_map

__all__ = [
"generate_site",
"fetch_entity_title_map",
"enrich_detail_records",
"fetch_entity_title_map",
"generate_site",
"make_event_charts",
]
36 changes: 18 additions & 18 deletions analytics/analytics_package/analytics/static_site/export.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"""Export analytics DataFrames to JSON files for the static site."""

import glob
import json
import os
from datetime import datetime
from pathlib import Path

import pandas as pd
from pandas.api.types import is_object_dtype, is_string_dtype
Expand All @@ -28,7 +27,7 @@ def export_df_as_json(df, col_map, change_col, filename, output_dir):
col_map: Dict mapping source column names to output names.
change_col: Source column name for the change metric (may be absent).
filename: Output JSON filename.
output_dir: Output directory.
output_dir: Output directory, as a Path.
"""
if df is None or len(df) == 0:
records = []
Expand Down Expand Up @@ -56,7 +55,7 @@ def export_df_as_json(df, col_map, change_col, filename, output_dir):
if pd.isna(record.get("change")):
record["change"] = None

with open(os.path.join(output_dir, filename), "w") as f:
with (output_dir / filename).open("w") as f:
json.dump(records, f, indent=2)
print(f" Wrote {filename} ({len(records)} records)")

Expand All @@ -78,12 +77,13 @@ def export_data(
current_month: Current month string (YYYY-MM).
analytics_start: Analytics start date string.
custom_events: List of custom event dicts with results.
output_dir: Output directory for JSON files.
output_dir: Output directory for JSON files, as a str or Path.
"""
os.makedirs(output_dir, exist_ok=True)
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)

for old_file in glob.glob(os.path.join(output_dir, "event_*_detail.json")):
os.remove(old_file)
for old_file in output_dir.glob("event_*_detail.json"):
old_file.unlink()

df_monthly_traffic = data["monthly_traffic"]
dates = data.get("dates", {})
Expand All @@ -96,7 +96,7 @@ def export_data(
traffic_data["users"] = traffic_data["users"].fillna(0).astype(int)
traffic_data["pageviews"] = traffic_data["pageviews"].fillna(0).astype(int)

with open(os.path.join(output_dir, "monthly_traffic.json"), "w") as f:
with (output_dir / "monthly_traffic.json").open("w") as f:
json.dump(traffic_data.to_dict(orient="records"), f, indent=2)
print(f" Wrote monthly_traffic.json ({len(traffic_data)} records)")

Expand Down Expand Up @@ -138,28 +138,28 @@ def export_data(
# File downloads
print("Exporting file downloads data...")
file_downloads = data.get("file_downloads", 0)
with open(os.path.join(output_dir, "file_downloads.json"), "w") as f:
with (output_dir / "file_downloads.json").open("w") as f:
json.dump({"total": file_downloads}, f, indent=2)
print(f" Wrote file_downloads.json (total: {file_downloads})")

# Access requests
print("Exporting access requests data...")
access_requests = data.get("access_requests", [])
with open(os.path.join(output_dir, "access_requests.json"), "w") as f:
with (output_dir / "access_requests.json").open("w") as f:
json.dump(access_requests, f, indent=2)
print(f" Wrote access_requests.json ({len(access_requests)} records)")

# File download events (GA4 enhanced measurement)
print("Exporting file download events data...")
file_download_events = data.get("file_download_events", 0)
with open(os.path.join(output_dir, "file_download_events.json"), "w") as f:
with (output_dir / "file_download_events.json").open("w") as f:
json.dump({"total": file_download_events}, f, indent=2)
print(f" Wrote file_download_events.json (total: {file_download_events})")

# Search queries
print("Exporting search queries data...")
search_queries = data.get("search_queries", {"total": 0, "queries": []})
with open(os.path.join(output_dir, "search_queries.json"), "w") as f:
with (output_dir / "search_queries.json").open("w") as f:
json.dump(search_queries, f, indent=2)
print(
f" Wrote search_queries.json ({len(search_queries.get('queries', []))} queries)"
Expand All @@ -180,7 +180,7 @@ def export_data(
event_entry["detail_file_column"] = event["detail_file_column"]
events_output.append(event_entry)

with open(os.path.join(output_dir, "custom_events.json"), "w") as f:
with (output_dir / "custom_events.json").open("w") as f:
json.dump(events_output, f, indent=2)
print(f" Wrote custom_events.json ({len(events_output)} events)")

Expand All @@ -190,7 +190,7 @@ def export_data(
detail = data.get(f"event_{key}_detail")
if detail is not None:
filename = f"event_{key}_detail.json"
with open(os.path.join(output_dir, filename), "w") as f:
with (output_dir / filename).open("w") as f:
json.dump(detail, f, indent=2)
print(f" Wrote {filename} ({len(detail)} records)")

Expand Down Expand Up @@ -218,13 +218,13 @@ def export_data(
)
chart_output["charts"].append(chart_data)

with open(os.path.join(output_dir, "event_charts.json"), "w") as f:
with (output_dir / "event_charts.json").open("w") as f:
json.dump(chart_output, f, indent=2)
print(f" Wrote event_charts.json ({len(chart_output['charts'])} charts)")

# Config (for the HTML template)
print("Exporting site config...")
with open(os.path.join(output_dir, "config.json"), "w") as f:
with (output_dir / "config.json").open("w") as f:
json.dump(config, f, indent=2)
print(" Wrote config.json")

Expand All @@ -242,6 +242,6 @@ def export_data(
"engagement_rate": data.get("engagement_rate", {}),
}

with open(os.path.join(output_dir, "meta.json"), "w") as f:
with (output_dir / "meta.json").open("w") as f:
json.dump(meta, f, indent=2)
print(" Wrote meta.json")
17 changes: 9 additions & 8 deletions analytics/analytics_package/analytics/static_site/generator.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Generate a static analytics site from GA4 data."""

import os
import shutil
from pathlib import Path

from .export import export_data
from .fetch import fetch_data

TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "template")
TEMPLATE_DIR = Path(__file__).parent / "template"


def generate_site(
Expand Down Expand Up @@ -39,7 +39,7 @@ def generate_site(
property_id: GA4 property ID.
current_month: Current month string (YYYY-MM).
analytics_start: Start date for all-time data (YYYY-MM-DD).
output_dir: Output directory for the generated site.
output_dir: Output directory for the generated site, as a str or Path.
custom_events: List of dicts with "event_name" and "label" keys.
Example: [{"event_name": "chat_submitted", "label": "Chat Submissions"}]
historic_data_path: Path to historic UA data JSON file (optional).
Expand All @@ -55,6 +55,7 @@ def generate_site(
"""
if custom_events is None:
custom_events = []
output_dir = Path(output_dir)

print("=" * 50)
print(f"Generating analytics site: {config['site_title']}")
Expand All @@ -80,13 +81,13 @@ def generate_site(
print("Resolving entity titles...")
title_resolver(data)

os.makedirs(output_dir, exist_ok=True)
template_html = os.path.join(TEMPLATE_DIR, "index.html")
output_html = os.path.join(output_dir, "index.html")
output_dir.mkdir(parents=True, exist_ok=True)
template_html = TEMPLATE_DIR / "index.html"
output_html = output_dir / "index.html"
shutil.copy2(template_html, output_html)
print(f"Copied template to {output_html}")

data_dir = os.path.join(output_dir, "data")
data_dir = output_dir / "data"
export_data(
data=data,
config=config,
Expand All @@ -99,7 +100,7 @@ def generate_site(

print("\n" + "=" * 50)
print("Static site generation complete!")
print(f"Files written to: {os.path.abspath(output_dir)}")
print(f"Files written to: {output_dir.resolve()}")
print("\nTo view the site locally, run:")
print(f" cd {output_dir} && python -m http.server 8080")
print("Then open http://localhost:8080 in your browser.")
2 changes: 1 addition & 1 deletion analytics/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ analytics = { workspace = true }
dev = ["ruff==0.16.3"]

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I", "W", "B"]
select = ["E4", "E7", "E9", "F", "I", "W", "B", "C4", "PIE", "PTH", "RET", "RUF", "SIM", "UP"]
Loading