Migrating de datos CSV a Rangos de Tiempo de Fecha

Publicado por Gerardo Hernández el 27 de Enero de 2017

Como se sabe el módulo Datetime range actualmente es experimental dentro del core de Drupal 8, sin embargo, éste módulo permite crear intervalos de tiempo específicos, ejemplo de ello puede ser: la fecha de creación a la expiración de un producto, un horario de un taller o evento el cual tenga diferentes bloques de horas, o las reservaciones de habitaciones de un hotel, etc.

El uso de él es muy simple, pero ¿Cómo funciona una migración a un campo de rango de tiempo de fecha?, como respuesta a esta pregunta he creado un ejemplo que consta de dos segmentos, cada migración será apoyada por un patch que crea un plugin dentro del módulo Migrate del Core para formatos de Fecha; La primera será a partir de subcampos, y la siguiente haciendo iteraciones de procesos, haciendo énfasis en que usarán diferentes plugins de procesos de Migrate.

Antes hay que crear contenido para migrar, por lo que haremos dos archivos CSV dentro del directorio sites/default/files/csv para alojarlo, y le tendrán cada nombre de forma simbólica, la primera fila en cada uno contiene las cabeceras de cada columna como si fueran nombres máquina para agilizar el proceso de configuración.

#Drugs.csv
id,name,description,date_range,type
1,Acetaminophen,"this is a common medicine very effective",20/04/2015-20/04/2016,"Analgesics,Antipyretics"
2,Diclofenac,"This is topical application medicine",06/07/2014-06/07/2015,Anti-inflammatory
3,Dicloxacillin,"Synthetic penicillin",28/12/2016-28/12/2017,Antibiotics
4,Iodo,"Low cost antiseptic",11/06/2015-11/06/2016,Antiseptics
5,Prednisolone,"Anti-inflammatory activity",08/10/2014-08/10/2015,Anti-inflammatory
6,Peroxide,"Does not cause tissue irritation, stimulates blood clotting",24/04/2016-24/04/2017,Antiseptics
7,Bisolgrip,"Cough syrup effective",10/03/2015-10/03/2016,Analgesics
8,Amoxicillin,"Prevents bacterial infections",17/09/2014-17/09/2015,Antibiotics
9,Dipyrone,"Of oral use, with anti-inflammatory qualities",25/12/2016-25/12/2017,"Analgesics,Antipyretics,Anti-inflammatory"

Éste define cada fila con un identificador, el nombre de un medicamento común en Centroamérica, un intervalo de fechas ficticias, simulando su inicio y expiración en el mercado; y su categoría de medicamento.

#Training.csv
id,topic,description,teacher,days,hours
1,Node.js,"Learn about this excellent JavaScript runtime breaking paradigms.",John Miller,"21/11/2016,23/11/2016,25/11/2016,29/11/2016,01/12/2016","07:00:00-08:45:00,15:00:00-16:45:00"
2,Html5,"Integrating new tags for a more dynamic and easy interaction for the developer.",Cloe Nutter,"22/11/2016,24/11/2016,28/11/2016,30/11/2016,02/12/2016","11:00:00-12:45:00,09:00:00-10:45:00"
3,CSS3,"Creating new projection of websites.",Mary Ludwing,"21/11/2016,23/11/2016,25/11/2016,29/11/2016,01/12/2016","09:00:00-10:45:00,13:00:00-14:45:00"
4,Javascript,"Learns its advanced form of programming.",Gerald Schneider,"22/11/2016,24/11/2016,28/11/2016,30/11/2016,02/12/2016","13:00:00-14:45:00,11:00:00-12:45:00"
5,PHP 5 to 7,"Interacts with the backend and you have your websites service easily.",Kevin Robson,"21/11/2016,23/11/2016,25/11/2016,29/11/2016,01/12/2016","15:00:00-16:45:00,07:00:00-08:45:00"

Acá mostraremos posibles entrenamientos o cursos, donde cada uno posee un identificador, el nombre del tópico a recibir, una breve descripción, quien impartirá el entrenamiento, las fechas de los días que se dará, en forma de matriz; y dos posibles bloques de horas que en los que podría recibir tal tópico.

Descargamos el patch y aplicamos al proyecto puede ser con herramienta que tu desees sino te recomiendo patchutils y utiliza el comando.

patch -p1 < patch_correspondiente.patch

Configuración de Migración 1: Creación y Expiración de Fármacos

Ya que CSV file contiene un campo para la categoría, demanda crear un tipo de término de taxonomía que llevará el mismo nombre que el archivo.

Luego de haber definido el contenido a migrar y determinado la Referencia de Entidad necesitada, es necesario tener un contenedor, por lo cual agregaremos un nuevo Content type llamado Medicines con los siguiente campos:

Nombre de Campo Tipo de Campo
Body Text(formatted, long with summary)
Expire Date Date Range
Type Entity reference

 

 

 

 

Exporta tus configuraciones con drush, así mantendrás registro de cambios del sitio; usa el comando:

drush cex -y

Si has realizado todo lo anterior, hemos llegado al punto de crear el archivo YAML de configuración dentro del directorio /sync el cual llamaremos migrate_plus.migration.medicines.yml. Si deseas, antes puedes ver Migración de datos CSV a párrafos donde notarás algunas configuraciones son muy parecidas.

id: medicines
migration_tags:
  - CSV
migration_group: null
label: 'Example of migration with Datetime Range'
source:
  plugin: csv
  path: 'public://csv/drugs.csv'
  header_row_count: 1
  keys:
    - id
process:
  type:
    plugin: default_value
    default_value: medicines
  title: name
  body: description
  field_expire_date/value:
    -
      plugin: explode
      source: date_range
      delimiter: '-'
    -
      plugin: extract
      index:
        - '0'
    -
      plugin: format_date
      from_format: d/m/Y
      to_format: Y-m-d
  field_expire_date/end_value:
    -
      plugin: explode
      source: date_range
      delimiter: '-'
    -
      plugin: extract
      index:
        - '1'
    -
      plugin: format_date
      from_format: d/m/Y
      to_format: Y-m-d
  field_type:
    -
      plugin: explode
      source: type
      delimiter: ','
    -
      plugin: entity_generate
destination:
  plugin: 'entity:node'
migration_dependencies:
  require: { }
  optional: { }
  • field_expire_date/value y field_expire_date/end_value: Como puedes ver necesitamos definir los subcampos del real, el primer plugin Explode nos retornará un arreglo de valores, del cual seleccionamos el necesario haciendo uso del plugin Extract argumentando el índice del valor que queremos de la matriz devuelta anteriormente, en el caso de value su índice es ‘0’ y el de end_value es ‘1’.

Al final hacemos uso del plugin FormatDate permitiendo que cualquier formato de fecha o tiempo que hayamos usado como fuente, convertirlo bajo ISO 8601 que será su destino.

  • field_type: Cabe la posibilidad de tener una o más categorías, por ello esperamos el arreglo que nos devolverá el primer plugin, y luego el siguiente plugin del módulo Migrate Plus EntityGenerate entabla cada Referencia de Entidad al término de taxonomía; para una mejor perspectiva puedes observar Entity Lookup & Generate plugins de procesos de migración.

Importa la configuración de manera que ya sea reconocida por medio de drush con el comando:

drush cim -y

Configuración de Migración 2: Horario de Entrenamiento.

Dado que este no utiliza ningún tipo de Referencia de Entidad, creamos el content type necesario para almacenar el contenido del CSV.

Nombre de Campo Tipo de Campo
Body Text(formatted, long with summary)
Date Date range
Teacher Text(Plain)

 

Siempre has una exportación de configuraciones con drush, así evitas cualquier conflicto. Crea el siguiente archivo YAML dentro del directorio de configuraciones,con el nombre de migrate_plus.migration.training.yml.

id: training
migration_tags:
  - CSV
migration_group: null
label: 'Example of migration with Datetime Range'
source:
  plugin: csv
  path: 'public://csv/training.csv'
  header_row_count: 1
  keys:
    - id
process:
  title: topic
  body: description
  field_teacher: teacher
  day1:
    -
      plugin: explode
      delimiter: ','
      source: days
    -
      plugin: extract
      index:
        - '0'
  day2:
    -
      plugin: explode
      delimiter: ','
      source: days
    -
      plugin: extract
      index:
        - '1'
  day3:
    -
      plugin: explode
      delimiter: ','
      source: days
    -
      plugin: extract
      index:
        - '2'
  day4:
    -
      plugin: explode
      delimiter: ','
      source: days
    -
      plugin: extract
      index:
        - '3'
  day5:
    -
      plugin: explode
      delimiter: ','
      source: days
    -
      plugin: extract
      index:
        - '4'
  hours1:
    -
      plugin: explode
      delimiter: ','
      source: hours
    -
      plugin: extract
      index:
        - '0'
  hours2:
    -
      plugin: explode
      delimiter: ','
      source: hours
    -
      plugin: extract
      index:
        - '1'
  start_time1:
    -
      plugin: explode
      delimiter: '-'
      source: '@hours1'
    -
      plugin: extract
      index:
        - '0'
  end_time1:
    -
      plugin: explode
      delimiter: '-'
      source: '@hours1'
    -
      plugin: extract
      index:
        - '1'
  start_time2:
    -
      plugin: explode
      delimiter: '-'
      source: '@hours2'
    -
      plugin: extract
      index:
        - '0'
  end_time2:
    -
      plugin: explode
      delimiter: '-'
      source: '@hours2'
    -
      plugin: extract
      index:
        - '1'
  start_date1:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day1'
        - '@start_time1'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  end_date1:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day1'
        - '@end_time1'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  start_date2:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day2'
        - '@start_time2'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  end_date2:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day2'
        - '@end_time2'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  start_date3:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day3'
        - '@start_time1'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  end_date3:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day3'
        - '@end_time1'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  start_date4:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day4'
        - '@start_time2'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  end_date4:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day4'
        - '@end_time2'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  start_date5:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day5'
        - '@start_time1'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  end_date5:
    -
      plugin: concat
      delimiter: ' '
      source:
        - '@day5'
        - '@end_time1'
    -
      plugin: format_date
      from_format: 'd/m/Y H:i:s'
      to_format: 'Y-m-d\TH:i:s'
  union1:
    plugin: get
    source:
      - '@start_date1'
      - '@end_date1'
  union2:
    plugin: get
    source:
      - '@start_date2'
      - '@end_date2'
  union3:
    plugin: get
    source:
      - '@start_date3'
      - '@end_date3'
  union4:
    plugin: get
    source:
      - '@start_date4'
      - '@end_date4'
  union5:
    plugin: get
    source:
      - '@start_date5'
      - '@end_date5'
  field_date:
    -
      plugin: get
      source:
        - '@union1'
        - '@union2'
        - '@union3'
        - '@union4'
        - '@union5'
    -
      plugin: iterator
      process:
        value: '0'
        end_value: '1'
destination:
  plugin: 'entity:node'
  default_bundle: training
migration_dependencies:
  require: {  }
  optional: {  }

En comparación con la anterior configuración de migración, ésta requiere ciertas variables, ya que parte del contenido se tomará de conjuntos de datos de la columna days y los combinará segmentos de hours, para ir proyectando los rangos en diferentes tiempos para un día.

  • Las variables day1, day2, day3, day4, day5 obtienen una fecha diferente del arreglo, según el índice argumentado, hours1, y hours2 conseguirán dos matrices, las cuales contendrán diferentes horas para intervalos de tiempo, pero no podrían combinarse con las diferentes fechas ya extraídas, sino que hay  que desmembrar aún, por ello se utilizan start_time1, end_time1, start_time2, y end_time2 donde ya se hacen llamados a las variables de proceso, al pasarlas al fuente así ‘@hours1’, cada una se quedará con un dato de hora en singularidad, así ya podrán realizar las combinaciones necesarias.
  • Con start_date1, end_date1, start_date2, end_date2, start_date3, end_date3, start_date4, end_date4, start_date5, end_date5, unimos con el plugin Concat una fecha a dos diferentes horas, así se van creando intervalos de tiempo, no obstante, se les dá el formato apropiado ya se habrá notado, aunque todavía no se han establecidos los rangos, y por ello creamos union1, union2, union3, union4, union5 acá se combinan los lapsos de tiempo creando así los esperados valores a migrar.
  • El campo field_date sólo hará una llamada al plugin Get para conseguir los valores que tienen cada variable union y estos son depositados consecutivamente en el plugin Iterator, quien gestiona por iteración un proceso interno donde almacena en una o muchas propiedades de un campo un valor, por ello el subcampo value recibe un índice ‘0’ correspondiente al valor de la unión, y end_value recibe un índice ‘1’ teniendo como pauta dicha unión.

Para finalizar puedes nuevamente importar, ya que hay una nueva configuración; finaliza haciendo las dos migraciones ya sea al mismo tiempo o individualmente así:

drush mi --all
drush mi medicines
drush mi training

Estos son dos pequeños ejemplos de cómo realizar éste tipo migración, espero realmente les sea de mucha ayuda.

¿Está buscando ayuda para una migración o actualización de Drupal? Independientemente de la complejidad del sitio o de los datos, MTech puede ayudarle a pasar de un CMS privado o actualizarlo a la última versión: Drupal 8.

Escríbanos sobre su proyecto y nos pondremos en contacto con usted dentro de 48 horas.