Skip to main content
Curation is the human-in-the-loop step between parsing and dispatch. After the file is parsed and items are auto-categorized, an operator reviews each item in the ingestion view, corrects any extraction errors, and refines summaries before the content is sent to officials.

The ingestion view

The ingestion view (served by ImportarComponent at /controlmedios) displays all parsed news items as a list. Each item shows:
  • The detected media outlet and program
  • The full message text with keyword matches highlighted
  • The editable iaResume summary field
  • The assigned categories from the topic array
  • The Destacadas toggle
The list reflects the current state of the parsed session. Items remain in this view until the operator navigates to a category panel or starts a new upload.

Keyword highlighting

The resaltarTexto() function wraps matched keywords in the item text with a <span class="resaltado"> element so operators can quickly verify that the correct categories were assigned. It builds an accent-aware character-class regex for each keyword:
resaltarTexto(texto: string, i: any): string {
  const palabrasClaveOriginal = this.palabrasClaveOriginal;

  let textoResaltado = texto;

  palabrasClaveOriginal.forEach(({ palabra }) => {
    const regexStr = palabra
      .split('')
      .map((letra: string) => {
        const base = letra.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
        const vocales: { [key: string]: string } = { a: 'á', e: 'é', i: 'í', o: 'ó', u: 'ú' };
        const acento = vocales[base] || '';
        const variantes = [base, acento, letra].filter(Boolean).join('');
        return `[${variantes}]`;
      })
      .join('');

    const regex = new RegExp(
      `(^|[^\\wáéíóúÁÉÍÓÚ])(\\b${regexStr}\\b)(?=[^\\wáéíóúÁÉÍÓÚ]|$)`,
      'gi'
    );

    textoResaltado = textoResaltado.replace(
      regex,
      (match, pre, palabraCoincidente) =>
        `${pre}<span class="resaltado">${palabraCoincidente}</span>`
    );
  });

  return textoResaltado;
}
The regex builds character classes that accept both the plain and accented form of each vowel, so a keyword stored as córdoba also highlights cordoba in the displayed text.
If a highlight you expect is missing, check whether the keyword exists in the Google Sheets source with the correct spelling. See the categorization page for details on how keyword matching works.

Editing summaries

Each news item has an iaResume field — the summary that appears in the dispatched digest. Operators can edit this field directly in the ingestion view.

Inline editing mode

The editingIndex state variable (declared as number | null) tracks which item is currently in edit mode. When an item is in edit mode, its summary renders as an editable input. All other items render as read-only text.
editingIndex: number | null = null;
Only one item can be in edit mode at a time.

Managing categories per item

The auto-assigned categories can be adjusted manually after review using two methods on ImportarComponent.

Adding a category — agregarCategoria()

agregarCategoria(categoria: string, i: number): void {
  this.newAgrupadas = this.dataService.getAgrupadas();
  const newsItem = this.newsItems[i];

  if (categoria === 'Destacadas') {
    // Toggle the featured flag
    newsItem.destacada = !newsItem.destacada;
    if (newsItem.destacada) {
      if (!newsItem.topic.includes('Destacadas')) {
        newsItem.topic.push('Destacadas');
      }
      this.agruparNoticias(newsItem);
    } else {
      const index = newsItem.topic.indexOf('Destacadas');
      if (index > -1) newsItem.topic.splice(index, 1);
      this.desagruparNoticias(newsItem, 'Destacadas');
    }
  } else {
    if (!newsItem.topic.includes(categoria)) {
      newsItem.topic.push(categoria);
      this.agruparNoticias(newsItem);
    }
  }

  this.dataService.setNews(this.newsItems);
  this.dataService.setAgrupadas(this.newAgrupadas);
}
The method takes the category name string and the index i of the item in this.newsItems. It guards against duplicate category additions. For Destacadas specifically, it toggles the destacada boolean rather than simply appending.

Removing a category — quitarCategoria()

quitarCategoria(categoria: string, i: number): void {
  this.newAgrupadas = this.dataService.getAgrupadas();
  const newsItem = this.newsItems[i];
  const topicIndex = newsItem.topic.indexOf(categoria);

  if (topicIndex > -1) {
    newsItem.topic.splice(topicIndex, 1);
    this.desagruparNoticias(newsItem, categoria);
    if (categoria === 'Destacadas') {
      newsItem.destacada = false;
    }
    this.dataService.setNews(this.newsItems);
    this.dataService.setAgrupadas(this.newAgrupadas);
  }
}
Removing Destacadas also resets destacada to false.
Removing all categories from an item does not restore the General fallback automatically. If you remove the last category, manually add General to keep the item visible in a panel.

Toggling Destacadas

The Destacadas flag is managed inside agregarCategoria() — there is no separate toggle method. Calling agregarCategoria('Destacadas', i) flips newsItem.destacada and adds or removes the item from the Destacadas category group accordingly. Items with destacada: true are visually distinguished in the ingestion view and are included in a dedicated featured section when dispatched.

Copying a single item from the ingestion view

The copy() method on ImportarComponent formats a single item and writes it to the clipboard. Note this is a simplified format — the full summary is not included at the ingestion stage:
copy(text: any): void {
  const texto =
    '->** \n' +
    '-> ' + text.media + ' / ' + text.program + '\n' +
    '🔗 ->' + text.link.trim();

  navigator.clipboard.writeText(texto).then(() => {
    this.toast = true;
    this.cerrarToast();
  });
}
The output format is:
->** 
-> Media outlet / Program name
🔗 ->https://example.com/article
This ingestion-view copy format intentionally leaves the summary line blank (->**). The full iaResume-populated format is used in the dispatch panel — see the dispatch page for the copyAll() format.

Categorization

How items are initially assigned to government areas.

Dispatch

How curated digests are sent to officials by category.

Build docs developers (and LLMs) love