<commit_before>uuid          = require 'node-uuid'
platform      = require 'platform'
chalk         = require 'chalk'
_             = require '../vendor/lodash'
k             = require './constants'
filters       = require './filters'
hub           = require './hub'
{ serialize } = require './serialize'

DEFAULT_SRC = 'main'
DEFAULT_CHILD_TITLE = ''

# Record formats:
# * 1 (or undefined): initial version
# * 2: embeds objects directly, not their visual representation
#   (does not call treeLines before attaching). Circular refs are removed
RECORD_FORMAT_VERSION = 2

_hiddenStories = {}
_hubId = hub.getHubId()

#-----------------------------------------------
# ### Helpers
#-----------------------------------------------
_getStoryId = -> (if k.IS_BROWSER then "cs/" else "ss/") + uuid.v4()
_getRecordId = -> (if k.IS_BROWSER then 'c-' else 's-') + uuid.v4()

#-----------------------------------------------
# ## Story
#-----------------------------------------------
Story = ({parents, src, title, levelNum, fHiddenByFilter}) ->
  @parents = parents
  @fRoot = not parents.length
  @storyId = (if @fRoot then '*/' else '') + _getStoryId()
  @src = src
  @title = title
  @level = levelNum
  @fServer = not k.IS_BROWSER
  @t = new Date().getTime()
  @fOpen = true
  @status = undefined
  @hiddenRecords = []
  @fHiddenByFilter = fHiddenByFilter or (not filters.passesFilter @src, @level)
  if @fHiddenByFilter then _hiddenStories[@storyId] = @
  @emitAction 'CREATED', @t

#-----------------------------------------------
# ### Story lifecycle
#-----------------------------------------------
Story::close = -> 
  @fOpen = false
  @emitAction 'CLOSED'
  if @fHiddenByFilter
    _hiddenStories[@storyId] = null
    @hiddenRecords = []
  return
Story::changeTitle = (title) ->
  @title = title
  @emitAction 'TITLE_CHANGED'
  return
Story::changeStatus = (status) ->
  @status = status
  @emitAction 'STATUS_CHANGED'
  return

Story::child = (options = {}) -> 
  {src, title, extraParents, level: levelStr} = options
  src ?= DEFAULT_SRC
  title ?= DEFAULT_CHILD_TITLE
  levelNum = k.LEVEL_STR_TO_NUM[levelStr?.toUpperCase()] ? k.LEVEL_STR_TO_NUM.INFO
  parents = [@storyId]
  if extraParents? then parents = parents.concat extraParents
  return new Story {
    parents, src, title, levelNum, 
    fHiddenByFilter: @fHiddenByFilter
  }

#-----------------------------------------------
# ### Logs
#-----------------------------------------------
_.each k.LEVEL_STR_TO_NUM, (levelNum, levelStr) ->
  Story::[levelStr.toLowerCase()] = (src, msg, options) ->

    # Prepare arguments
    #- `log.info msg`
    if arguments.length <= 1
      msg = arguments[0] ? ''
      src = DEFAULT_SRC
    #- `log.info msg, options`
    else if _.isObject arguments[1]
      options = arguments[1]
      msg = arguments[0] ? ''
      src = DEFAULT_SRC
    options ?= {}

    # Filtering rule #1
    return if not filters.passesFilter src, levelNum

    # Prepare record
    record =
      storyId: @storyId
      level: levelNum
      src: src
      msg: msg
    _processAttachments record, options
    _completeRecord record

    # Filtering rule #2
    if @fHiddenByFilter
      if levelNum < k.LEVEL_STR_TO_NUM.WARN
        @hiddenRecords.push record
        return
      @reveal()

    _emit record
    return

#-----------------------------------------------
# ### Story helpers
#-----------------------------------------------
Story::emitAction = (action, t) ->
  record =
    parents: @parents
    fRoot: @fRoot
    storyId: @storyId
    src: @src
    title: @title
    level: @level
    fServer: @fServer
    t: t
    fOpen: @fOpen
    status: @status
    fStory: true
    action: action
  _completeRecord record
  if @fHiddenByFilter
    @hiddenRecords.push record
    return
  _emit record
  return

# Reveal parents recursively, and then reveal myself
Story::reveal = ->
  for parentStoryId in @parents
    _hiddenStories[parentStoryId]?.reveal()
  @fHiddenByFilter = false
  _hiddenStories[@storyId] = null
  _emit record for record in @hiddenRecords
  @hiddenRecords = []
  return

# Records can be logs or stories:
# * Common to stories and logs:
#   - `id: string` (a unique record id)
#   - `hubId: string`
#   - `version: integer`
#   - `fStory: boolean`
#   - `fServer: boolean`
#   - `storyId: string`
#   - `t: string` (for stories, creation time)
#   - `src: string?`
#   - `level: number`
# * Only for stories:
#   - `fRoot: boolean`
#   - `title: string?`
#   - `action: string`
#   - `parents: Array`
# * Only for logs:
#   - `msg: string`
#   - `obj: object?`
#   - `objExpanded: bool?`
#   - `objLevel: integer?`
#   - `objOptions: object?`
#   - `objIsError: bool?`
_completeRecord = (record) ->
  record.id = _getRecordId()
  record.hubId = _hubId
  record.version = RECORD_FORMAT_VERSION
  record.t ?= new Date().getTime()
  record.fServer = not k.IS_BROWSER
  record.fStory ?= false
  record.fRoot ?= false<commit_msg>Fix docs<commit_after>uuid          = require 'node-uuid'
platform      = require 'platform'
chalk         = require 'chalk'
_             = require '../vendor/lodash'
k             = require './constants'
filters       = require './filters'
hub           = require './hub'
{ serialize } = require './serialize'

DEFAULT_SRC = 'main'
DEFAULT_CHILD_TITLE = ''

# Record formats:
# * 1 (or undefined): initial version
# * 2: embeds objects directly, not their visual representation
#   (does not call treeLines before attaching). Circular refs are removed
RECORD_FORMAT_VERSION = 2

_hiddenStories = {}
_hubId = hub.getHubId()

#-----------------------------------------------
# ### Helpers
#-----------------------------------------------
_getStoryId = -> (if k.IS_BROWSER then "cs/" else "ss/") + uuid.v4()
_getRecordId = -> (if k.IS_BROWSER then 'c-' else 's-') + uuid.v4()

#-----------------------------------------------
# ## Story
#-----------------------------------------------
Story = ({parents, src, title, levelNum, fHiddenByFilter}) ->
  @parents = parents
  @fRoot = not parents.length
  @storyId = (if @fRoot then '*/' else '') + _getStoryId()
  @src = src
  @title = title
  @level = levelNum
  @fServer = not k.IS_BROWSER
  @t = new Date().getTime()
  @fOpen = true
  @status = undefined
  @hiddenRecords = []
  @fHiddenByFilter = fHiddenByFilter or (not filters.passesFilter @src, @level)
  if @fHiddenByFilter then _hiddenStories[@storyId] = @
  @emitAction 'CREATED', @t

#-----------------------------------------------
# ### Story lifecycle
#-----------------------------------------------
Story::close = -> 
  @fOpen = false
  @emitAction 'CLOSED'
  if @fHiddenByFilter
    _hiddenStories[@storyId] = null
    @hiddenRecords = []
  return
Story::changeTitle = (title) ->
  @title = title
  @emitAction 'TITLE_CHANGED'
  return
Story::changeStatus = (status) ->
  @status = status
  @emitAction 'STATUS_CHANGED'
  return

Story::child = (options = {}) -> 
  {src, title, extraParents, level: levelStr} = options
  src ?= DEFAULT_SRC
  title ?= DEFAULT_CHILD_TITLE
  levelNum = k.LEVEL_STR_TO_NUM[levelStr?.toUpperCase()] ? k.LEVEL_STR_TO_NUM.INFO
  parents = [@storyId]
  if extraParents? then parents = parents.concat extraParents
  return new Story {
    parents, src, title, levelNum, 
    fHiddenByFilter: @fHiddenByFilter
  }

#-----------------------------------------------
# ### Logs
#-----------------------------------------------
_.each k.LEVEL_STR_TO_NUM, (levelNum, levelStr) ->
  Story::[levelStr.toLowerCase()] = (src, msg, options) ->

    # Prepare arguments
    #- `log.info msg`
    if arguments.length <= 1
      msg = arguments[0] ? ''
      src = DEFAULT_SRC
    #- `log.info msg, options`
    else if _.isObject arguments[1]
      options = arguments[1]
      msg = arguments[0] ? ''
      src = DEFAULT_SRC
    options ?= {}

    # Filtering rule #1
    return if not filters.passesFilter src, levelNum

    # Prepare record
    record =
      storyId: @storyId
      level: levelNum
      src: src
      msg: msg
    _processAttachments record, options
    _completeRecord record

    # Filtering rule #2
    if @fHiddenByFilter
      if levelNum < k.LEVEL_STR_TO_NUM.WARN
        @hiddenRecords.push record
        return
      @reveal()

    _emit record
    return

#-----------------------------------------------
# ### Story helpers
#-----------------------------------------------
Story::emitAction = (action, t) ->
  record =
    parents: @parents
    fRoot: @fRoot
    storyId: @storyId
    src: @src
    title: @title
    level: @level
    fServer: @fServer
    t: t
    fOpen: @fOpen
    status: @status
    fStory: true
    action: action
  _completeRecord record
  if @fHiddenByFilter
    @hiddenRecords.push record
    return
  _emit record
  return

# Reveal parents recursively, and then reveal myself
Story::reveal = ->
  for parentStoryId in @parents
    _hiddenStories[parentStoryId]?.reveal()
  @fHiddenByFilter = false
  _hiddenStories[@storyId] = null
  _emit record for record in @hiddenRecords
  @hiddenRecords = []
  return

# Records can be logs or stories:
# * Common to stories and logs:
#   - `id: string` (a unique record id)
#   - `hubId: string`
#   - `version: integer`
#   - `fStory: boolean`
#   - `fServer: boolean`
#   - `storyId: string`
#   - `t: number` [ms] (for stories, creation time)
#   - `src: string?`
#   - `level: number`
# * Only for stories:
#   - `fRoot: boolean`
#   - `title: string?`
#   - `action: string`
#   - `parents: Array`
# * Only for logs:
#   - `msg: string`
#   - `obj: object?`
#   - `objExpanded: bool?`
#   - `objLevel: integer?`
#   - `objOptions: object?`
#   - `objIsError: bool?`
_completeRecord = (record) ->
  record.id = _getRecordId()
  record.hubId = _hubId
  record.version = RECORD_FORMAT_VERSION
  record.t ?= new Date().getTime()
  record.fServer = not k.IS_BROWSER
  record.fStory ?= false
  record.fRoot ?= false
{'fraction_non_alphanumeric': 0.21376518218623483, 'fraction_numerical': 0.0024291497975708503, 'mean_word_length': 2.114087614245194, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 27, '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': '886778', 'n_tokens_mistral': 3152, 'n_tokens_neox': 3092, 'n_words': 803}