Overview
The package’s data comes from five independent sources, updated on
five different schedules. See vignette("Data Provenance")
for how every dataset in data/ maps onto these sources and
the pipeline stages below.
-
Show data - dates, venues, attendance, sound
quality, played-with bands, notes, and tracklists, scraped from the Fugazi Live
Series website. Fully automated via
scrape_fls_shows(). - Tags/duration data - song-level timings, taken from my own personally-tagged MP3 collection. Manual, done outside this repository.
- Venue coordinates - latitude/longitude for each venue. Manual, looked up on Google Maps as new venues show up.
-
Song/release/duration data - release, track number,
instrumental/vocalist, and duration metadata per song, hand-maintained
in
inst/extdata/releases_songs_durations_wikipedia.csvagainst the Wikipedia Fugazi discography page. Manual, done outside this repository. -
Song tempo data - beats-per-minute per song,
hand-compiled in
inst/extdata/song_tempo_bpm_data.csv. Manual, done outside this repository; also read directly by the Shiny app.
All five feed into Repeatr_1() (song tempo data is read
directly by the Shiny app instead), which combines them (plus a fair
amount of hand-written, gid-keyed correction code accumulated over time)
into the package’s data/*.rda objects.
Repeatr_Updatr(really = "really") runs
Repeatr_1() and the rest of the modelling pipeline
(Repeatr_2 through Repeatr_5) in sequence, and
the Fugazetteer
Shiny app is just a consumer of those same rebuilt
data/*.rda objects - it never reads a CSV directly (song
tempo data is the one exception, read straight from
inst/extdata/song_tempo_bpm_data.csv), so once the package
data is rebuilt and reinstalled the app picks it up automatically.
None of steps 1-4 need to happen together. Run whichever one has new material, then rebuild (step 5) and redeploy (step 6).
data-raw/build_data.R is the canonical, runnable version
of steps 1-5 below - this vignette explains the why, that script is what
to actually run.
1. Updating show data
scrape_fls_shows() (R/scrape_fls_shows.R)
discovers the current show listing directly from the site - including
any pages added since the last run - and scrapes each show’s detail page
for date, venue, door price, attendance, recorded/mastered by, original
source, sound quality, played-with, any official notes, and the
tracklist. It also attaches tour (e.g. “1988 Fall European
Tour”) from the listing pages’ own tour headings - that’s the only field
here that doesn’t come from the show’s own detail page, so a run with
gids supplied directly (bypassing listing discovery) won’t
be able to fill it in.
Test on a small slice first:
test_shows <- scrape_fls_shows(max_listing_pages = 1, max_shows = 3, sleepseconds = 2)By default it’s incremental: it only scrapes shows not already in the
existing dataset (fls_data.csv unless you point
existing_data elsewhere), so routine re-runs are quick. It
also picks up on shows whose recording has newly gone from unavailable
to available (detect_changes, on by default) without
needing a full re-scrape - this is how ~50 shows that had gone from “no
tracklist yet” to fully available got picked up in one run without
touching the ~1000 shows that hadn’t changed.
To actually refresh the packaged file, for real, against the live site:
fls_data <- scrape_fls_shows(
update_existing = TRUE,
sleepseconds = 2,
mycsvfilename = "inst/extdata/fls_data.csv"
)A few things worth knowing:
- It respects a delay (
sleepseconds, default 2) between every request, listing pages and show pages alike - don’t reduce this to hammer the site. - A full run pages through every listing page and scrapes every target
show, which can be tens of minutes depending on how many shows are new.
max_shows/max_listing_pagescap it for testing. -
fugazi-live-all-access(FLS0000) is a standing all-access download bundle, not a show, and is always excluded automatically. -
fls_data.csvsupersedes the olderfugotcha.csv,gid_fls_id_sound_quality.csvandgid_fls_id_played_with.csv- those older files are left in place for reference but nothing reads them anymore. - If
fls_data.csvever needstour/city/state/countrybackfilled without a full show-by-show re-scrape (e.g. after adding one of these columns, or if some rows are missing it),scrape_fls_listing_data()crawls just the listing pages - a few dozen requests instead of 1000+ - and returns agid+ all four:fls_data <- fls_data %>% select(-tour) %>% left_join(scrape_fls_listing_data() %>% select(gid, tour), by = "gid").Repeatr_1()readstourstraight fromfls_data.csv/Repeatr0now, not from the olderfugazi-small.csvfile -city/state/countryare scraped the same way but haven’t been migrated over to source fromfls_data.csvyet.
2. Updating tags/duration data
Song-level durations (fls_tags,
fls_tags_show, and everything derived from them like
duration_summary, cumulative_duration_counts)
come from inst/extdata/fls_tags.txt, which isn’t produced
by any script - it’s exported from the mp3 files using kid3. Each show’s MP3s get tagged with
the track name and album set to
YYYYMMDD Venue, City, State, Country, and duration comes
along for free from the file itself.
To update: open the new mp3 files in kid3, export the tag data as
format “CSV quoted” with header
track; artist; album; name; duration and tracks
"%{track}";"%{artist}";"%{album}";"%{title}";"%{duration}".
Open the resulting file in a text editor, remove the quotation marks and
add spaces after the semi-colons using find and replace, then and add
the new data to the end of any existing
inst/extdata/fls_tags.txt file, or create the file if it
does not exist. Save as UTF-8, not the editor’s default
ANSI/system codepage - a venue or city name with an accent (é, ö, ã, …)
saved in the wrong encoding will parse fine at first but crash
Repeatr_1() with an invalid multibyte string
error once it reaches nchar()/gregexpr()
further down. fls_tags_importer() parses it, and
Repeatr_1() calls it automatically - no separate function
to run. If a new show’s album string doesn’t parse cleanly
(typos happen), Repeatr_1()’s “process tags data” section
is where the hand-written corrections for specific albums/venues live -
add a new mutate(album = ifelse(...)) line there following
the existing pattern if needed.
3. Updating venue coordinates
Venue coordinates now come primarily from
inst/extdata/fls_venue_geocoding_v2.csv, a local snapshot
of a private Google Sheet the coordinates are actually maintained in -
Repeatr_1() reads this file directly (matched to shows by
country/city/venue), not the live
sheet itself, since that sheet isn’t reliably available and isn’t part
of the formal package workflow. To update: look up new/corrected venues
on Google Maps in the sheet as usual, then download it and overwrite
inst/extdata/fls_venue_geocoding_v2.csv with the current
export.
inst/extdata/fugazi-small.csv is still consulted as a
fallback for any venue fls_venue_geocoding_v2.csv doesn’t
cover, so a show doesn’t lose its coordinates entirely just because a
new venue hasn’t been added to the sheet yet.
The older fls_venue_geocoding.csv file and
nscmov()’s (R/nscmov.R) to-do-list bookkeeping
around it are no longer part of this pipeline -
Repeatr_1() stopped reading that file once
fls_venue_geocoding_v2.csv became the source of truth.
They’re left in place for reference but nothing in
Repeatr_1() calls nscmov() or reads
fls_venue_geocoding.csv anymore; treat that workflow as
retired unless it gets revived deliberately.
4. Updating song/release/duration and tempo data
inst/extdata/releases_songs_durations_wikipedia.csv
carries release, track number, instrumental/vocalist, and duration
metadata per song, hand-maintained against the Wikipedia
Fugazi discography page. Repeatr_1() reads it into
songvarslookup and joins it onto the live, classified song
set by song title (song) - not by a
hardcoded id column, precisely so this file can’t silently drift out of
sync with Repeatr_1()’s classification rules the way it
once did. If you add, rename, or remove a song here,
Repeatr_1() will warning() at build time
listing any song names that don’t match between the live classification
and this file - see the songid reconciliation check in
R/Repeatr_1.R for what that looks like, and fix the
mismatch in this CSV’s song column before trusting the
rebuilt data.
inst/extdata/song_tempo_bpm_data.csv carries
hand-compiled tempo (BPM) per song. Unlike the other four sources, it is
not read by Repeatr_1() at all - the Shiny
app (inst/shiny/Fugazetteer/app.R) reads it directly - so
there is no automated check tying its song names to the live
classification; when a song’s title changes elsewhere, update it here by
hand too.
5. Rebuilding everything
Once any of sources 1-4 is updated, rebuild the package’s data objects:
Repeatr_Updatr(really = "really", update_stacks = TRUE)This runs
Repeatr_1() -> Repeatr_2() -> Repeatr_3() -> Repeatr_4() -> Repeatr_5()
and saves every downstream dataset (othervariables,
Repeatr0, Repeatr1,
gid_sound_quality, played_with,
shows_data, xray, fls_tags,
fls_tags_show, the choice-model outputs, and more) into
data/. It’s the really = "not_really" default
that stops this running by accident - always pass
really = "really" explicitly. It can take a while (the
choice model fit in Repeatr_4() is the slow part), so it’s
worth checking Repeatr_1() on its own first if you only
want to sanity-check the data-ingestion changes.
update_stacks = TRUE also regenerates
gid_initial_gid_sound_quality (the data behind the Shiny
app’s “stock” pages, via Repeatr_6()) -
data-raw/build_data.R’s rebuild call passes this by
default, so a normal rebuild keeps it current; omit it only if you
deliberately want to skip the (slower) stacks regeneration.
6. Reinstalling and redeploying
- Commit and push the updated
inst/extdata/*source files and the regenerateddata/*.rdafiles. - Reinstall the package from the updated source
(
devtools::install(), ordevtools::install_github("alexmitrani/Repeatr")once pushed). - Run the Shiny app locally to check nothing broke:
shiny::runApp("inst/shiny/Fugazetteer"). - Redeploy to shinyapps.io
(
rsconnect::deployApp("inst/shiny/Fugazetteer"), or the RStudio “Publish” button fromapp.R).
The app reads the package’s lazy-loaded data objects for everything
except song_tempo_bpm_data (read directly from its source
CSV, see step 4), so as long as steps 1-5 above ran cleanly there’s
nothing else to change in app.R itself.