import Boards from './boards' ;
export class CsvCreator {
constructor ( data ) {
// date to be used for timestamps during import
this . _nowDate = new Date ( ) ;
// index to help keep track of what information a column stores
// each row represents a card
this . fieldIndex = { } ;
this . lists = { } ;
// Map of members using username => wekanid
this . members = data . membersMapping ? data . membersMapping : { } ;
this . swimlane = null ;
}
/ * *
* If dateString is provided ,
* return the Date it represents .
* If not , will return the date when it was first called .
* This is useful for us , as we want all import operations to
* have the exact same date for easier later retrieval .
*
* @ param { String } dateString a properly formatted Date
* /
_now ( dateString ) {
if ( dateString ) {
return new Date ( dateString ) ;
}
if ( ! this . _nowDate ) {
this . _nowDate = new Date ( ) ;
}
return this . _nowDate ;
}
_user ( wekanUserId ) {
if ( wekanUserId && this . members [ wekanUserId ] ) {
return this . members [ wekanUserId ] ;
}
return Meteor . userId ( ) ;
}
/ * *
* Map the header row titles to an index to help assign proper values to the cards ' fields
* Valid headers ( name of card fields ) :
* title , description , status , owner , member , label , due date , start date , finish date , created at , updated at
* Some header aliases can also be accepted .
* Headers are NOT case - sensitive .
*
* @ param { Array } headerRow array from row of headers of imported CSV / TSV for cards
* /
mapHeadertoCardFieldIndex ( headerRow ) {
const index = { } ;
index . customFields = [ ] ;
for ( let i = 0 ; i < headerRow . length ; i ++ ) {
switch ( headerRow [ i ] . trim ( ) . toLowerCase ( ) ) {
case 'title' :
index . title = i ;
break ;
case 'description' :
index . description = i ;
break ;
case 'stage' :
case 'status' :
case 'state' :
index . stage = i ;
break ;
case 'owner' :
index . owner = i ;
break ;
case 'members' :
case 'member' :
index . members = i ;
break ;
case 'labels' :
case 'label' :
index . labels = i ;
break ;
case 'due date' :
case 'deadline' :
case 'due at' :
index . dueAt = i ;
break ;
case 'start date' :
case 'start at' :
index . startAt = i ;
break ;
case 'finish date' :
case 'end at' :
index . endAt = i ;
break ;
case 'creation date' :
case 'created at' :
index . createdAt = i ;
break ;
case 'update date' :
case 'updated at' :
case 'modified at' :
case 'modified on' :
index . modifiedAt = i ;
break ;
}
if ( headerRow [ i ] . toLowerCase ( ) . startsWith ( 'customfield' ) ) {
if ( headerRow [ i ] . split ( '-' ) [ 2 ] === 'dropdown' ) {
index . customFields . push ( {
name : headerRow [ i ] . split ( '-' ) [ 1 ] ,
type : headerRow [ i ] . split ( '-' ) [ 2 ] ,
options : headerRow [ i ] . split ( '-' ) [ 3 ] . split ( '/' ) ,
position : i ,
} ) ;
} else if ( headerRow [ i ] . split ( '-' ) [ 2 ] === 'currency' ) {
index . customFields . push ( {
name : headerRow [ i ] . split ( '-' ) [ 1 ] ,
type : headerRow [ i ] . split ( '-' ) [ 2 ] ,
currencyCode : headerRow [ i ] . split ( '-' ) [ 3 ] ,
position : i ,
} ) ;
} else {
index . customFields . push ( {
name : headerRow [ i ] . split ( '-' ) [ 1 ] ,
type : headerRow [ i ] . split ( '-' ) [ 2 ] ,
position : i ,
} ) ;
}
}
}
this . fieldIndex = index ;
}
createCustomFields ( boardId ) {
this . fieldIndex . customFields . forEach ( customField => {
let settings = { } ;
if ( customField . type === 'dropdown' ) {
settings = {
dropdownItems : customField . options . map ( option => {
return { _id : Random . id ( 6 ) , name : option } ;
} ) ,
} ;
} else if ( customField . type === 'currency' ) {
settings = {
currencyCode : customField . currencyCode ,
} ;
} else {
settings = { } ;
}
const id = CustomFields . direct . insert ( {
name : customField . name ,
type : customField . type ,
settings ,
showOnCard : false ,
automaticallyOnCard : false ,
showLabelOnMiniCard : false ,
boardIds : [ boardId ] ,
} ) ;
customField . id = id ;
customField . settings = settings ;
} ) ;
}
createBoard ( csvData ) {
const boardToCreate = {
archived : false ,
color : 'belize' ,
createdAt : this . _now ( ) ,
labels : [ ] ,
members : [
{
userId : Meteor . userId ( ) ,
wekanId : Meteor . userId ( ) ,
isActive : true ,
isAdmin : true ,
isNoComments : false ,
isCommentOnly : false ,
swimlaneId : false ,
} ,
] ,
modifiedAt : this . _now ( ) ,
//default is private, should inform user.
permission : 'private' ,
slug : 'board' ,
stars : 0 ,
title : ` Imported Board ${ this . _now ( ) } ` ,
} ;
// create labels
const labelsToCreate = new Set ( ) ;
for ( let i = 1 ; i < csvData . length ; i ++ ) {
if ( csvData [ i ] [ this . fieldIndex . labels ] ) {
for ( const importedLabel of csvData [ i ] [ this . fieldIndex . labels ] . split (
' ' ,
) ) {
if ( importedLabel && importedLabel . length > 0 ) {
labelsToCreate . add ( importedLabel ) ;
}
}
}
}
for ( const label of labelsToCreate ) {
let labelName , labelColor ;
if ( label . indexOf ( '-' ) > - 1 ) {
labelName = label . split ( '-' ) [ 0 ] ;
labelColor = label . split ( '-' ) [ 1 ] ;
} else {
labelName = label ;
}
const labelToCreate = {
_id : Random . id ( 6 ) ,
color : labelColor ? labelColor : 'black' ,
name : labelName ,
} ;
boardToCreate . labels . push ( labelToCreate ) ;
}
const boardId = Boards . direct . insert ( boardToCreate ) ;
Boards . direct . update ( boardId , {
$set : {
modifiedAt : this . _now ( ) ,
} ,
} ) ;
// log activity
Activities . direct . insert ( {
activityType : 'importBoard' ,
boardId ,
createdAt : this . _now ( ) ,
source : {
id : boardId ,
system : 'CSV/TSV' ,
} ,
// We attribute the import to current user,
// not the author from the original object.
userId : this . _user ( ) ,
} ) ;
return boardId ;
}
createSwimlanes ( boardId ) {
const swimlaneToCreate = {
archived : false ,
boardId ,
createdAt : this . _now ( ) ,
title : 'Default' ,
sort : 1 ,
} ;
const swimlaneId = Swimlanes . direct . insert ( swimlaneToCreate ) ;
Swimlanes . direct . update ( swimlaneId , { $set : { updatedAt : this . _now ( ) } } ) ;
this . swimlane = swimlaneId ;
}
createLists ( csvData , boardId ) {
let numOfCreatedLists = 0 ;
for ( let i = 1 ; i < csvData . length ; i ++ ) {
const listToCreate = {
archived : false ,
boardId ,
createdAt : this . _now ( ) ,
} ;
if ( csvData [ i ] [ this . fieldIndex . stage ] ) {
const existingList = Lists . find ( {
title : csvData [ i ] [ this . fieldIndex . stage ] ,
boardId ,
} ) . fetch ( ) ;
if ( existingList . length > 0 ) {
continue ;
} else {
listToCreate . title = csvData [ i ] [ this . fieldIndex . stage ] ;
}
} else listToCreate . title = ` Imported List ${ this . _now ( ) } ` ;
const listId = Lists . direct . insert ( listToCreate ) ;
this . lists [ csvData [ i ] [ this . fieldIndex . stage ] ] = listId ;
numOfCreatedLists ++ ;
Lists . direct . update ( listId , {
$set : {
updatedAt : this . _now ( ) ,
sort : numOfCreatedLists ,
} ,
} ) ;
}
}
createCards ( csvData , boardId ) {
for ( let i = 1 ; i < csvData . length ; i ++ ) {
const cardToCreate = {
archived : false ,
boardId ,
createdAt :
csvData [ i ] [ this . fieldIndex . createdAt ] !== ' ' || ''
? this . _now ( new Date ( csvData [ i ] [ this . fieldIndex . createdAt ] ) )
: null ,
dateLastActivity : this . _now ( ) ,
description : csvData [ i ] [ this . fieldIndex . description ] ,
listId : this . lists [ csvData [ i ] [ this . fieldIndex . stage ] ] ,
swimlaneId : this . swimlane ,
sort : - 1 ,
title : csvData [ i ] [ this . fieldIndex . title ] ,
userId : this . _user ( ) ,
startAt :
csvData [ i ] [ this . fieldIndex . startAt ] !== ' ' || ''
? this . _now ( new Date ( csvData [ i ] [ this . fieldIndex . startAt ] ) )
: null ,
dueAt :
csvData [ i ] [ this . fieldIndex . dueAt ] !== ' ' || ''
? this . _now ( new Date ( csvData [ i ] [ this . fieldIndex . dueAt ] ) )
: null ,
endAt :
csvData [ i ] [ this . fieldIndex . endAt ] !== ' ' || ''
? this . _now ( new Date ( csvData [ i ] [ this . fieldIndex . endAt ] ) )
: null ,
spentTime : null ,
labelIds : [ ] ,
modifiedAt :
csvData [ i ] [ this . fieldIndex . modifiedAt ] !== ' ' || ''
? this . _now ( new Date ( csvData [ i ] [ this . fieldIndex . modifiedAt ] ) )
: null ,
} ;
// add the labels
if ( csvData [ i ] [ this . fieldIndex . labels ] ) {
const board = Boards . findOne ( boardId ) ;
for ( const importedLabel of csvData [ i ] [ this . fieldIndex . labels ] . split (
' ' ,
) ) {
if ( importedLabel && importedLabel . length > 0 ) {
let labelToApply ;
if ( importedLabel . indexOf ( '-' ) === - 1 ) {
labelToApply = board . getLabel ( importedLabel , 'black' ) ;
} else {
labelToApply = board . getLabel (
importedLabel . split ( '-' ) [ 0 ] ,
importedLabel . split ( '-' ) [ 1 ] ,
) ;
}
cardToCreate . labelIds . push ( labelToApply . _id ) ;
}
}
}
// add the members
if ( csvData [ i ] [ this . fieldIndex . members ] ) {
const wekanMembers = [ ] ;
for ( const importedMember of csvData [ i ] [ this . fieldIndex . members ] . split (
' ' ,
) ) {
if ( this . members [ importedMember ] ) {
const wekanId = this . members [ importedMember ] ;
if ( ! wekanMembers . find ( wId => wId === wekanId ) ) {
wekanMembers . push ( wekanId ) ;
}
}
}
if ( wekanMembers . length > 0 ) {
cardToCreate . members = wekanMembers ;
}
}
// add the custom fields
if ( this . fieldIndex . customFields . length > 0 ) {
const customFields = [ ] ;
this . fieldIndex . customFields . forEach ( customField => {
if ( csvData [ i ] [ customField . position ] !== ' ' ) {
if ( customField . type === 'dropdown' ) {
customFields . push ( {
_id : customField . id ,
value : customField . settings . dropdownItems . find (
( { name } ) => name === csvData [ i ] [ customField . position ] ,
) . _id ,
} ) ;
} else {
customFields . push ( {
_id : customField . id ,
value : csvData [ i ] [ customField . position ] ,
} ) ;
}
}
cardToCreate . customFields = customFields ;
} ) ;
Cards . direct . insert ( cardToCreate ) ;
}
}
}
create ( board , currentBoardId ) {
const isSandstorm =
Meteor . settings &&
Meteor . settings . public &&
Meteor . settings . public . sandstorm ;
if ( isSandstorm && currentBoardId ) {
const currentBoard = Boards . findOne ( currentBoardId ) ;
currentBoard . archive ( ) ;
}
this . mapHeadertoCardFieldIndex ( board [ 0 ] ) ;
const boardId = this . createBoard ( board ) ;
this . createLists ( board , boardId ) ;
this . createSwimlanes ( boardId ) ;
this . createCustomFields ( boardId ) ;
this . createCards ( board , boardId ) ;
return boardId ;
}
}