Skip to content
This repository has been archived by the owner on Mar 25, 2024. It is now read-only.

Commit

Permalink
Merge branch 'release/1.2.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
smallhadroncollider committed May 22, 2018
2 parents 047e4b2 + c53f3dd commit 7f02567
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 43 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,15 @@ Make sure you have permission to view the Trello board, otherwise you'll get an
### Limitations

- This is a one-off procedure: it effectively imports a Trello board to taskell
- Only lists, card titles, and card descriptions are currently supported
- Currently imports:
- Lists
- Cards
- Card descriptions
- Card due dates
- Card checklists (merged into one list per card)

### Plans

- Better support for Card details (e.g. sub-tasks, due dates)
- Full syncing with Trello: effectively using taskell as a CLI Trello front-end


Expand Down
2 changes: 1 addition & 1 deletion docs/html/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: taskell
tagline: Command-line Kanban board/task managment
baseurl: ""
locale: "en"
version: 1.2.1
version: 1.2.2
destination: _site/public
exclude: [deployment, Capfile, log, Gemfile, Gemfile.lock]

Expand Down
2 changes: 1 addition & 1 deletion package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: taskell
version: '1.2.1.0'
version: '1.2.2.0'
category: CLI
author: Mark Wales
maintainer: [email protected]
Expand Down
17 changes: 11 additions & 6 deletions roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

## Features

- Check times work no matter what timezone
- Sort out Homebrew forumula
> Make the necessary changes so that taskell can be put on the homebrew-core repository
* Find someone to submit it
Expand Down Expand Up @@ -78,12 +79,10 @@

## In Progress

- Move to column only works for columns before the one you're in
- Add Trello import
* ~Basic trello import~
* ~Add due date support~
* Add sub-tasks support
* ~Add card summary support~
- Improve Trello checklist import
* ~Take checklist fetch errors into account~
* ~Refactor code~
* Use Reader to pass around trello token?

## Done

Expand Down Expand Up @@ -213,3 +212,9 @@
* ~Editable due dates~
- Trello dates need to take current timezone into account
> Trello gives dates in UTC, but need to display them in the current timezone. Deadlines should also take timezones into account if necessary.
- Move to column only works for columns before the one you're in
- Add Trello import
* ~Basic trello import~
* ~Add due date support~
* ~Add sub-tasks support~
* ~Add card summary support~
2 changes: 1 addition & 1 deletion src/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ClassyPrelude
import Data.FileEmbed (embedFile)

version :: Text
version = "1.2.1"
version = "1.2.2"

usage :: Text
usage = decodeUtf8 $(embedFile "templates/usage.txt")
98 changes: 69 additions & 29 deletions src/IO/Trello.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,90 @@ import ClassyPrelude
import Network.HTTP.Simple (parseRequest, httpBS, getResponseBody, getResponseStatusCode)
import Data.Aeson

import IO.Trello.List (List, trelloListToList)
import IO.Trello.List (List, trelloListToList, cards)
import IO.Trello.Card (Card, idChecklists, setChecklists)
import IO.Trello.ChecklistItem (ChecklistItem, checkItems)
import Data.Taskell.Lists (Lists)
import Data.Time.LocalTime (TimeZone, getCurrentTimeZone)

type TrelloToken = Text
type TrelloBoardID = Text
type TrelloChecklistID = Text

key :: Text
key = "80dbcf6f88f62cc5639774e13342c20b"

root :: Text
root = "https://api.trello.com/1/boards/"

getUrl :: TrelloToken -> TrelloBoardID -> String
getUrl token board = unpack $ concat [
root,
board,
"/lists",
"?cards=all",
"&card_fields=name,due,desc",
"&fields=id,name,cards",
"&key=",
key,
"&token=",
token
root = "https://api.trello.com/1/"

fullURL :: Text -> TrelloToken -> String
fullURL uri token = unpack $ concat [root, uri, "&key=", key, "&token=", token]

boardURL :: TrelloBoardID -> TrelloToken -> String
boardURL board = fullURL $ concat [
"boards/", board, "/lists",
"?cards=open",
"&card_fields=name,due,desc,idChecklists",
"&fields=id,name,cards"
]

checklistURL :: TrelloChecklistID -> TrelloToken -> String
checklistURL checklist = fullURL $ concat [
"checklists/", checklist,
"?fields=id",
"&checkItem_fields=name,state"
]

trelloListsToLists :: TimeZone -> [List] -> Lists
trelloListsToLists tz ls = fromList $ trelloListToList tz <$> ls

fetch :: String -> IO (Int, ByteString)
fetch url = do
request <- parseRequest url
response <- httpBS request
return (getResponseStatusCode response, getResponseBody response)

getChecklist :: TrelloToken -> TrelloChecklistID -> IO (Either Text [ChecklistItem])
getChecklist token checklist = do
(status, body) <- fetch (checklistURL checklist token)

return $ case status of
200 -> case checkItems <$> decodeStrict body of
Just ls -> Right ls
Nothing -> Left "Could not parse response. Please file an Issue on GitHub."
429 -> Left "Too many checklists"
_ -> Left $ tshow status ++ " error while fetching checklist " ++ checklist

updateCard :: TrelloToken -> Card -> IO (Either Text Card)
updateCard token card = do
let ids = idChecklists card
checklists <- sequence $ getChecklist token <$> ids
return $ setChecklists card . concat <$> sequence checklists

updateList :: TrelloToken -> List -> IO (Either Text List)
updateList token l = do
let set c = l { cards = c }
cs <- sequence $ updateCard token <$> cards l
return $ set <$> sequence cs

getChecklists :: TrelloToken -> [List] -> IO (Either Text [List])
getChecklists token ls = do
lists <- sequence $ updateList token <$> ls
return $ sequence lists

getCards :: TrelloToken -> TrelloBoardID -> IO (Either Text Lists)
getCards token board = do
request <- parseRequest $ getUrl token board
response <- httpBS request
(status, body) <- fetch (boardURL board token)
timezone <- getCurrentTimeZone
let status = getResponseStatusCode response
let body = getResponseBody response

let result
| status == 200 = case trelloListsToLists timezone <$> decodeStrict body of
Just ls -> Right ls
Nothing -> Left "Could not parse response. Please file an Issue on GitHub."
| status == 404 = Left $ "Could not find Trello board " ++ board ++ ". Make sure the ID is correct"
| status == 401 = Left $ "You do not have permission to view Trello board " ++ board
| otherwise = Left $ tshow status ++ " error. Cannot fetch from Trello."

return result

putStrLn "Fetching from Trello..."

case status of
200 -> case decodeStrict body of
Just raw -> do
lists <- getChecklists token raw
return $ trelloListsToLists timezone <$> lists
Nothing -> return $ Left "Could not parse response. Please file an Issue on GitHub."
404 -> return . Left $ "Could not find Trello board " ++ board ++ ". Make sure the ID is correct"
401 -> return . Left $ "You do not have permission to view Trello board " ++ board
_ -> return . Left $ tshow status ++ " error. Cannot fetch from Trello."
15 changes: 12 additions & 3 deletions src/IO/Trello/Card.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,36 @@
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
module IO.Trello.Card (
Card
, idChecklists
, cardToTask
, setChecklists
) where

import ClassyPrelude

import Data.Aeson
import qualified Data.Taskell.Task as T (Task, new, setSummary, due)
import qualified Data.Taskell.Task as T (Task, new, setSummary, due, subTasks)
import Data.Taskell.Date (utcToLocalDay)
import Data.Time.Format (parseTimeM, iso8601DateFormat)
import Data.Time.LocalTime (TimeZone)
import IO.Trello.ChecklistItem (ChecklistItem, checklistItemToSubTask)

data Card = Card {
name :: Text
, desc :: Text
, due :: Maybe Text
, idChecklists :: [Text]
, checklists :: Maybe [ChecklistItem]
} deriving (Eq, Show, Generic, ToJSON, FromJSON)

textToTime :: TimeZone -> Text -> Maybe Day
textToTime tz text = utcToLocalDay tz <$> utc
where utc = parseTimeM False defaultTimeLocale (iso8601DateFormat (Just "%H:%M:%S%Q%Z")) $ unpack text

cardToTask :: TimeZone -> Card -> T.Task
cardToTask tz card = task { T.due = textToTime tz $ fromMaybe "" (due card) }
where task = T.setSummary (desc card) $ T.new (name card)
cardToTask tz card = task' { T.due = textToTime tz $ fromMaybe "" (due card) }
where task = T.setSummary (desc card) $ T.new (name card)
task' = task { T.subTasks = fromList $ checklistItemToSubTask <$> fromMaybe [] (checklists card) }

setChecklists :: Card -> [ChecklistItem] -> Card
setChecklists card cls = card { checklists = Just cls }
26 changes: 26 additions & 0 deletions src/IO/Trello/ChecklistItem.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
module IO.Trello.ChecklistItem (
ChecklistItem
, checklistItemToSubTask
, checkItems
) where

import ClassyPrelude

import Data.Aeson

import Data.Taskell.Task (SubTask, subTask)

data ChecklistItem = ChecklistItem {
name :: Text
, state :: Text
} deriving (Eq, Show, Generic, ToJSON, FromJSON)

data ChecklistWrapper = ChecklistWrapper {
checkItems :: [ChecklistItem]
} deriving (Eq, Show, Generic, ToJSON, FromJSON)

checklistItemToSubTask :: ChecklistItem -> SubTask
checklistItemToSubTask cl = subTask (name cl) (state cl == "complete")
Binary file modified trello.paw
Binary file not shown.

0 comments on commit 7f02567

Please sign in to comment.