--- a/trac/cmd.go Fri Jul 03 20:12:12 2020 -0500
+++ b/trac/cmd.go Fri Jul 03 22:56:34 2020 -0500
@@ -7,7 +7,9 @@
- EnvPath string `kong:"arg,name='tracenv',help='The path to the trac environment'"`
+ EnvPath string `kong:"arg,name='tracenv',help='The path to the trac environment'"` + ImportUsers string `kong:"flag,name='import-users',help='The json file of users to import',required='true',type='existingfile'"` + UnknownUser string `kong:"flag,name='unknown-user',help='The login name to use for unknown users',default='ghost'"` func (c *Cmd) Run(g *globals.Globals) error {
@@ -18,7 +20,7 @@
- project, err := createProject(env)
+ project, err := createProject(env, c.ImportUsers, c.UnknownUser) --- a/trac/project.go Fri Jul 03 20:12:12 2020 -0500
+++ b/trac/project.go Fri Jul 03 22:56:34 2020 -0500
@@ -6,14 +6,19 @@
"keep.imfreedom.org/grim/youtrack-import/youtrack"
-func createProject(e *environment) (*youtrack.Project, error) {
- users, err := e.loadUsers()
+func createProject(e *environment, importUsersFile, defaultUser string) (*youtrack.Project, error) { + users, err := e.loadUsers(importUsersFile) fmt.Printf("loaded %d users\n", len(users))
+ components, err := e.loadComponents() tracTickets, err := e.loadTickets()
@@ -21,7 +26,7 @@
issues := make([]*youtrack.Issue, len(tracTickets))
for idx, ticket := range tracTickets {
- issue, err := ticket.toYoutrack()
+ issue, err := ticket.toYoutrack(defaultUser, users) @@ -29,8 +34,17 @@
+ sliceUsers := make([]*youtrack.User, len(users)) + for _, user := range users { project := &youtrack.Project{
+ Subsystems: components, fmt.Printf("Project: has %d issues\n", len(issues))
--- a/trac/tickets.go Fri Jul 03 20:12:12 2020 -0500
+++ b/trac/tickets.go Fri Jul 03 22:56:34 2020 -0500
@@ -45,29 +45,68 @@
Value sql.NullString `db`
-func (t *ticket) toYoutrack() (*youtrack.Issue, error) {
+ typeMap = map[string]string{ + "enhancement": "Feature", + "plugin request": "Feature", + "rejected_patch": "Feature", + "translation": "Feature", + stateMap = map[string]string{ + "pending": "To be discussed", + priorityMap = map[string]string{ + "blocker": "Show-stopper", + "critical": "Critical", +func (t *ticket) toYoutrack(defaultUser string, users map[string]*youtrack.User) (*youtrack.Issue, error) { + convertUsername := func(sqluser sql.NullString) string { + username := convertString(sqluser) + if user, found := users[username]; found { issue := &youtrack.Issue{
Description: t.Description,
Created: convertTime(t.Time),
Updated: convertTime(t.ChangeTime),
- Reporter: convertString(t.Reporter),
- Assignee: convertString(t.Owner),
+ Reporter: convertUsername(t.Reporter), + Assignee: convertUsername(t.Owner), Version: convertString(t.Version),
Subsystem: convertString(t.Component),
- Priority: convertString(t.Priority),
+ Priority: priorityMap[convertString(t.Priority)], + State: stateMap[t.Status], - // convert the trac cc to a string slice
+ // convert the trac cc to a string slice of known youtrack users watchers := strings.Split(convertString(t.CarbonCopy), ",")
- for idx, watcher := range watchers {
- watchers[idx] = strings.TrimSpace(watcher)
+ ytWatchers := []string{} + for _, watcher := range watchers { + if ytUser, found := users[watcher]; found { + ytWatchers = append(ytWatchers, ytUser.Login) - issue.Watchers = watchers
+ issue.Watchers = ytWatchers // walk through the changes and update fields as necessary
for _, change := range t.Changes {
--- a/trac/users.go Fri Jul 03 20:12:12 2020 -0500
+++ b/trac/users.go Fri Jul 03 22:56:34 2020 -0500
@@ -1,6 +1,10 @@
"keep.imfreedom.org/grim/youtrack-import/youtrack"
@@ -11,30 +15,43 @@
Value string `db:"value"`
-func (e *environment) loadUsers() (map[string]*youtrack.User, error) {
+type importUsers struct { + Users map[string]importUser `json:"users"` - users := map[string]*youtrack.User{}
+type importUser struct { + Login string `json:"login"` + FullName string `json:"fullname"` + Email string `json:"email"` - sessionAttributes := []sessionAttribute{}
- err = e.db.Select(&sessionAttributes, "SELECT * FROM session_attribute WHERE name = 'name' or name = 'email'")
+func loadImportUsers(importUsersFile string) (*importUsers, error) { + data, err := ioutil.ReadFile(importUsersFile) - for _, sessionAttribute := range sessionAttributes {
- user, found := users[sessionAttribute.Sid]
- sid := sessionAttribute.Sid
- user = &youtrack.User{Login: sid}
+ importUsers := &importUsers{} + err = json.Unmarshal(data, importUsers) + return importUsers, err +func (e *environment) loadUsers(importUsersFile string) (map[string]*youtrack.User, error) { - switch sessionAttribute.Name {
- user.FullName = sessionAttribute.Value
- user.Email = sessionAttribute.Value
+ importUsers, err := loadImportUsers(importUsersFile) + users := map[string]*youtrack.User{} + for tracId, user := range importUsers.Users { + users[tracId] = &youtrack.User{ + Login: strings.TrimSpace(user.Login), + FullName: strings.TrimSpace(user.FullName), + Email: strings.TrimSpace(user.Email),