El Mapa de Acción de Justicia Climática (CJAM) es una herramienta de mapeo personalizada que extrae 350 eventos y grupos de múltiples fuentes de datos (por ejemplo: ActionKit, EveryAction, CiviCRM) y muestra un mapa interactivo que los partidarios pueden usar para involucrarse.

Se puede incrustar en sitios web con muchas opciones de personalización (por ejemplo: preestablecer el centro del mapa en una ubicación, mostrar el texto y los botones del mapa en un idioma diferente, mostrar solo eventos relacionados con una campaña en particular, etc.).
Utiliza Mapbox para el mapa, OpenStreetMaps para el conjunto de mosaicos y Google Maps para la búsqueda.
La aplicación CJAM Extract, Transform Load (ETL) es un procesador de datos escrito en Python que se ejecuta cada 15 minutos y extrae datos de esas muchas fuentes (por ejemplo: EveryAction, CiviCRM) a través de API y consultas directas de SQL. Escribe el evento combinado y los datos del grupo en un archivo de datos JSON alojado en Amazon S3, que luego es consumido por el JavaScript CJAM.
Nos reunimos con 350 a mediados de junio, con las huelgas programadas para el 20 de septiembre y organizando impulsos en julio y agosto. Con plazos ajustados, un nuevo equipo y una nueva base de código, rápidamente nos pusimos manos a la obra para comprender los objetivos del mapa, su implementación actual y lo que había que hacer para cada hito.
En los proyectos que requieren cambios rápidos, es tentador sumergirse primero en la cola de problemas. Sin embargo, sabemos que un proyecto solo tiene éxito si todos están alineados con los objetivos generales del proyecto. Afortunadamente, el equipo del producto ya tenía una excelente documentación (¡incluso tenían una presentación de diapositivas!) Sobre cuál es el propósito del mapa de acción climática y sus audiencias clave.
350.org tuvo una presentación de diapositivas que detalla los objetivos y el público, lo que nos ayudó a obtener los conocimientos básicos necesarios para colaborar de manera efectiva.
Objetivos
Audiencias Primarias
Visitante del sitio
Insertador de mapas
Fue genial tener estos documentos en nuestra llamada inicial.
Familiarizarse con el funcionamiento interno del mapa de acción climática fue particularmente desafiante porque el código estaba esencialmente en dos estados: la rama principal con el JavaScript personalizado original y una rama refactorizadora donde estaba ocurriendo la transición a React.js. React es uno de los frameworks más populares y ampliamente utilizados. La conversión de la aplicación a React hizo que el código fuera más fácil de mantener y desarrollar. El desarrollador voluntario original había comenzado este proceso de conversión y había nuevas características escritas en la nueva forma Reaccionar, no disponibles hasta que se completara la refactorización.
Mauricio y Chris se reunieron con él para aclarar cómo ver la transición hasta el final. Luego se familiarizaron con la base de código y refactorizaron en el camino. Al comprender, por ejemplo, una función larga y compleja, y luego reescribirla en funciones discretas más pequeñas, pudimos simplificar el código, comprender nuestro funcionamiento interno y facilitar el trabajo para que el próximo desarrollador se una al proyecto .
Cuando se trabaja por primera vez con una base de código, lleva tiempo entender por qué un nuevo cambio no se mantiene o por qué se produce un error. Los registros son el mejor amigo de un desarrollador cuando se trata de depuración. Desafortunadamente, el registro disponible fue marcado. El ETL tenía un registro en ejecución, pero no se guardaba en un archivo para referencia futura o recuperación fácil. Chris mejoró el registro de errores e incluso agregó la integración de Slack enviando un mensaje al equipo cada vez que ocurría un error, ayudando a las personas a responder rápidamente a los problemas.
350.org tiene cientos de capítulos, repartidos en siete continentes, con miembros que hablan docenas de idiomas. Su herramienta de mapeo fue construida con esta diversidad en mente. Sirve como un poderoso dispositivo para contar historias (objetivo número uno), con un solo mapa que transmite el impresionante alcance del movimiento y no hace suposiciones sobre dónde está un visitante o qué está buscando.
Por otro lado, la movilización es más efectiva cuando proviene de personas que conocemos, de comunidades de las que formamos parte. Como tal, el mapa puede vivir en contextos más localizados, mostrando solo eventos y grupos relevantes para un escenario particular. Por ejemplo, el capítulo 350 Colorado puede mostrar un mapa ampliado en Mountain West, mientras que 350 Francia puede mostrar un mapa con solo eventos en francés.
Estos mapas personalizados se crean utilizando parámetros de inserción. Para hacer esto, un organizador de 350.org pegó el mapa en una página usando un iframe, pasando parámetros como el idioma, la ubicación y la fuente de datos al incluir un parámetro de consulta en la url.
Sin embargo, este enfoque era engorroso, técnicamente prohibitivo y propenso a errores. Descartamos el enfoque de iframe y lo reemplazamos con una serie de códigos cortos, un método más intuitivo, que realiza llamadas directas a la API de Mapa de Acción Climática para representar un mapa específico para las necesidades de un organizador.
Agregamos soporte para los siguientes códigos cortos:
Ahora los organizadores pueden crear cualquier cantidad de mapas con criterios que satisfagan las necesidades específicas de su campaña o comunidad.
With so many different events happening at any given time, the map risked overwhelming visitors looking to get involved. 350.org's designer Matthew Hinders-Anderson came up with the solution of applying different map pin styles to events depending on when they were happening. Past events have a subdued teal, while current and future events have a strong teal. To emphasize the storytelling (goal number) of the map, current events throb.
Para lograr esto, necesitábamos calcular la fecha y hora de un evento en relación con la hora actual. Desafortunadamente, muchos de los eventos no tenían zona horaria asociada a ellos. Sin embargo, todos tenían alguna forma de ubicación disponible. Chris encontró una práctica herramienta de Python llamada timezonefinder que calcula la zona horaria de un evento en función de la latitud y la longitud.
Con la zona horaria en la mano, Mauricio podría aplicar los diferentes colores (y parpadear) en función del tiempo del evento en relación con ahora.
Utilizamos Python para calcular la zona horaria de un evento en función de su latitud y longitud.
Con tantos eventos organizados, queríamos que los posibles huelguistas encontraran un evento para asistir rápidamente. Sin embargo, se encontraron embebedores de mapas, que a veces las búsquedas daban como resultado un mapa vacío, a pesar de que los eventos estaban cerca. Este es uno de los muchos desafíos de diseñar mapas interactivos. Un ejemplo fue un visitante que vivía en un suburbio cercano de Boston. Una búsqueda de Allston no resultaría nada, a pesar de que haya múltiples eventos dentro de un radio de 5 millas. Ajustamos el comportamiento del zoom para mostrar mejor los eventos cercanos.
Sin embargo, todavía había casos extremos. Abordamos esto mostrando un botón de "Alejar" si un visitante aparecía vacío. Al hacer clic, se aleja a un usuario al resultado más cercano.
Si un visitante no obtiene resultados de su búsqueda, puede alejarse al evento o grupo más cercano.
El plan de movilización era presionar a activistas y organizadores para que planificaran eventos de junio a agosto. Luego reúna a tantas personas para confirmar su asistencia a los eventos recién creados desde agosto hasta los grandes días: 20 de septiembre y 27 de septiembre. En agosto, implementamos la funcionalidad de código de inserción que los organizadores utilizaron bien, incorporando mapas específicos locales y regionales en 350 páginas grupales locales y sitios web específicos sobre el impacto climático que habían construido.
El mapa era tan popular que otras organizaciones preguntaron si podían insertarlo en sus propios sitios, aumentando los puntos de movilización y el público alcanzado. El hecho de que pudiéramos hacer esto habla de la importancia de defender la web abierta y el software libre y de código abierto que permite compartir y utilizar herramientas de forma descentralizada.
El primer día de las huelgas, los estilos de alfileres cobraron vida, iluminando las numerosas huelgas, manifestaciones y protestas que ocurrieron ese día. Fue un gráfico para periodistas y simpatizantes en las redes sociales para compartir cuando informaban sobre una participación sin precedentes.
En última instancia, los números que vimos fueron un testimonio de los largos y arduos esfuerzos que los organizadores trabajan constantemente y la urgencia del momento en que nos encontramos. Sin embargo, con herramientas como el Mapa de Acción de Justicia Climática, creado por activistas tecnológicos junto con los organizadores que los utilizan, profundizamos y ampliamos la movilización posible. Y en estos tiempos de desigualdad masiva de la riqueza, profunda corrupción política y cierre de la ventana de tiempo para la acción audaz que necesitamos, interrumpir el statu quo es más importante que nunca.
Teachers with GUTS (TWiG) needed an online community space to support teachers in integrating the modern scientific practices of computer modeling and simulation into their school day science classes.
We do recommend moving directly to Drupal 9 (which was released on June 3rd of 2020), however:
Moving to Drupal 8 or to Drupal 9 is much the same. Drupal 8 starts what i call the "modern Drupal" era. Whereas for going from Drupal 5 to 6 or 6 to 7 or 7 to 8 broke backward compatibility and might as well be a full rebuild (so we would often recommend hopping a version, say, stay on Drupal 6 and wait for Drupal 8 to be ready) going from Drupal 8 to 9 is closer to going from Drupal 8.8 to 8.9— an in-place upgrade from 8.9 to 9.0. Going from 9 to 10 will work the same, and that's the plan and promise for Drupal 8 on out.
All that said, if anything significant needs fixing on your current Drupal 7 site, or you are looking to make any improvements, you'll want to do that on Drupal 8+ or Drupal 8/9 as we phrased it back when Drupal 9 was still a pretty recent release, but now we can just say Drupal 9— or, as i call it to emphasize the decreased importance of major version numbers, modern Drupal.
Agaric is always happy to discuss more! Mostly what i'm saying here is the useful things to talk about are the specific goals for the sites—when you want to accomplish what—because the official support cycles are a distraction in the current context of Drupal. So make sure your current site is maintained, but take your time to get clear on your objectives, and contact Agaric or the Drupal professionals of your choice when you think it might make sense to upgrade your site into the era of modern Drupal.
Taxonomy terms, as very simple content usually directly associated with nodes, are amenable to two main approaches of migrating.
Migrate first in a true migration, similar to the entity_references example from the training, or create the taxonomy terms during the node migration.
See Migrate Plus for examples, specifically the submodule migrate_example_advanced.
This approach means you can't clean them up easily with a migrate rollback but it can be suitable when you are confident of not needing to roll back or being able to delete all terms in the vocabulary if you need to do the migration fresh.
This was shown in the training in the professors example. There is also community documentation of the entity generate plugin that we make use of there; it is provided by Migrate Plus.
Find It Features and Functionality: Broadening Educational Opportunities for Youth
For clients whose sites we built and clients whose sites we inherited, Agaric frequently provides these services through one simple monthly retainer:
We roll over hours month-to-month so we can put the work in when you need it.
Let us know how we can help you!
Join us on Agaric's Meet.coop BigBlueButton videochat & screenshare for presentation and discussion, February 2, Sunday, at 15:00 UTC (10am Eastern).
https://meet.agaric.coop/rooms/a8m-x61-skh-ift/join
How can a group of thousands of people talk about and decide anything? How's this 'community' concept supposed to work at scale, even in theory?
Any Free/Libre Open Source Software project will have elements of do-ocracy (rule of those who do the work), but not all decisions should devolve to implementors. A better ideal is that decisions should be made by the people who are most affected.
Particularly when a decision strongly impacts more than those who carry it out, we need better ways of making decisions that give everyone their say. This starts by letting people by heard by everyone else. Fortunately, we can scale conversations and decisions in a fair and truly democratic way.
Today we continue the conversation about migration dependencies with a hierarchical taxonomy terms example. Along the way, we will present the process and syntax for migrating into multivalue fields. The example consists of two separate migrations. One to import taxonomy terms accounting for term hierarchy. And another to import into a multivalue taxonomy term field. Following this approach, any node and taxonomy term created by the migration process will be removed from the system upon rollback.

You can get the full code example at https://github.com/dinarcon/ud_migrations The module to enable is UD multivalue taxonomy terms whose machine name is ud_migrations_multivalue_terms. The two migrations to execute are udm_dependencies_multivalue_term and udm_dependencies_multivalue_node. Notice that both migrations belong to the same module. Refer to this article to learn where the module should be placed.
The example assumes Drupal was installed using the standard installation profile. Particularly, a Tags (tags) taxonomy vocabulary, an Article (article) content type, and a Tags (field_tags) field that accepts multiple values. The words in parenthesis represent the machine name of each element.
The example data for the taxonomy terms migration is fruits and fruit varieties. Each row will contain the name and description of the fruit. Additionally, it is possible to define a parent term to establish hierarchy. For example, “Red grape” is a child of “Grape”. Note that no numerical identifier is provided. Instead, the value of the name is used as a string identifier for the migration. If term names could change over time, it is recommended to have another column that did not change (e.g., an autoincrementing number). The following snippet shows how the source section is configured:
source:
plugin: embedded_data
data_rows:
- fruit_name: 'Grape'
fruit_description: 'Eat fresh or prepare some jelly.'
- fruit_name: 'Red grape'
fruit_description: 'Sweet grape'
fruit_parent: 'Grape'
- fruit_name: 'Pear'
fruit_description: 'Eat fresh or prepare a jam.'
ids:
fruit_name:
type: stringThe destination is quite short. The target entity is set to taxonomy terms. Additionally, you indicate which vocabulary to migrate into. If you have terms that would be stored in different vocabularies, you can use the vid property in the process section to assign the target vocabulary. If you write to a single one, the default_bundle key in the destination can be used instead. The following snippet shows how the destination section is configured:
destination:
plugin: 'entity:taxonomy_term'
default_bundle: tagsFor the process section, three entity properties are set: name, description, and parent. The first two are strings copied directly from the source. In the case of parent, it is an entity reference to another taxonomy term. It stores the taxonomy term id (tid) of the parent term. To assign its value, the migration_lookup plugin is configured similar to the previous example. The difference is that, in this case, the migration to reference is the same one being defined. This sets an important consideration. Parent terms should be migrated before their children. This way, they can be found by the lookup operation. Also note that the lookup value is the term name itself, because that is what this migration set as the unique identifier in the source section. The following snippet shows how the process section is configured:
process:
name: fruit_name
description: fruit_description
parent:
plugin: migration_lookup
migration: udm_dependencies_multivalue_term
source: fruit_parentTechnical note: The taxonomy term entity contains other properties you can write to. For a list of available options check the baseFieldDefinitions() method of the Term class defining the entity. Note that more properties can be available up in the class hierarchy.
The next step is to create a node migration that can write to a multivalue taxonomy term field. To stay on point, only one more field will be set: the title, which is required by the node entity. Read this change record for more information on how the Migrate API processes Entity API validation. The following snippet shows how the source section is configured for the node migration:
source:
plugin: embedded_data
data_rows:
- unique_id: 1
thoughtful_title: 'Amazing recipe'
fruit_list: 'Green apple, Banana, Pear'
- unique_id: 2
thoughtful_title: 'Fruit-less recipe'
ids:
unique_id:
type: integerThe fruits column contains a comma separated list of taxonomies to apply. Note that the values match the unique identifiers of the taxonomy term migration. If you had used numbers as migration identifiers there, you would have to use those numbers in this migration to refer to the terms. An example of that was presented in the previous post. Also note that there is one record that has no terms associated. This will be considered during the field mapping. The following snippet shows how the process section is configured for the node migration:
process:
title: thoughtful_title
field_tags:
- plugin: skip_on_empty
source: fruit_list
method: process
message: 'Row does not contain fruit_list.'
- plugin: explode
delimiter: ','
- plugin: callback
callable: trim
- plugin: migration_lookup
migration: udm_dependencies_multivalue_term
no_stub: trueThe title of the node is a verbatim copy of the thoughtful_title column. The Tags fields, mapped using its machine name field_tags, uses three chained process plugins. The skip_on_empty plugin reads the value of the fruit_list column and skips the processing of this field if no value is provided. This is done to accommodate the fact that some records in the source do not specify tags. Note that the method configuration key is set to process. This indicates that only this field should be skipped and not the entire record. Ultimately, tags are optional in this context and nodes should still be imported even if no tag is associated.
The explode plugin allows you to break a string into an array, using a delimiter to determine where to make the cut. Later, the callback plugin will use the trim PHP function to remove any space from the start or end of the exploded taxonomy term name. Finally, this array is passed to the plugin specifying the term migration as the one to use for the lookup operation. Again, the taxonomy term names are used here because they are the unique identifiers of the term migration. The `no_stub` configuration should be set to `true` to prevent terms to be created if they are not found by the plugin. This would not occur in the example because we make sure a match is found. If we did not set this configuration and we do not include the trim step, some new terms would be created with spaces at the beginning. Note that neither of these plugins has a migration_lookupsource configuration. This is because when process plugins are chained, the result of one plugin is sent as source to be transformed by the next one in line. The end result is an array of taxonomy term ids that will be assigned to field_tags. The migration_lookup is able to process single values and arrays.
The last part of the migration is specifying the process section and any dependencies. Refer to this article for more details on setting migration dependencies. The following snippet shows how both are configured for the node migration:
destination:
plugin: 'entity:node'
default_bundle: article
migration_dependencies:
required:
- udm_dependencies_multivalue_term
optional: []One way to set multivalue fields in Drupal migrations is assigning its value to an array. Another option is to set each value manually using field deltas. Deltas are integer numbers starting with zero (0) and incrementing by one (1) for each element of a multivalue field. Although you could set any delta in the Migrate API, consider the field definition in Drupal. It is possible that limits had been set to the number of values a field could hold. You can specify deltas and subfields at the same time. The full syntax is field_name/field_delta/subfield. The following example shows the syntax for a multivalue image field:
process:
field_photos/0/target_id: source_fid_first
field_photos/0/alt: source_alt_first
field_photos/1/target_id: source_fid_second
field_photos/1/alt: source_alt_second
field_photos/2/target_id: source_fid_third
field_photos/2/alt: source_alt_thirdManually setting a multivalue field is less flexible and error-prone. In today’s example, we showed how to accommodate for the list of terms not being provided. Imagine having to that for each delta and subfield combination, but the functionality is there in case you need it. In the end, Drupal offers more syntactic sugar so you can write shorted field mappings. Additionally, there are various process plugins that can handle arrays for setting multivalue fields.
Note: There are other ways to migrate multivalue fields. For example, when using the entity_generate plugin provided by Migrate Plus, there is no need to create a separate taxonomy term migration. This plugin is able to create the terms on the fly while running the import process. The caveat is that terms created this way are not deleted upon rollback.
What did you learn in today’s blog post? Have you ever done a taxonomy term migration before? Were you aware of how to migrate hierarchical entities? Did you know you can manually import multivalue fields using deltas? Please share your answers in the comments. Also, I would be grateful if you shared this blog post with others.
Next: Migrating users into Drupal - Part 1
This blog post series, cross-posted at UnderstandDrupal.com as well as here on Agaric.coop, is made possible thanks to these generous sponsors. Contact Understand Drupal if your organization would like to support this documentation project, whether it is the migration series or other topics.
We're proud to have worked with designer Todd Linkner to produce a bold and unique web site worthy of the world-renowned architectural firm Studio Daniel Libeskind.
Master planner for the Ground Zero memorial and architect of numerous acclaimed museums, offices, and residences around the world, Daniel Libeskind needed to present his and his studio's amazing work with commensurate impact online.
The Studio Daniel Libeskind project was one of our most ambitious to date. The architect partners and their one-woman public relations powerhouse, Amanda Ice, are fantastic to work with, as is the project's driving force and designer, Todd Linkner. We worked through many challenges, remaining flexible to the business needs and the design needs (developed in parallel with the work on base functionality)— and missed a September 11, 2011 launch date despite putting all hands on deck for as the scope outpaced the resources available. We continued, and completed the site successfully for beautiful presentation across browsers, iPad, and smart phones.
Agaric architected, built and themed the redesigned site. In addition to bringing the bold design to life and further making the site work for mobile devices (iOS, Android, and even BlackBerry), Agaric vastly improved the content creation workflow and press inquiry handling capabilities of the site, as well as search and filtering. We added generating stylish PDF versions of project pages, custom cropping and ordering of images and kept hardware requirements low and user perceived performance high by adding Varnish HTTP caching to the server.
Claudina Sarahe and Benjamin Melançon presented on the challenges and successes of this project at Pacific Northwest Drupal Summit.
We're thrilled to have had the opportunity to be such a large part in giving Studio Daniel Libeskind an online home worthy of the inspiring places they create in the physical world.