Initial ideas around a related-to special property.
This commit is contained in:
		| @@ -181,6 +181,30 @@ Issue Properties: | |||||||
|           after 12 hours |           after 12 hours | ||||||
|           every 2 weeks, 10a544 |           every 2 weeks, 10a544 | ||||||
|  |  | ||||||
|  |     relations | ||||||
|  |  | ||||||
|  |       Used to store information about relationships between issues. PIT treats | ||||||
|  |       all relations as bi-directional and will update related tickets as | ||||||
|  |       necessary to maintain the integrity of the links. | ||||||
|  |  | ||||||
|  |       Relations are captured as <relation-type> <related-issue-id>. Multiple | ||||||
|  |       relations can be separated by ",". The <related-issue-id> must be a unique | ||||||
|  |       issue ID or ID prefix (in the case of multiple matching issues, the first | ||||||
|  |       found will be used with no guarantee on ordering). Valid value pairs for | ||||||
|  |       <relation-type> are: | ||||||
|  |  | ||||||
|  |         - child-of / parent-of | ||||||
|  |         - related-to (default) | ||||||
|  |         - blocks / blocked-by | ||||||
|  |         - follows / precedes | ||||||
|  |         - caused / caused-by | ||||||
|  |  | ||||||
|  |       Examples: | ||||||
|  |  | ||||||
|  |           relation: child-of fb3e63, blocked-by 2b71c1 | ||||||
|  |           relation: 2b71c1, 8f2b4c, follows 184dc6 | ||||||
|  |           relation: relates-to 2b71c1 | ||||||
|  |  | ||||||
|     tags |     tags | ||||||
|  |  | ||||||
|       If present, expected to be a comma-delimited list of text tags. The -g |       If present, expected to be a comma-delimited list of text tags. The -g | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ type | |||||||
|     filepath*: string |     filepath*: string | ||||||
|     summary*, details*: string |     summary*, details*: string | ||||||
|     properties*: TableRef[string, string] |     properties*: TableRef[string, string] | ||||||
|  |     relations*: seq[Relation] | ||||||
|     tags*: seq[string] |     tags*: seq[string] | ||||||
|     state*: IssueState |     state*: IssueState | ||||||
|  |  | ||||||
| @@ -40,6 +41,19 @@ type | |||||||
|     interval*: TimeInterval |     interval*: TimeInterval | ||||||
|     isFromCompletion*: bool |     isFromCompletion*: bool | ||||||
|  |  | ||||||
|  |   Relation* = tuple[rel: RelationType, id: UUID] | ||||||
|  |  | ||||||
|  |   RelationType* = enum | ||||||
|  |     ParentOf = "parent-of", | ||||||
|  |     ChildOf = "child-of", | ||||||
|  |     RelatedTo = "related-to", | ||||||
|  |     Blocks = "blocks", | ||||||
|  |     BlockedBy = "blocked-by", | ||||||
|  |     Follow = "follows", | ||||||
|  |     FollowedBy = "followed-by", | ||||||
|  |     Caused = "caused", | ||||||
|  |     CausedBy = "caused-by" | ||||||
|  |  | ||||||
| const DONE_FOLDER_FORMAT* = "yyyy-MM" | const DONE_FOLDER_FORMAT* = "yyyy-MM" | ||||||
| const ISO8601_MS = "yyyy-MM-dd'T'HH:mm:ss'.'fffzzz" | const ISO8601_MS = "yyyy-MM-dd'T'HH:mm:ss'.'fffzzz" | ||||||
|  |  | ||||||
| @@ -173,6 +187,11 @@ proc parseDate*(d: string): DateTime = | |||||||
|       continue |       continue | ||||||
|   raise newException(ValueError, "Unable to parse input as a date: " & d & errMsg) |   raise newException(ValueError, "Unable to parse input as a date: " & d & errMsg) | ||||||
|  |  | ||||||
|  | proc parseRelation*(relStr: string): Relation = | ||||||
|  |   let parts = relStr.split({' '}) | ||||||
|  |   if parts.len == 1: result = (RelatedTo, parseUUID(parts[0])) | ||||||
|  |   else: result = (parseEnum[RelationType](parts[0]), parseUUID(parts[1])) | ||||||
|  |  | ||||||
| ## Parse and format issues | ## Parse and format issues | ||||||
| proc fromStorageFormat*(id: string, issueTxt: string): Issue = | proc fromStorageFormat*(id: string, issueTxt: string): Issue = | ||||||
|   type ParseState = enum ReadingSummary, ReadingProps, ReadingDetails |   type ParseState = enum ReadingSummary, ReadingProps, ReadingDetails | ||||||
| @@ -180,6 +199,7 @@ proc fromStorageFormat*(id: string, issueTxt: string): Issue = | |||||||
|   result = Issue( |   result = Issue( | ||||||
|     id: parseUUID(id), |     id: parseUUID(id), | ||||||
|     properties: newTable[string,string](), |     properties: newTable[string,string](), | ||||||
|  |     relations: @[], | ||||||
|     tags: @[]) |     tags: @[]) | ||||||
|  |  | ||||||
|   var parseState = ReadingSummary |   var parseState = ReadingSummary | ||||||
| @@ -203,14 +223,16 @@ proc fromStorageFormat*(id: string, issueTxt: string): Issue = | |||||||
|         parseState = ReadingDetails |         parseState = ReadingDetails | ||||||
|         continue |         continue | ||||||
|  |  | ||||||
|  |  | ||||||
|       let parts = line.split({':'}, 1) --> map(it.strip()) |       let parts = line.split({':'}, 1) --> map(it.strip()) | ||||||
|       if parts.len != 2: |       if parts.len != 2: | ||||||
|         raise newException(ValueError, "unable to parse property line: " & line) |         raise newException(ValueError, "unable to parse property line: " & line) | ||||||
|  |  | ||||||
|       # Take care of special properties: `tags` |       # Take care of special properties: `tags` and `relations` | ||||||
|       if parts[0] == "tags": |       if parts[0] == "tags": | ||||||
|         result.tags = parts[1].split({','}) --> map(it.strip()) |         result.tags = parts[1].split({','}) --> map(it.strip()) | ||||||
|  |       elif parts[0] == "relations": | ||||||
|  |         result.relations = parts[1].split({','}) --> | ||||||
|  |           map(parseRelation(it.strip)) | ||||||
|       else: result[parts[0]] = parts[1] |       else: result[parts[0]] = parts[1] | ||||||
|  |  | ||||||
|     of ReadingDetails: |     of ReadingDetails: | ||||||
| @@ -231,6 +253,11 @@ proc toStorageFormat*(issue: Issue, withComments = false): string = | |||||||
|  |  | ||||||
|   if issue.tags.len > 0: lines.add("tags: " & issue.tags.join(",")) |   if issue.tags.len > 0: lines.add("tags: " & issue.tags.join(",")) | ||||||
|  |  | ||||||
|  |   if issue.relations.len > 0: | ||||||
|  |     lines.add("relations: " & | ||||||
|  |       (issue.relations --> map(it.rel & " " & it.id)). | ||||||
|  |       join(', ')) | ||||||
|  |  | ||||||
|   if not isEmptyOrWhitespace(issue.details) or withComments: |   if not isEmptyOrWhitespace(issue.details) or withComments: | ||||||
|     if withComments: lines.add("# Details go below the \"--------\"") |     if withComments: lines.add("# Details go below the \"--------\"") | ||||||
|     lines.add("--------") |     lines.add("--------") | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user