<commit_before>define [
  'jquery'
  'compiled/calendar/commonEventFactory'
  'jquery.ajaxJSON'
  'vendor/jquery.ba-tinypubsub'
], ($, commonEventFactory) ->

  class
    constructor: (contexts) ->
      @contexts = contexts
      @clearCache()
      @inFlightRequest = false
      @pendingRequests = []

      # The cache will store all the events we've fetched so far, and looks like this:
      # {
      #   contexts: {
      #     "user_1": {
      #       fetchedRanges: [
      #         sorted list of [start, end] tuples that represent
      #         ranges of dates that we have already fetched for
      #       ]
      #       events: {
      #         "assignment_1": <CommonEvent object>
      #       },
      #       fetchedUndated: true/false
      #     }, ...
      #   },
      #   appointmentGroups: {
      #     "1": <object>
      #   },
      #   participants: {
      #     "1_unregistered": [ users or groups ]
      #   }
      #   fetchedAppointmentGroups: { manageable: true/false }
      # }
      #
      # Note that the appointmentGroups are not cached per context, as
      # we get them all in the same request (not scoped to contexts at
      # all.) This might end up being confusing.
      
      $.subscribe "CommonEvent/eventDeleted", @eventDeleted
      $.subscribe "CommonEvent/eventSaved", @eventSaved

    eventSaved: (event) =>
      @addEventToCache(event)

    eventDeleted: (event) =>
      events = @cache.contexts[event.contextCode()]?.events
      if events
        delete events[event.id]

    eventWithId: (id) =>
      for contextCode, contextData of @cache.contexts
        if contextData.events[id]
          return contextData.events[id]

      null

    clearCache: () =>
      @cache = {
        contexts: {}
        appointmentGroups: {}
        participants: {}
        fetchedAppointmentGroups: null
      }
      for contextInfo in @contexts
        @cache.contexts[contextInfo.asset_string] = {
          events: {},
          fetchedRanges: [],
          fetchedUndated: false,
        }

    requiredDateRangeForContext: (start, end, context) =>
      unless contextInfo = @cache.contexts[context]
        return [ start, end ]

      unless ranges = contextInfo.fetchedRanges
        return [ start, end ]

      for range in ranges
        if range[0] <= start && start <= range[1]
          start = range[1]
        if range[0] <= end && end <= range[1]
          end = range[0]

      [ start, end ]

    requiredDateRangeForContexts: (start, end, contexts) =>
      # We assume that we're not going to need anything from the cache - setting
      # the initial assumptions to the opposites of the requests is a fun way to
      # do that.
      earliest = end
      latest = start
      for context in contexts
        [ s, e ] = @requiredDateRangeForContext(start, end, context)
        earliest = s if s < earliest
        latest = e if e > latest

      [ earliest, latest ]

    needUndatedEventsForContexts: (contexts) =>
      for context in contexts
        return true if !@cache.contexts[context].fetchedUndated
      false

    addEventToCache: (event) =>
      contextCode = event.contextCode()
      contextInfo = @cache.contexts[contextCode]

      contextInfo.events[event.id] = event

    getEventsFromCacheForContext: (start, end, context) =>
      contextInfo = @cache.contexts[context]

      events = []
      for id, event of contextInfo.events
        if !event.start && !start || event.start >= start && event.start <= end
          events.push event

      events

    processNextRequest: () =>
      request = @pendingRequests.shift()
      if request
        method = request[0]
        args = request[1]
        method args...

    getEventsFromCache: (start, end, contexts) =>
      events = []
      for context in contexts
        events = events.concat(@getEventsFromCacheForContext start, end, context)
      events

    getAppointmentGroupsFromCache: () =>
      (group for id, group of @cache.appointmentGroups)

    getAppointmentGroups: (fetchManageable, cb) =>
      if @inFlightRequest
        @pendingRequests.push([@getAppointmentGroups, arguments])
        return

      if @cache.fetchedAppointmentGroups && @cache.fetchedAppointmentGroups.manageable == fetchManageable
        cb @getAppointmentGroupsFromCache()
        @processNextRequest()
        return

      @cache.fetchedAppointmentGroups = { manageable: fetchManageable }
      @cache.appointmentGroups = {}

      dataCB = (data, url, params) =>
        if data
          for group in data
            if params.scope == "manageable"
              group.is_manageable = true
            else
              group.is_scheduleable = true
            @processAppointmentData group

      doneCB = () => cb @getAppointmentGroupsFromCache()

      fetchJobs = [[ '/api/v1/appointment_groups', { include: [ 'appointments', 'child_events' ] } ]]

      if fetchManageable
        fetchJobs.push [ '/api/v1/appointment_groups', { scope: 'manageable', include: [ 'appointments', 'child_events' ], include_past_appointments: true } ]

      @startFetch fetchJobs, dataCB, doneCB

    processAppointmentData: (group) =>
      id = group.id
      @cache.appointmentGroups[id] = group

      if group.appointments
        group.appointmentEvents = []
        for eventData in group.appointments
          event = commonEventFactory(eventData, @contexts)
          if event && event.object.workflow_state != 'deleted'
            group.appointmentEvents.push event
            @addEventToCache event

            if eventData.child_events
              event.childEvents = []
              for childEventData in eventData.child_events
                childEvent = commonEventFactory(childEventData, @contexts)
                @addEventToCache event
                event.childEvents.push childEvent

    getEventsForAppointmentGroup: (group, cb) =><commit_msg>added null check for child events in processAppointmentData<commit_after>define [
  'jquery'
  'compiled/calendar/commonEventFactory'
  'jquery.ajaxJSON'
  'vendor/jquery.ba-tinypubsub'
], ($, commonEventFactory) ->

  class
    constructor: (contexts) ->
      @contexts = contexts
      @clearCache()
      @inFlightRequest = false
      @pendingRequests = []

      # The cache will store all the events we've fetched so far, and looks like this:
      # {
      #   contexts: {
      #     "user_1": {
      #       fetchedRanges: [
      #         sorted list of [start, end] tuples that represent
      #         ranges of dates that we have already fetched for
      #       ]
      #       events: {
      #         "assignment_1": <CommonEvent object>
      #       },
      #       fetchedUndated: true/false
      #     }, ...
      #   },
      #   appointmentGroups: {
      #     "1": <object>
      #   },
      #   participants: {
      #     "1_unregistered": [ users or groups ]
      #   }
      #   fetchedAppointmentGroups: { manageable: true/false }
      # }
      #
      # Note that the appointmentGroups are not cached per context, as
      # we get them all in the same request (not scoped to contexts at
      # all.) This might end up being confusing.
      
      $.subscribe "CommonEvent/eventDeleted", @eventDeleted
      $.subscribe "CommonEvent/eventSaved", @eventSaved

    eventSaved: (event) =>
      @addEventToCache(event)

    eventDeleted: (event) =>
      events = @cache.contexts[event.contextCode()]?.events
      if events
        delete events[event.id]

    eventWithId: (id) =>
      for contextCode, contextData of @cache.contexts
        if contextData.events[id]
          return contextData.events[id]

      null

    clearCache: () =>
      @cache = {
        contexts: {}
        appointmentGroups: {}
        participants: {}
        fetchedAppointmentGroups: null
      }
      for contextInfo in @contexts
        @cache.contexts[contextInfo.asset_string] = {
          events: {},
          fetchedRanges: [],
          fetchedUndated: false,
        }

    requiredDateRangeForContext: (start, end, context) =>
      unless contextInfo = @cache.contexts[context]
        return [ start, end ]

      unless ranges = contextInfo.fetchedRanges
        return [ start, end ]

      for range in ranges
        if range[0] <= start && start <= range[1]
          start = range[1]
        if range[0] <= end && end <= range[1]
          end = range[0]

      [ start, end ]

    requiredDateRangeForContexts: (start, end, contexts) =>
      # We assume that we're not going to need anything from the cache - setting
      # the initial assumptions to the opposites of the requests is a fun way to
      # do that.
      earliest = end
      latest = start
      for context in contexts
        [ s, e ] = @requiredDateRangeForContext(start, end, context)
        earliest = s if s < earliest
        latest = e if e > latest

      [ earliest, latest ]

    needUndatedEventsForContexts: (contexts) =>
      for context in contexts
        return true if !@cache.contexts[context].fetchedUndated
      false

    addEventToCache: (event) =>
      contextCode = event.contextCode()
      contextInfo = @cache.contexts[contextCode]

      contextInfo.events[event.id] = event

    getEventsFromCacheForContext: (start, end, context) =>
      contextInfo = @cache.contexts[context]

      events = []
      for id, event of contextInfo.events
        if !event.start && !start || event.start >= start && event.start <= end
          events.push event

      events

    processNextRequest: () =>
      request = @pendingRequests.shift()
      if request
        method = request[0]
        args = request[1]
        method args...

    getEventsFromCache: (start, end, contexts) =>
      events = []
      for context in contexts
        events = events.concat(@getEventsFromCacheForContext start, end, context)
      events

    getAppointmentGroupsFromCache: () =>
      (group for id, group of @cache.appointmentGroups)

    getAppointmentGroups: (fetchManageable, cb) =>
      if @inFlightRequest
        @pendingRequests.push([@getAppointmentGroups, arguments])
        return

      if @cache.fetchedAppointmentGroups && @cache.fetchedAppointmentGroups.manageable == fetchManageable
        cb @getAppointmentGroupsFromCache()
        @processNextRequest()
        return

      @cache.fetchedAppointmentGroups = { manageable: fetchManageable }
      @cache.appointmentGroups = {}

      dataCB = (data, url, params) =>
        if data
          for group in data
            if params.scope == "manageable"
              group.is_manageable = true
            else
              group.is_scheduleable = true
            @processAppointmentData group

      doneCB = () => cb @getAppointmentGroupsFromCache()

      fetchJobs = [[ '/api/v1/appointment_groups', { include: [ 'appointments', 'child_events' ] } ]]

      if fetchManageable
        fetchJobs.push [ '/api/v1/appointment_groups', { scope: 'manageable', include: [ 'appointments', 'child_events' ], include_past_appointments: true } ]

      @startFetch fetchJobs, dataCB, doneCB

    processAppointmentData: (group) =>
      id = group.id
      @cache.appointmentGroups[id] = group

      if group.appointments
        group.appointmentEvents = []
        for eventData in group.appointments
          event = commonEventFactory(eventData, @contexts)
          if event && event.object.workflow_state != 'deleted'
            group.appointmentEvents.push event
            @addEventToCache event

            if eventData.child_events
              event.childEvents = []
              for childEventData in eventData.child_events
                childEvent = commonEventFactory(childEventData, @contexts)
                @addEventToCache event
                if childEvent
                  event.childEvents.push childEvent

    getEventsForAppointmentGroup: (group, cb) =>
{'fraction_non_alphanumeric': 0.09734810339039947, 'fraction_numerical': 0.0023497818059751594, 'mean_word_length': 2.203494623655914, 'pattern_counts': {'":': 8, '<': 19, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6713236', 'n_tokens_mistral': 3378, 'n_tokens_neox': 3292, 'n_words': 837}