Skip to content
This repository has been archived by the owner on Aug 23, 2022. It is now read-only.

Commit

Permalink
Merge branch 'release/1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
shomatan committed Aug 29, 2018
2 parents 4e519a0 + d01c40d commit 49c2eca
Show file tree
Hide file tree
Showing 32 changed files with 524 additions and 28 deletions.
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
CybozuLiveのグループを[Backlog]に移行するためのツールです。
(English document is described after Japanese)

**Backlog Migration for CybozuLiveはベータバージョンです。Backlog上の既存プロジェクトにインポートする場合は、先に新しく別プロジェクトを作成し、こちらにインポートし内容を確認後、正式なプロジェクトにインポートしてください**

* Backlog
* [https://backlog.com](https://backlog.com/)

Expand Down Expand Up @@ -103,11 +101,17 @@ Backlogの **管理者権限** が必要になります。
### プロジェクトについて
* テキスト整形のルール: **markdown**

### チャットデータの移行について
* チャットとして移行された課題の状態は全て **Open** となります。
* 課題の作成者と作成日時は、最初にチャットルームで投稿したユーザと投稿日時になります。

### CybozuLive側の制限について
- 掲示板/ToDoリスト
- コメント数:最新から10,000件
- イベント
- コメント数:最新から10,000件
- チャット
- コメント数:最新から10,000件
- 掲示板やイベントの添付ファイルは移行できません
- ToDoのカテゴリは移行できません。

Expand Down Expand Up @@ -255,11 +259,17 @@ Sample commands:
### Backlog's user roles
This program is for the users with the Space's **administrator** roles.

### About migrating chat data
* All issues that are created when migrating chats will have state **Open**.
* The user that first posted to the chat will be set as the issues Registered by and the first message post time will be set as the issues Created date.

### About limitations in CybozuLive
- Forum/ToDo-list
- Comments:10,000 from the latest
- Event
- Comments:10,000 from the latest
- Chat
- Comments:10,000 from the latest
- Can not migrate bulletin forum and event attachments
- The ToDo category can not be migrated.

Expand Down
6 changes: 3 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name := "backlog-migration-cybozulive"

lazy val commonSettings = Seq(
version := "1.0.0",
version := "1.1.0",
scalaVersion := "2.12.6"
)

Expand All @@ -24,7 +24,7 @@ lazy val root = (project in file("."))
.settings(commonSettings)
.settings(
libraryDependencies ++= {
val catsVersion = "1.1.0"
val catsVersion = "1.2.0"
val slickVersion = "3.2.3"
val monixVersion = "3.0.0-RC1"
Seq(
Expand All @@ -33,7 +33,7 @@ lazy val root = (project in file("."))
"org.typelevel" %% "cats-free" % catsVersion,
"com.typesafe.slick" %% "slick" % slickVersion,
"com.typesafe.slick" %% "slick-hikaricp" % slickVersion,
"org.xerial" % "sqlite-jdbc" % "3.21.0",
"org.xerial" % "sqlite-jdbc" % "3.23.1",
"io.monix" %% "monix" % monixVersion,
"io.monix" %% "monix-reactive" % monixVersion,
"io.monix" %% "monix-execution" % monixVersion,
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version = 1.1.4
sbt.version = 1.2.1
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
logLevel := Level.Warn

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")
2 changes: 1 addition & 1 deletion src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

application {
name = "Backlog Migration for CybozuLive"
version = "1.0.0"
version = "1.1.0"
title = ${application.name} ${application.version} (c) nulab.inc
fileName = backlog-migration-cybozulive-${application.version}.jar
language = default
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/messages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ name.mapping.user=User
name.mapping.priority=Priority
name.mapping.status=Status
name.status.open=Open
name.chat=Chat

# Validation
validation.access=Checking whether the {0} is accessible ...
Expand All @@ -36,6 +37,7 @@ validation.backlog_project_already_exist=Project "{0}" already exists. Do you wa
issue.type.todo=To-Do
issue.type.event=Event
issue.type.forum=Forum
issue.type.chat=Chat

# Mapping file
mapping.output_file=The mapping file about {0} is created. Check the mapping file and fix it if necessary.
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/messages_ja.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ name.mapping.user=ユーザー
name.mapping.priority=優先度
name.mapping.status=状態
name.status.open=未完了
name.chat=チャット

# Validation
validation.access={0}にアクセス可能かチェックしています...
Expand All @@ -36,6 +37,7 @@ validation.backlog_project_already_exist=プロジェクト[{0}]はBacklog内に
issue.type.todo=ToDo
issue.type.event=イベント
issue.type.forum=掲示板
issue.type.chat=チャット

# Mapping file
mapping.output_file={0}のマッピングファイルを作成しました。マッピングファイルを確認し、必要に応じて修正してください。
Expand Down
7 changes: 5 additions & 2 deletions src/main/scala/com/nulabinc/backlog/c2b/App.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ object App extends Logger {

val backlogApi = AllApi.accessKey(s"${config.backlogUrl}/api/v2/", config.backlogKey)

val csvFiles = Config.DATA_PATHS.toFile.listFiles().filter(_.getName.endsWith(".csv"))
val exportFiles = Config.DATA_PATHS
.toFile
.listFiles()
.filter(file => file.getName.endsWith(".csv") || file.getName.endsWith(".txt"))

for {
// Initialize
Expand All @@ -116,7 +119,7 @@ object App extends Logger {
_ <- AppDSL.fromStorage(StorageDSL.deleteFile(Config.DB_PATH))
_ <- AppDSL.fromStore(StoreDSL.createDatabase)
// Read CSV and to store
_ <- CybozuStore.copyToStore(csvFiles)
_ <- CybozuStore.copyToStore(exportFiles)
// Collect Backlog data to store
_ <- BacklogService.storePriorities(backlogApi.priorityApi)
_ <- BacklogService.storeStatuses(backlogApi.statusApi)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,25 @@ class IssueConverter()(implicit ctx: MappingContext) extends Logger {
}
}

def from(from: CybozuChat, firstPostUser: Option[CybozuUser], issueType: CybozuIssueType): Either[ConvertError, BacklogIssue] = {
val createdAt = DateUtil.toDateTimeString(from.createdAt)
for {
optCreator <- userConverter.to(firstPostUser)
} yield
defaultBacklogIssue.copy(
id = from.id,
summary = createBacklogIssueSummary(from.title),
description = from.description,
optIssueTypeName = Some(issueType.value),
operation = defaultBacklogIssue.operation.copy(
optCreatedUser = optCreator,
optCreated = Some(createdAt),
optUpdatedUser = optCreator,
optUpdated = Some(createdAt)
)
)
}

private def createBacklogIssueSummary(summary: String): BacklogIssueSummary =
BacklogIssueSummary(value = summary, original = summary)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,23 @@ object CybozuDBComment {
val tupled = (this.apply _).tupled

def from(parentIssueId: AnyId, comment: CybozuCSVComment, creatorId: AnyId): CybozuDBComment =
new CybozuDBComment(
CybozuDBComment(
id = 0,
parentId = parentIssueId,
creator = creatorId,
createdAt = comment.createdAt,
content = comment.content
)

def from(parentIssueId: AnyId, post: CybozuTextPost, postUserId: AnyId): CybozuDBComment =
CybozuDBComment(
id = 0,
parentId = parentIssueId,
creator = postUserId,
createdAt = post.postedAt,
content = post.content
)

def to(comment: CybozuDBComment, creator: CybozuUser): CybozuComment =
CybozuComment(
id = comment.id,
Expand Down Expand Up @@ -115,4 +124,22 @@ object CybozuDBForum {
updater = updaterId,
updatedAt = forum.updatedAt
)
}

case class CybozuDBChat(
id: AnyId,
title: String,
description: String
) extends Entity

object CybozuDBChat {

val tupled = (this.apply _).tupled

def from(topic: CybozuTextTopic): CybozuDBChat =
CybozuDBChat(
id = 0,
title = topic.title,
description = topic.description
)
}
25 changes: 24 additions & 1 deletion src/main/scala/com/nulabinc/backlog/c2b/datas/CybozuModels.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.nulabinc.backlog.c2b.datas

import java.time.{ZoneId, ZonedDateTime}

import com.nulabinc.backlog.c2b.datas.Types.{AnyId, DateTime}

case class CybozuUser(
Expand All @@ -10,7 +12,9 @@ case class CybozuUser(
object CybozuUser {
val tupled = (this.apply _).tupled
def from(user: CybozuCSVUser): CybozuUser =
new CybozuUser(0, user.value)
CybozuUser(0, user.value)
def fromCybozuTextUser(user: CybozuTextUser): CybozuUser =
CybozuUser(0, user.value)
}

case class CybozuTodo(
Expand Down Expand Up @@ -50,6 +54,14 @@ case class CybozuForum(
comments: Seq[CybozuComment]
)

case class CybozuChat(
id: AnyId,
title: String,
description: String,
comments: Seq[CybozuComment],
createdAt: DateTime = ZonedDateTime.now(ZoneId.systemDefault)
)

case class CybozuComment(
id: AnyId,
parentId: AnyId,
Expand All @@ -58,6 +70,17 @@ case class CybozuComment(
content: String
)

object CybozuComment {
def from(parentId: AnyId, post: CybozuTextPost, postUserId: AnyId): CybozuComment =
CybozuComment(
id = 0,
parentId = parentId,
creator = CybozuUser(postUserId, post.postUser.value),
createdAt = post.postedAt,
content = post.content
)
}

case class CybozuIssueType(value: String) extends AnyVal
case class CybozuStatus(value: String) extends AnyVal
case class CybozuPriority(value: String) extends AnyVal
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.nulabinc.backlog.c2b.datas

import com.nulabinc.backlog.c2b.datas.Types.DateTime

case class CybozuTextTopic(
title: String,
description: String,
)

case class CybozuTextPost(
content: String,
postUser: CybozuTextUser,
postedAt: DateTime
)

case class CybozuTextUser(value: String) extends AnyVal
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ object IssueType {
case object ToDo extends IssueType
case object Event extends IssueType
case object Forum extends IssueType
case object Chat extends IssueType
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.nulabinc.backlog.c2b.parsers

import com.nulabinc.backlog.c2b.datas.CybozuCSVComment
import com.nulabinc.backlog.c2b.datas.{CybozuCSVComment, CybozuTextPost}
import org.apache.commons.csv.CSVRecord

sealed trait ParseError[A]
case class CannotParseCSV[A](klass: Class[A], reason: String, record: CSVRecord) extends ParseError[A]
case class CannotParseFromString[A](klass: Class[A], value: String) extends ParseError[A] {
case class CannotParseFromString[A](klass: Class[A], reason: String, value: String) extends ParseError[A] {
override def toString: String = s"$klass $value"
}
case class CannotParseComment(reason: String, data: String) extends ParseError[CybozuCSVComment]
case class CannotParsePost(reason: String, data: String) extends ParseError[CybozuTextPost]
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.nulabinc.backlog.c2b.parsers

import com.nulabinc.backlog.c2b.datas.{CybozuTextPost, CybozuTextTopic, CybozuTextUser}

object TextFileParser {

private val titlePattern = """.+?: (.+?)""".r
private val MIN_TOPIC_LINES = 4
private val TITLE_LINE_INDEX = 1
private val DESCRIPRION_START_INDEX = 3

// post
private val MIN_POST_LINES = 5
private val userPattern = """\d+?: (.+?)""".r
private val USER_LINE_INDEX = 1
private val POSTED_AT_LINE_INDEX = 2
private val CONTENT_START_INDEX = 4

def topic(topicText: String): Either[ParseError[CybozuTextTopic], CybozuTextTopic] = {
val lines = topicText.split("\n")

if (lines.length < MIN_TOPIC_LINES)
Left(CannotParseFromString(classOf[CybozuTextTopic], "Invalid topic rows: min", topicText))
else {
val result = for {
title <- title(lines(TITLE_LINE_INDEX))
description = arrayToString(lines.slice(DESCRIPRION_START_INDEX, lines.length))
} yield CybozuTextTopic(title, description)
result match {
case Right(value) => Right(value)
case Left(error) => Left(CannotParseFromString(classOf[CybozuTextTopic], error.getMessage, lines.mkString("\n")))
}
}
}

def post(postStr: String): Either[ParseError[CybozuTextPost], CybozuTextPost] = {
val lines = postStr.split("\n")

if (lines.length < MIN_POST_LINES)
Left(CannotParsePost("Invalid post rows: min", postStr))
else {
val dateString = lines(POSTED_AT_LINE_INDEX)
val postedAtResult = ZonedDateTimeParser.toZonedDateTime(dateString) match {
case Right(value) => Right(value)
case Left(error) => Left(CannotParseFromString(classOf[CybozuTextPost], error.toString, dateString))
}
for {
postedAt <- postedAtResult
user <- user(lines(USER_LINE_INDEX))
} yield CybozuTextPost(
content = arrayToString(lines.slice(CONTENT_START_INDEX, lines.length)),
postUser = CybozuTextUser(user),
postedAt = postedAt
)
}
}

private[parsers] def title(line: String): Either[Throwable, String] =
line match {
case titlePattern(title) => Right(title)
case _ => Left(new RuntimeException("Cannot find topic title"))
}

private[parsers] def arrayToString(lines: Seq[String]): String =
lines.mkString("\n")

private[parsers] def user(line: String): Either[ParseError[CybozuTextPost], String] =
line match {
case userPattern(user) => Right(user)
case _ => Left(CannotParseFromString(classOf[CybozuTextPost], "Cannot find user string", line))
}

}
Loading

0 comments on commit 49c2eca

Please sign in to comment.