Skip to content

Concert tracking: REST endpoint for marking attendance #6

@chubes4

Description

@chubes4

Summary

REST endpoints for concert attendance tracking. Thin wrappers around extrachill-users data functions, following the abilities-first pattern.

Endpoints

1. Toggle Attendance

POST /extrachill/v1/concert-tracking/toggle

Toggle mark/unmark for an event. Returns new state + count.

Request:

{
    "event_id": 123,
    "blog_id": 7
}

Response (marked):

{
    "marked": true,
    "count": 13,
    "count_label": "13 going",
    "timing": "upcoming"
}

Response (unmarked):

{
    "marked": false,
    "count": 12,
    "count_label": "12 going",
    "timing": "upcoming"
}

Permission: is_user_logged_in()

Implementation: Delegates to ec_users_toggle_event() from extrachill-users.

2. Get Event Attendance

GET /extrachill/v1/concert-tracking/event/{event_id}

Get attendance info for a specific event (public).

Response:

{
    "count": 47,
    "count_label": "47 were there",
    "timing": "past",
    "user_marked": true,
    "attendees": [
        { "user_id": 1, "display_name": "Chubes", "avatar_url": "..." },
        { "user_id": 5, "display_name": "Zach U", "avatar_url": "..." }
    ]
}

Permission: Public read (__return_true), user_marked requires auth.

3. Get User Shows

GET /extrachill/v1/concert-tracking/user/{user_id}/shows

Paginated concert history for a user.

Query params:

  • page (default 1)
  • per_page (default 20)
  • periodupcoming | past | all (default all)
  • year — filter by year (e.g. 2025)
  • date_from, date_to — custom date range

Response:

{
    "shows": [
        {
            "event_id": 123,
            "title": "Goose at Moody Center",
            "event_date": "2026-04-05",
            "venue": "Moody Center",
            "city": "Austin",
            "artists": ["Goose"],
            "timing": "upcoming",
            "thumbnail": "..."
        }
    ],
    "total": 47,
    "pages": 3,
    "page": 1
}

Permission: Own shows = is_user_logged_in(), other users = public (for future public profiles).

4. Get User Stats

GET /extrachill/v1/concert-tracking/user/{user_id}/stats

Aggregate stats for a user's concert history.

Query params:

  • year — filter by year
  • date_from, date_to — custom date range

Response:

{
    "total_shows": 47,
    "unique_venues": 23,
    "unique_artists": 38,
    "unique_cities": 12,
    "top_artists": [
        { "name": "Goose", "slug": "goose", "count": 5 },
        { "name": "Billy Strings", "slug": "billy-strings", "count": 4 }
    ],
    "top_venues": [
        { "name": "Moody Center", "slug": "moody-center", "count": 8 },
        { "name": "Stubb's", "slug": "stubbs", "count": 6 }
    ],
    "top_cities": [
        { "name": "Austin", "slug": "austin", "count": 22 },
        { "name": "Charleston", "slug": "charleston", "count": 15 }
    ],
    "shows_by_year": {
        "2026": 12,
        "2025": 25,
        "2024": 10
    }
}

Permission: Own stats = is_user_logged_in(), other users = public.

Implementation

New file: inc/routes/concert-tracking.php

All handlers follow the thin-wrapper pattern:

register_rest_route( 'extrachill/v1', '/concert-tracking/toggle', [
    'methods'  => 'POST',
    'callback' => function( $request ) {
        $event_id = absint( $request['event_id'] );
        $blog_id  = absint( $request->get_param( 'blog_id' ) ?: get_current_blog_id() );
        $user_id  = get_current_user_id();
        
        $result = ec_users_toggle_event( $user_id, $event_id, $blog_id );
        $count  = ec_users_get_event_mark_count( $event_id, $blog_id );
        $timing = ec_users_get_event_timing( $event_id );
        
        return rest_ensure_response( [
            'marked'      => $result['marked'],
            'count'       => $count,
            'count_label' => ec_users_format_count_label( $count, $timing ),
            'timing'      => $timing,
        ] );
    },
    'permission_callback' => 'is_user_logged_in',
] );

Route file auto-discovered by extrachill-api's RecursiveIteratorIterator.

Acceptance Criteria

  • Toggle endpoint works (mark → unmark → mark cycle)
  • Event endpoint returns correct count + timing
  • User shows endpoint returns paginated results with event details
  • User stats endpoint returns correct aggregates
  • Year/date range filters work on shows + stats endpoints
  • Auth required for toggle, public read for event/user endpoints
  • function_exists() guard for extrachill-users functions
  • All responses follow WP REST API conventions

Dependencies

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions