Problem/Motivation

I recently had to import availability data from an external source with Migrate. I propose to integrate this capability in the Availability Calendars module.

Proposed resolution

Integration with Migrate for contrib modules used to be in a separate module called Migrate Extras, but this has been deprecated:

The best place to implement migration support for a contributed module is in that module, not in the Migrate or Migrate Extras modules. That way, the migration support is always self-consistent with the current module implementation.

I propose to embed a field handler directly in the Availability Calendars module.

Remaining tasks

Review the patch proposed in #2.

User interface changes

None.

API changes

Developers will now be able to migrate availability data to a field of type "availability_calendar", provided they use the correct format, as described in README.txt.

Data model changes

None.

Comments

FMB created an issue. See original summary.

fmb’s picture

fmb’s picture

Issue summary: View changes
fmb’s picture

Issue summary: View changes
fietserwin’s picture

Status: Active » Needs work

Thanks for posting this patch. I had a quick look at it and found the points below. Reading your blog made some things clear, but let's ensure the code is readable stand alone, so adding some additional comments would be nice.

Regarding the hybrid approach:
It started as a field, and a complex one. So complex that I had to store the "real" data outside the field storage. Only later I discovered that it would be handier to (also) define it as an entity as that would facilitate integration with Views and Search API. So I defined it as an entity, but only on the reading side, thus no C,U, and D out of crud, via the entity API as that served my purposes. If I am going to create a D8 version it will be an entity only and the field should be replaced by an entity reference field.

Regarding the code you are reusing:
This code was so specific for the communication with the edit form that I could not envisage any use elsewhere and as such did not nbother to abstract it into a decent API method. However, I can do so now by moving the code you are using out of the widget code into the .inc where most of the API functions are living. In doing so, I can add parameters for the period to accept or even push that to a separate validator.
On the other hand, you can easily create your own array to pass directly to availability_calendar_update_multiple_availability() by adding 1 line per day, using a lookup to convert your source state to the destination state and a DateTime that you advance with a day on each character. In doing so, there's no need for the very strict validation, the need to convert to a string and back, nor for the date restrictions. Moreover, just passing NULL as $cid to availability_calendar_update_multiple_availability() ensures that a new calendar will be created and its cid returned, so exit $cid_unique and $new_cid_count. You can even "optimize" that array later:

$current_entry = 0;
while ($current_entry < count($availability_array) - 1) {
  if ($availability_array[$current_entry][state] === $availability_array[$current_entry + 1][state]) {
    $availability_array[$current_entry][enddate] = $availability_array[$current_entry + 1][enddate];
    unset($availability_array[$current_entry + 1]);
  }
  else {
    $current_entry++;
  }
}
  1. +++ b/availability_calendar_migrate_field_handlers.inc
    @@ -0,0 +1,94 @@
    +   * Register availability_calendar type.
    +   */
    

    Due to the hybrid approach of A.C. I'm not sure whether this registers the entity or field :)

  2. +++ b/availability_calendar_migrate_field_handlers.inc
    @@ -0,0 +1,94 @@
    +   * Force creation of an entity embedding availability.
    +   *
    

    not sure about this sentence, perhaps something like:
    Creates and populates an availability entity/field.

  3. +++ b/availability_calendar_migrate_field_handlers.inc
    @@ -0,0 +1,94 @@
    +  protected function prepareCalendar($dates, $element, $entity) {
    +    $changes = array();
    

    Add @param and @return

  4. +++ b/availability_calendar_migrate_field_handlers.inc
    @@ -0,0 +1,94 @@
    +      if (!empty($line)) {
    +        $change = availability_calendar_field_widget_month_form_validate_line($line, $element);
    

    trim($line) before checking for empty

  5. +++ b/availability_calendar_migrate_field_handlers.inc
    @@ -0,0 +1,94 @@
    +        if ($change == FALSE) {
    +          watchdog('availability_calendar', "Invalid availability data: %line", array('%line' => $line), WATCHDOG_ERROR);
    

    use ===.

fmb’s picture

Status: Needs work » Needs review
StatusFileSize
new4.07 KB
new6.44 KB

Thanks fietserwin for your review and explanations! I considered the use of availability_calendar_field_widget_month_form_validate() vs directly calling availability_calendar_update_multiple_availability() and I think we should still call the validation function because:
1) We need a common input format: the one you're using for that is perfectly fine in my opinion, and it comes really handy that availability_calendar_field_widget_month_form_validate() is able to convert it into the "internal" format needed in availability_calendar_update_multiple_availability().
2) It is nice for the developer of a migration class to know his data can't be imported properly because they're not formatted as expected and thus don't pass the validation.

Now, we could, as you say, decouple this validation function from the widget, and maybe add a parameter to allow for less restrictive validation (allow to import past availability dates, for instance, even though I'm not entirely sure this might actually be of any use). As you can see, I don't have much contrib time, but if you open an issue for that, I'd be glad to work on a patch.

As for $cid_unique and such, I have to confess I'm not sure I understood entirely this mechanism, maybe some of the code I wrote is superfluous...

fietserwin’s picture

StatusFileSize
new7.94 KB
new5.32 KB

I was more thinking about going the direct way (using availability_calendar_update_multiple_availability() directly), but think that this solution that accepts both ways is better.

I Also made some other (small) changes. Can you review (and test?)? Will this work in a real migrate situation?

fmb’s picture

I integrated your changes, and not only tested them, but wrote a test! I hope you will be OK with this approach. That works with the included test data, but I think I am going to work a little bit on availability_calendar_field_widget_month_form_validate_line(), so it is able to accept intervals that begin in the past.

fmb’s picture

StatusFileSize
new24.12 KB
new3.53 KB

I made a small change in this function as announced.

fmb’s picture

fietserwin’s picture

Nice, however I can't get the test to work, it fails on the last assertion.

- hook_migrate_api in availability_calendar.migrate.inc gets called
- I see the XML mapper at work
- But the resulting string does not get "prepared". It gets set as the field value and after that the node gets saved with that incorrect field value ("2,2016-04-01,2016-05-31\n ...")

Where should AvailabilityCalendarFieldHandler enter the stage (getting constructed) and when should its prepare() method gets called?

I'm running the test via the UI on my local install.

  • fietserwin committed 8216334 on 7.x-5.x
    [#2658754] by FMB: Allow to import availability data with Migrate.
    
fietserwin’s picture

Status: Needs review » Fixed

I committed the code, including the tests, even if I can't get them to run locally. Can you have another look? Maybe my latest changes contain a bug after all.

Do you want to be listed as a sponsor/contributor to this module on the project page? If so, under what name and link?

fmb’s picture

Issue summary: View changes
StatusFileSize
new47.6 KB

I was a bit busy, sorry for not answering lately ^^;

Nice, however I can't get the test to work, it fails on the last assertion.

That's strange, I ran it using the Simpletest UI in a new Drupal installation and the latest Git version of Availability Calendars, and it worked (see attached screenshot)... Does Simpletest display some kind of message?

Thanks a lot for committing the patch! It would indeed be really kind of you to list me in the sponsor/contributor section on the project page under "Felip Manyer i Ballester" and link to my user page.

fmb’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.