Skip to main content
The application is built around a single core entity — NewsItem — that represents one parsed press clipping extracted from a WhatsApp log. Items flow from raw ingestion through categorisation and finally into WordPress via the REST API. This page documents every field on that entity, how the DataService manages application state, and the structures that wrap items at runtime and at rest.

NewsItem interface

Every parsed clipping is represented as a NewsItem. The interface is defined in the shared types file and used throughout ingestion, display, and persistence.
export interface NewsItem {
  id_: string;
  sendDate: string;
  sendTime: string;
  media: string;
  program: string;
  text: string;
  resume?: string;
  iaResume?: string;
  link: string;
  startTime: string;
  endTime: string;
  topic: string[];
  topics: string;
  destacada?: boolean;
  copy: boolean;
}

Field reference

id_
string
required
Unique identifier for the item. Generated by concatenating the date and time components of the WhatsApp message timestamp in the format DDMMYYHHmmss. For example, a message sent on 15 March 2024 at 09:04:30 produces 150324090430.
sendDate
string
required
The date the WhatsApp message was sent, formatted as d/m/YY (e.g., 15/3/24). Parsed directly from the log timestamp.
sendTime
string
required
The time the WhatsApp message was sent, formatted as H:mm:ss AM/PM (e.g., 9:04:30 AM). Parsed directly from the log timestamp.
media
string
required
The resolved name of the media outlet. The parser looks up the raw sigla (abbreviation) found in the message against the mediosYProgramas lookup table. If no match is found, the raw sigla is stored as-is.
program
string
required
The resolved name of the broadcast programme. Like media, the raw sigla is resolved against mediosYProgramas. If the message originates from a web source, the fallback value is "web". If no match is found, the raw sigla is stored.
text
string
required
The cleaned body of the news item. During parsing, the following elements are stripped from the raw message text: any URL, any time range (e.g., 09:00 - 09:30), and the leading media/programme prefix. What remains is the substantive content of the clipping.
resume
string | undefined
The transcript portion extracted from an IA.TXT attachment when present in the WhatsApp message. Only populated when the message includes an AI-generated transcript file. undefined when no transcript is attached.
iaResume
string | undefined | null
The operator-edited summary used as the dispatch copy. Operators write or refine this field in the panel UI before sending digests. undefined before the operator has edited it; null may be set programmatically to clear it. This value is also used as the WordPress post title when the item is persisted.
The URL extracted from the message body. The parser isolates the first URL found in the raw text and stores it here. May be an empty string if no URL was present.
startTime
string
required
The broadcast start time in HH:MM format (e.g., 09:00), parsed from the time range in the message body.
endTime
string
required
The broadcast end time in HH:MM format (e.g., 09:30), parsed from the time range in the message body.
topic
string[]
required
An array of category names that matched the item’s text against the keyword rules loaded from Google Sheets. An item may belong to multiple categories. An empty array means no category matched.
topics
string
required
A semicolon-joined string representation of the topic array (e.g., "Salud;Educación"). This field is computed and set immediately before the item is persisted to WordPress, because ACF stores it as a plain text field rather than a repeater.
destacada
boolean | undefined
When true, the item is marked as featured and surfaces at the top of the dispatch panel for its category. undefined is treated as false by the UI. Operators toggle this flag manually.
copy
boolean
required
A UI state flag that controls whether the item card is in view mode or edit mode in the panel. false means view mode; true activates the editable copy field. This flag is not persisted to WordPress.

id_ generation

The unique identifier is built by concatenating six date/time components extracted from the WhatsApp log timestamp, with no separators:
// Pseudocode — actual extraction uses the parsed date/time fields
const id_ = DD + MM + YY + HH + mm + ss;
// Example: "150324090430"
SegmentMeaningExample
DDDay15
MMMonth03
YYYear (2-digit)24
HHHour (24h)09
mmMinute04
ssSecond30
Because id_ is derived from the send timestamp, two messages arriving within the same second from the same log file would produce a collision. In practice this is avoided by the one-message-per-row structure of the WhatsApp export format.

DataService state

DataService is an Angular service that acts as the central state store for the application. All components read from and write to these properties.
palabrasClaveOriginal: Array<{ palabra: string; padre: string }> = [];
mediosYProgramas: Array<{ sigla: string; nombre: string }> = [];
categorias: string[] = [];
newsItems: [] = [];
newAgrupadas: Array<{ padre: string; noticia: any[] }> = [];
noticiasGuardadas: [] = [];
loading: boolean = false;
estadisticasList: any[] = [];
mediosList: any[] = [];
statsDesde: any = '';
statsHasta: any = '';

Property reference

palabrasClaveOriginal
Array<{ palabra: string; padre: string }>
The raw keyword rules loaded from Google Sheets. Each entry maps a keyword (palabra) to a parent category name (padre). The categorisation engine iterates this array to assign topic values to each NewsItem.
mediosYProgramas
Array<{ sigla: string; nombre: string }>
The lookup table for resolving media outlet and programme abbreviations to their full names. Used during parsing to populate NewsItem.media and NewsItem.program.
categorias
string[]
The de-duplicated list of unique category names derived from palabrasClaveOriginal. Used to populate navigation tabs and filter dropdowns across the UI.
newsItems
[]
The in-memory list of parsed NewsItem objects produced by the current import session. Populated after the operator pastes and parses a WhatsApp log.
newAgrupadas
Array<{ padre: string; noticia: any[] }>
The categorised, grouped view of newsItems. Each entry groups the items belonging to one parent category. See the newAgrupadas structure section below.
noticiasGuardadas
[]
The list of news items already persisted to WordPress, fetched via leerNoticias(). Used by copyAll() to determine whether to create or update each item.
loading
boolean
Global loading flag. Set to true while async operations (API fetches, saves) are in progress. Components bind to this to show/hide loading indicators.
estadisticasList
any[]
The dataset used by EstadisticasComponent to render analytics. Populated from noticiasGuardadas after filtering is applied.
mediosList
any[]
The distinct list of media outlets present in the current statistics dataset. Used to populate the media-outlet filter in the analytics view.
statsDesde
any
The start date for the statistics date range filter. Bound to the date picker input in EstadisticasComponent. Empty string means no lower bound.
statsHasta
any
The end date for the statistics date range filter. Bound to the date picker input in EstadisticasComponent. Empty string means no upper bound.

newAgrupadas structure

newAgrupadas is the pivotal runtime structure that connects parsed items to the dispatch panels. After categorisation, each NewsItem that matched at least one keyword is placed into one or more groups.
newAgrupadas: Array<{
  padre: string;    // category name, e.g. "Salud"
  noticia: NewsItem[];
}>;
Example:
[
  {
    "padre": "Salud",
    "noticia": [
      {
        "id_": "150324090430",
        "media": "Canal 12",
        "program": "Mediodía",
        "text": "Inauguraron el nuevo centro de salud en barrio Müller.",
        "topic": ["Salud"],
        "topics": "Salud"
      }
    ]
  },
  {
    "padre": "Educación",
    "noticia": [...]
  }
]
PanelComponent receives the cat route parameter and finds its matching entry in newAgrupadas to display the relevant items. If newAgrupadas is empty when PanelComponent initialises, it redirects to /.

WordPress ACF wrapper

When items are fetched from the WordPress REST API, each post object wraps the NewsItem fields inside an acf property — the standard envelope produced by the Advanced Custom Fields plugin.
{
  "id": 42,
  "title": { "rendered": "Inauguraron el nuevo centro de salud..." },
  "acf": {
    "id_": "150324090430",
    "sendDate": "15/3/24",
    "sendTime": "9:04:30 AM",
    "media": "Canal 12",
    "program": "Mediodía",
    "text": "Inauguraron el nuevo centro de salud en barrio Müller.",
    "link": "https://example.com/nota",
    "startTime": "09:00",
    "endTime": "09:30",
    "topics": "Salud",
    "destacada": false
  }
}
When reading saved items, access fields as item.acf.fieldName. For example:
const savedId = item.acf.id_;
const savedTopics = item.acf.topics;
The topic array field is not stored in WordPress. Only the semicolon-joined topics string is persisted. When reading items back from the API, you must split item.acf.topics on ";" to reconstruct the array if needed.

Build docs developers (and LLMs) love