Urgent.News

One page, thousands of outlets. See who else covered it.

Editions

Tech

Modeling Recurring vs One-Off Events in a Bilingual City Guide

An events listing looks like a simple content type until you notice that two things share the shape and behave nothing alike. A dated concert exists once. It happens on 2026-09-10, and after that it is history. A weekly salsa night has no date at all. It happens every Thursday, indefinitely, until the venue stops running it. Model both as one Event row with a date column and you will hit the same…

In a bilingual city guide for Santa Cruz de la Sierra, events are classified as either one-off or recurring. Dated events, such as a concert on 2026-09-10, have an ISO date and a random suffix in their slugs. Recurring events, like weekly salsa nights, have a '-weekly' marker but no date in their slugs. This distinction enables the guide to route events differently.

When modeled as one event row with a date column, four problems arise. First, the archive policy differs for dated and recurring events. Dated events should be removed from the listing the day after they occur while still returning a 200 status code for inbound links and indexed pages. Recurring events, however, should never fall out of the listing as they have no expiry.

Mixing them in one table keyed on date causes recurring events with a null date to vanish silently. Second, schema.org/Event schema requires a startDate field, but it is not feasible to emit a valid Event for "every Thursday" using a single node. This assertion of a specific occurrence in a machine-readable format is incorrect. Third, internationalization (i18n) complicates the guide's URL structure.

While localized path segments improve user experience and regional search, translating the slug body leads to unrelated identifiers, multiple hreflang pairs, and permanent URL forks. Translating the segment, keeping the identifier stable, and deriving hreflang pairs avoids these issues. Lastly, the source data arrives as public announcements from venues, lacking a stable identifier, schema, and update semantics.

Deduplication cannot key on the title alone, as the same event is announced repeatedly with different phrasing. A composite key, including venue, date, and rough time, with title as a similarity check, is a better solution. Additionally, each record needs a verified_at timestamp separate from updated_at. updated_at indicates when the row changed, while verified_at shows when a human last confirmed the record against the source, enabling the display of staleness warnings or recheck decisions before publication.

The guide, Bolivamos, uses a discriminated type instead of separate tables to address these challenges effectively.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

The Pokédex Is 2.4MB. My Users Never Download It.

A random Pokémon generator is a strange thing to optimize. The entire product is: press a button, get a Pokémon. But behind that one button sits the whole dataset of the franchise — 1,025 species…

  • Pokédex weighs 2.4MB when downloaded.
  • Author compiles dataset into 13MB JSON file.
  • Discards unnecessary details to reduce download.

Building an offline CAD tool that can also turn photos into 3D meshes

I originally put PolyForge together as a ChatGPT skill, just for OpenSCAD, so I could describe a bracket or an enclosure and get back something I could actually print on my modified Ender 3 V2 or my…

  • PolyForge is a standalone Python package for CAD tool
  • Added offline photogrammetry capability without cloud API
  • Runs COLMAP for sparse reconstruction, OpenMVS for dense mesh

More from Sunday 16 August →