Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update structure to be much more conformant to the validator #11

Open
lukehesluke opened this issue Apr 2, 2020 · 12 comments
Open

Update structure to be much more conformant to the validator #11

lukehesluke opened this issue Apr 2, 2020 · 12 comments

Comments

@lukehesluke
Copy link

Hi @jopotts ,

Thanks for our call earlier. Further to that, here's some notes about how you might update your feed to be sufficiently conformant so that the feeds can be ingested by our "universal" adapter which should hopefully be quick wins.

I've included an example for a SessionSeries with a Schedule at the end, which might be easier to digest than a big list of changes

Changes

For event_type in general:

  • kind: SessionSeries
  • data.type: SessionSeries
  • Change data['@context'] to equal:
    ["https://openactive.io/", "https://openactive.io/ns-beta"]
  • Rename data.offer -> data.offers
  • Add "type": "GeoCoordinates" to data.location.geo
  • Create data.id using data.identifier and the feed's URL.
    e.g. if the SessionSeries feed is hosted at https://bookwhen.com/api/openactive/session-series, and a SessionSeries' identifier is pysafz84ka8v, the id should be:
    https://bookwhen.com/api/openactive/session-series/pysafz84ka8vp
    

For an event_type with a recurrence rule

  • Move .eventSchedule out of subEvent and into the top level of the SessionSeries. (this might be easier to see if you look at the example at the bottom of this description)
  • Turn eventSchedule into an array with one item
  • Move duration out of subEvent and into the Schedule item.
  • Add fields to the Schedule item:
    • "scheduledEventType": "ScheduledSession"
    • "scheduleTimezone": "Europe/London"
  • In the Schedule item, use repeatFrequency instead of frequency and interval. e.g.
    frequency: weekly & interval: 1 => "repeatFrequency": "P1W"
  • Move beta:urlTemplate into the Schedule item.
    Validator will not recognise this field but, for now, we'll accept it. There is a urlTemplate field for Schedules (https://www.openactive.io/modelling-opportunity-data/#publishing-event-schedules)
    but it has a different format so I imagine this would be a big change
  • Now that these fields have been moved out of subEvent, subEvent can just be deleted. It is not needed for a SessionSeries with a Schedule

For an event_type with just one occurrence:

  • rename "type": "Event" to "type": "ScheduledSession"

For an event:

  • Change data[‘@context’] to equal:
    ["https://openactive.io/", "https://openactive.io/ns-beta"]
  • rename "type": "Event" to "type": "ScheduledSession"
  • Update superEvent, so that it is the URL ID of the SessionSeries that is this ScheduledSession's parent. Use the method described above for a SessionSeries' .data.id to construct this ID.
    e.g. where, before, the superEvent was:
    "superEvent": {
      "identifier": "eventType-nx610pp9r6p7"
    }
    
    Change it to:
    "superEvent": "https://bookwhen.com/api/openactive/session-series/nx610pp9r6p7"
    

Example

This is a SessionSeries with a Schedule. This is how it looks presently:

{
  "@context": [
    "https://www.openactive.io/ns/oa.jsonld",
    "https://www.openactive.io/ns-beta/oa.jsonld"
  ],
  "type": "Event",
  "identifier": "eventType-g6puavnqax9i",
  "name": "Street / 3 - 6 years - TRIAL CLASS ",
  "description": "Funky street dance class for children with lots of attitude and style. Perfect for children of all abilities",
  "category": [],
  "url": "https://bookwhen.com/ddc-kiveton?r=oa",
  "activity": [],
  "offer": [],
  "maximumAttendeeCapacity": 8,
  "eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
  "beta:formattedDescription": "<p>Funky street dance class for children with lots of attitude and style. Perfect for children of all abilities</p>\n",
  "organizer": {
    "type": "Organization",
    "identifier": "organization-1fihlz2a8ot5",
    "name": "Studio 44",
    "email": "[email protected]",
    "telephone": "01246641000",
    "beta:website": "https://www.studio-44.co.uk"
  },
  "location": {
    "type": "Place",
    "identifier": "location-d94pg2jx4unp",
    "description": "",
    "address": "St John&#39;s Room 107 Wales Road\nKiveton Park\nSheffield\nS26 6RA",
    "geo": {
      "latitude": 53.340761,
      "longitude": -1.2731072
    }
  },
  "subEvent": [
    {
      "type": "Event",
      "duration": "PT45M",
      "beta:urlTemplate": "https://bookwhen.com/ddc-kiveton/e/ev-sdfo-{YYYYMMDDHHMISS}?r=oa",
      "eventSchedule": {
        "type": "Schedule",
        "startDate": "2019-02-11",
        "frequency": "weekly",
        "byDay": [
          "https://schema.org/Monday"
        ],
        "startTime": "16:45",
        "endTime": "17:30",
        "exceptDate": [
          "2019-05-06T15:45:00Z",
          "2019-05-27T15:45:00Z"
        ],
        "interval": 1
      }
    }
  ]
}

This is how it would look with the changes detailed above:

{
  "@context": [
    "https://openactive.io/",
    "https://openactive.io/ns-beta"
  ],
  "type": "SessionSeries",
  "id": "https://bookwhen.com/api/openactive/session-series/pysafz84ka8vp",
  "identifier": "g6puavnqax9i",
  "name": "Street / 3 - 6 years - TRIAL CLASS ",
  "description": "Funky street dance class for children with lots of attitude and style. Perfect for children of all abilities",
  "category": [],
  "url": "https://bookwhen.com/ddc-kiveton?r=oa",
  "activity": [],
  "offers": [],
  "maximumAttendeeCapacity": 8,
  "eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
  "beta:formattedDescription": "<p>Funky street dance class for children with lots of attitude and style. Perfect for children of all abilities</p>\n",
  "organizer": {
    "type": "Organization",
    "identifier": "organization-1fihlz2a8ot5",
    "name": "Studio 44",
    "email": "[email protected]",
    "telephone": "01246641000",
    "beta:website": "https://www.studio-44.co.uk"
  },
  "location": {
    "type": "Place",
    "identifier": "location-d94pg2jx4unp",
    "description": "",
    "address": "St John&#39;s Room 107 Wales Road\nKiveton Park\nSheffield\nS26 6RA",
    "geo": {
      "type": "GeoCoordinates",
      "latitude": 53.340761,
      "longitude": -1.2731072
    }
  },
  "eventSchedule": [
    {
      "type": "Schedule",
      "scheduledEventType": "ScheduledSession",
      "scheduleTimezone": "Europe/London",
      "beta:urlTemplate": "https://bookwhen.com/ddc-kiveton/e/ev-sdfo-{YYYYMMDDHHMISS}?r=oa",
      "startDate": "2019-02-11",
      "repeatFrequency": "P1W",
      "byDay": [
        "https://schema.org/Monday"
      ],
      "startTime": "16:45",
      "endTime": "17:30",
      "duration": "PT45M",
      "exceptDate": [
        "2019-05-06T15:45:00Z",
        "2019-05-27T15:45:00Z"
      ]
    }
  ]
}
@nickevansuk
Copy link
Contributor

nickevansuk commented Apr 2, 2020

@lukehesluke looks great! Just a couple of additions...

There is a new virtual events requirement for organizer @id (https://developer.openactive.io/publishing-data/virtual-events#conformance-criteria), based on what Sport England require for #StayInWorkOut - hopefully simple to add.

So the above would look something like this (using the preferred @type and @id for better compatibility with JSON-LD, though type and id are still valid):

  "organizer": {
    "@type": "Organization",
    "@id": "https://id.bookwhen.com/organizer/1fihlz2a8ot5",
    "identifier": "organization-1fihlz2a8ot5",
    "name": "Studio 44",
    "email": "[email protected]",
    "telephone": "01246641000",
    "url": "https://www.studio-44.co.uk"
  },

Additionally, to pass validation properties with values that are empty arrays and empty strings must be omitted.

@domfennell
Copy link

domfennell commented Apr 21, 2020

Hi @jopotts,

We thought it might be helpful to split @lukehesluke's suggestions into 3 categories ("Simple re-names / moving fields around", "Logic changes" and "Nice-to-haves") to make it easier for you. Hopefully this will save you time and allow you to focus on those quick-win changes ("simple re-names / moving fields around" - maybe <30 mins total time?) that will bring your feed more inline with the Spec. and allow your virtual classes to be advertised across user-facing platforms.

Simple re-names / moving fields around

(we estimate <30 mins total time for all of these)

For event_type in general:

  • kind: SessionSeries
  • data.type: SessionSeries
  • Change data['@context'] to equal:
["https://openactive.io/", "https://openactive.io/ns-beta"]
  • Rename data.offer -> data.offers
  • Add "type": "GeoCoordinates" to data.location.geo

For event_type with a recurrence rule:

  • Move .eventSchedule out of subEvent and into the top level of the SessionSeries. (this might be easier to see if you look at the example at the bottom of this description)
  • Turn eventSchedule into an array with one item
  • Move duration out of subEvent and into the Schedule item.
  • Add fields to the Schedule item:
    --"scheduledEventType": "ScheduledSession"
    --"scheduleTimezone": "Europe/London"
  • Move beta:urlTemplate into the Schedule item. Validator will not recognise this field but, for now, we'll accept it. There is a urlTemplate field for Schedules (https://www.openactive.io/modelling-opportunity-data/#publishing-event-schedules), but it has a different format so I imagine this would be a big change
  • Now that these fields have been moved out of subEvent, subEvent can just be deleted. It is not needed for a SessionSeries with a Schedule

For an event_type with just one occurrence:

  • rename "type": "Event" to "type": "ScheduledSession"

For an event:

["https://openactive.io/", "https://openactive.io/ns-beta"]
  • Rename "type": "Event" to "type": "ScheduledSession"
  • Update superEvent, so that it is the URL ID of the SessionSeries that is this ScheduledSession's parent. Use the method described above for a SessionSeries' .data.id to construct this ID, e.g. where, before, the superEvent was:
"superEvent": {
  "identifier": "eventType-nx610pp9r6p7"
}

... change it to:

"superEvent": "https://bookwhen.com/api/openactive/session-series/nx610pp9r6p7"

Logic changes

(we estimate 30-35 mins total time for both of these - the first one could take less than 5 minutes!)

  • Create data.id using data.identifier and the feed's URL -> very quick (maybe 5 mins?).
    For example, if the SessionSeries feed is hosted at https://bookwhen.com/api/openactive/session-series and a SessionSeries' identifier is pysafz84ka8v, the id should be:
https://bookwhen.com/api/openactive/session-series/pysafz84ka8vp
  • In the Schedule item, use repeatFrequency instead of frequency and interval -> perhaps <30 mins.
    For example, frequency: weekly & interval: 1 => "repeatFrequency": "P1W"

Nice-to-haves

@nickevansuk
Copy link
Contributor

nickevansuk commented May 12, 2020

@gregjoy1 as discussed issues below:

1 Split into three feeds

So there should be three feeds here: a SessionSeries and ScheduledSession feed pair for recurring events (documentation), and an Event feed for one-off events (documentation).

  • /sessionseries - only entries that have a recurrence rule and include a eventSchedule. @type and kind in this feed are both SessionSeries.
  • /scheduledsessions - is event occurrences which have a booking on them (the current contents of the /events feed). @type and kind in this feed are both ScheduledSession.
  • /events - entries that have a single occurrence (currently these are included in /sessionseries with a just a subEvent). @type and kind in this feed are both Event. The properties of the single subEvent can be moved into the parent Event itself, and no subEvent is required.

2 activity is missing

https://bookwhen.com/api/openactive/session_series?afterId=5wkobfev8rwr&afterTimestamp=1573822146

Has activity been accidentally missed here?

Example below:

"activity": [
  {
    "@type": "Concept",
    "@id": "https://openactive.io/activity-list#5e78bcbe-36db-425a-9064-bf96d09cc351",
    "prefLabel": "Bodypump™",
    "inScheme": "https://openactive.io/activity-list"
  }
]

4 price must be a decimal

https://bookwhen.com/api/openactive/session_series?afterId=5wkobfev8rwr&afterTimestamp=1573822146

"price": "5.00"

should be

"price": 5.00

5 Use beta:virtualLocation for online events

https://bookwhen.com/api/openactive/session_series?afterId=5wkobfev8rwr&afterTimestamp=1573822146

location is not permitted for online-only events

  "location": {
    "type": "Place",
    "identifier": "izdxleom4a4b",
    "description": "Example description",
    "name": "Online"
  },

should be replaced by

   "beta:virtualLocation": {
     "@type": "VirtualLocation",
     "identifier": "izdxleom4a4b",
     "description": "Example description",
     "name": "Online"
   }

6 eventAttendanceMode only required in SessionSeries

eventAttendanceMode is not required in ScheduledSession, unless it has a different value from the related parent SessionSeries.

7 Use @type and @id consistently

  • Every property named type should be renamed to @type
  • Every property named id within the data property should be renamed to @id (note the id that sits at the same level as kind must stay as id)

8 Remove empty strings and empty arrays

Properties should not be included where their value is an empty string and or empty array, or an empty object that contains only an @type (e.g. below):

"ageRange": {
"@type": "QuantitativeValue"
},

The OpenActive Ruby library will do this for you.

@nickevansuk
Copy link
Contributor

nickevansuk commented Jun 18, 2020

@gregjoy1 just reviewed the latest feeds, great work so far! Fresh feedback below:

1 Use Schedule without beta properties

Include the idTemplate and urlTemplate as specified in https://developer.openactive.io/publishing-data/schedules. Note for your usecase we recommend creating a simple 301 redirect that takes users from e.g. https://bookwhen.com/openactive/events/s51e/2018-04-25T18-00-00Z (or whatever urlTemplate you use) to e.g. https://bookwhen.com/hulafit/e/ev-s51e-20180425180000
This means you can stop using the long-since deprecated beta:urlTemplate.

2 Event capacity

remainingAttendeeCapacity and beta:attendanceCount appear to be missing from https://bookwhen.com/api/openactive/events

3 price must be a decimal

https://bookwhen.com/api/openactive/session_series?afterId=5wkobfev8rwr&afterTimestamp=1573822146

"price": "5.00"

should be

"price": 5.00

4 url should be used instead of beta:website

"organizer": {
  "@type": "Organization",
  "@id": "https://bookwhen.com/api/openactive/organizers/uwdefqoiez8h",
  "beta:website": "http:/www.kasostudios.co.uk",
  ...
},

Should be

"organizer": {
  "@type": "Organization",
  "@id": "https://bookwhen.com/api/openactive/organizers/uwdefqoiez8h",
  "url": "http:/www.kasostudios.co.uk",
  ...
},

5 eventAttendanceMode only required in SessionSeries

eventAttendanceMode is not required in ScheduledSession, unless it has a different value from the related parent SessionSeries.

6 Add a courses feed

I've just noticed the property "beta:course": true, and realised that Bookwhen also supports courses! Courses should in fact be a separate feed at /course-instances using the CourseInstance type, as they are different to regularly scheduled sessions.

See here for an example: https://validator.openactive.io/?url=https%3A%2F%2Fopenactive.io%2Fdata-models%2Fversions%2F2.x%2Fexamples%2Fcourseinstance_event_example_1.json&version=2.x

Note that you don't need to include beta:course property referenced in the example, just put everything that was in the SessionSeries into the CourseInstance.

Also you'll need to add the course-related subEvents to that feed instead of putting them in the ScheduledSession feed (as per example)

7 byDay is an array

"byDay": "1SU", should be "byDay": [ "1SU" ],

@gregjoy1
Copy link
Contributor

Hi @nickevansuk, @domfennell and @lukehesluke, thank you all for helpfully and clearly setting out these requirements, sorry for the delay in responding to them. We have recently released the requested updates :)

The only thing not included is 3, this is because the JSON serialiser we use at Bookwhen always casts decimals as strings - https://apidock.com/rails/BigDecimal/as_json

It appears the OA Gem also has the same constraint as it also returns strings for decimals - https://github.com/openactive/models-ruby

Is it possible to quietly still consume it considering its known to be a decimal? :)

Also are these changes enough for us to be consumed in your universal feed reader yet? :)

@nickevansuk
Copy link
Contributor

Thanks @gregjoy1! Will take a proper look after the dataset site is updated too.

However one quick thing, free activities (where "isAccessibleForFree": true) for events, courses, and sessions should also include a zero priced Offer (priceCurrency is not required), e.g.:

        "offers": [
          {
            "@type": "Offer",
            "price": 0
          }
        ],

@gregjoy1
Copy link
Contributor

Hi @nickevansuk, @domfennell and @lukehesluke, the updated the dataset site can be found here - https://data.bookwhen.com/ :)

@nickevansuk
Copy link
Contributor

Great work @gregjoy1 - one small note: you seem to be using a background image hot-linked to another dataset site (https://data.better.org.uk/images/bg.jpg).

Perhaps replace it with something hosted on your own CDN? There's a great selection or relevant free images here: https://unsplash.com/photos/gJtDg6WfMlQ

@nickevansuk
Copy link
Contributor

nickevansuk commented Jul 29, 2020

Also please feel free to delete this old repo: https://github.com/bookwhen/openactive-datasite, so that it's easy to see from the forks that you've migrated successfully.

@nickevansuk
Copy link
Contributor

nickevansuk commented Jul 30, 2020

Hey @gregjoy1, so this round of feedback is by way of extracts in the validator.

Please address the errors shown in the validator for each of the following:

Note: It's fine to keep those items in that are missing activity, though perhaps stronger signposting to let users know they need to fill in this field could be useful?

Will have a think about about the issue with price... TeamUp have also had the same issue. See proposal here

@gregjoy1
Copy link
Contributor

Thanks @nickevansuk, il get this into an upcoming sprint :)

@thill-odi
Copy link

thill-odi commented Oct 5, 2020

I recommend closing this issue, as all issues listed above have been checked and are resolved (except for price, which needs to be addressed in the standard and the validator).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants