commenter 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/CLAUDE.md +68 -0
- data/README.adoc +115 -14
- data/commenter.gemspec +3 -2
- data/lib/commenter/cli.rb +23 -25
- data/lib/commenter/comment.rb +23 -8
- data/lib/commenter/comment_sheet.rb +18 -2
- data/lib/commenter/comment_type.rb +35 -0
- data/lib/commenter/github_integration.rb +11 -60
- data/lib/commenter/github_session.rb +35 -0
- data/lib/commenter/parser/osd_xlsx_parser.rb +243 -0
- data/lib/commenter/parser/track_change_docx_parser.rb +332 -0
- data/lib/commenter/parser.rb +93 -37
- data/lib/commenter/version.rb +1 -1
- data/lib/commenter.rb +5 -0
- data/schema/iso_comment_2012-03.yaml +2 -2
- data/schema/iso_comment_osd.yaml +112 -0
- data/spec/commenter/cli_spec.rb +35 -0
- data/spec/commenter/comment_sheet_spec.rb +25 -0
- data/spec/commenter/comment_type_spec.rb +45 -0
- data/spec/commenter/github_integration_spec.rb +65 -32
- data/spec/commenter/osd_xlsx_parser_spec.rb +180 -0
- data/spec/commenter/track_change_docx_parser_spec.rb +277 -0
- data/spec/support/osd_fixtures.rb +94 -0
- data/spec/support/redline_docx_builder.rb +88 -0
- data/spec/support/xlsx_builder.rb +147 -0
- metadata +32 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1609bb9eed80e408daebc9c67cda2c657e8484186acce89b823e6b411a5f9ea5
|
|
4
|
+
data.tar.gz: 46316674e2c639b9256390385d2cb3c59ed8946b604b44fbb0be28728475cd38
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 887aa19ab8ca3559fd20caf3e19a9941b631c677faeda86553597671883fc8dbae8c1e1f6d83bd0014e036774743addc16bc538b7404f6939be6636e1d623704
|
|
7
|
+
data.tar.gz: 3b850d0872f2f572b599d0bf7a70a977e1e5e7e797f5fb3447905c09cc7578cff18d99de6b32ae3b539758b7319b07590fdfd9d5249f74024ffdcd842d48d09d
|
data/.rubocop.yml
CHANGED
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bin/setup # install dependencies
|
|
9
|
+
bundle exec rake # default task: specs + RuboCop (this is what CI runs)
|
|
10
|
+
bundle exec rspec # all specs
|
|
11
|
+
bundle exec rspec spec/commenter/comment_spec.rb # single spec file
|
|
12
|
+
bundle exec rspec spec/commenter/comment_spec.rb:42 # single example
|
|
13
|
+
bundle exec rubocop # lint (part of default rake task — must be clean)
|
|
14
|
+
bundle exec rubocop -a # autocorrect
|
|
15
|
+
bundle exec exe/commenter # run the CLI locally
|
|
16
|
+
bin/console # interactive prompt
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
CI (metanorma/ci `generic-rake.yml`) runs `bundle exec rake` on Ruby 3.2/3.4/4.0 across macOS/Ubuntu/Windows. RuboCop offenses fail CI — run `bundle exec rake`, not just rspec, before pushing. `Gemfile.lock` is gitignored; dependencies resolve fresh in CI.
|
|
20
|
+
|
|
21
|
+
Specs generate XLSX fixtures at runtime via `spec/support/xlsx_builder.rb` (plain rubyzip, no spreadsheet-writing dependency) plus row data in `spec/support/osd_fixtures.rb` — there are no binary fixture files in the repo. Redline DOCX fixtures are built at runtime via `spec/support/redline_docx_builder.rb` (same approach).
|
|
22
|
+
|
|
23
|
+
## Release
|
|
24
|
+
|
|
25
|
+
NEVER bump `lib/commenter/version.rb` by hand and never push tags. Releases go through the GHA workflow, which does the version bump, `v*` tag, and gem push itself:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
gh workflow run release.yml --ref main -f next_version=X.Y.Z
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The version number is the maintainer's decision — ask, never infer it from semver reasoning. PRs are rebase-merged.
|
|
32
|
+
|
|
33
|
+
## What this gem does
|
|
34
|
+
|
|
35
|
+
Converts ISO comment sheets to structured YAML and back, and syncs comments to GitHub issues:
|
|
36
|
+
|
|
37
|
+
- **Import**: DOCX (ISO 2012-03 balloting template) or XLSX (ISO Online Standards Development exports) → YAML + schema file. Format auto-detected from extension. Redline DOCX with tracked changes is dispatched to `Parser::TrackChangeDocxParser` via `--format redline`.
|
|
38
|
+
- **Fill**: YAML → filled DOCX comment sheet (`data/iso_comment_template_2012-03.docx`), optional status-based cell shading.
|
|
39
|
+
- **GitHub round-trip**: `github-create` makes issues from YAML via Liquid templates; `github-retrieve` pulls `> **OBSERVATION:**` blockquotes from closed issues back into the YAML's observations field.
|
|
40
|
+
|
|
41
|
+
Plain text only — formulas/images/complex formatting are unsupported (docx gem limitation).
|
|
42
|
+
|
|
43
|
+
## Architecture
|
|
44
|
+
|
|
45
|
+
Core flow: `Parser` → `CommentSheet` (metadata + `Comment` objects) → YAML / DOCX / GitHub issues.
|
|
46
|
+
|
|
47
|
+
- `Commenter::Comment` / `Commenter::CommentSheet` — data model. `Comment` carries the common fields plus optional OSD-specific fields (`user_name`, `resolution_status`, `motivation`, etc.) and a `github` sub-hash tracking issue state. Comment types: short codes (`ge`/`te`/`ed`) are expanded to full names (`general`/`technical`/`editorial`) when a `Comment` is loaded; YAML output stores the expanded form. The sheet's `version` field (`"2012-03"` vs `"osd"`) selects the output schema.
|
|
48
|
+
- `Commenter::Parser` — dispatches by format: `.docx` parsed inline with the `docx` gem; `.xlsx` delegated to `Parser::OsdXlsxParser` (uses `roo`); `redline` delegated to `Parser::TrackChangeDocxParser`.
|
|
49
|
+
- `Parser::OsdXlsxParser` — auto-detects two OSD export variants by header row ("resolved" 17-col starting `Comment ID`; "unresolved" 15-col starting `User name`), extracts metadata (date/reference/stage/titles) from header rows 1–2, maps columns by header name into `Comment` attributes, and synthesizes `observations` from `resolution_status` + `motivation`.
|
|
50
|
+
- `Parser::TrackChangeDocxParser` — redline DOCX import: streams `word/document.xml` with Nokogiri XML::Reader (redlines can exceed 100 MB), captures `w:ins`/`w:del`/`w:moveFrom`/`w:moveTo` as comment entries (`proposed_change` renders the change itself, e.g. `Insert: "text"`; clause resolved from the nearest preceding heading including sub-clauses; element resolved from Table/Figure/Formula/NOTE references in the same clause; self-closing paragraph-mark markers skipped), plus reviewer `w:comment` threads as `-CNNN` remark entries (remark verbatim in `comments`, instruction reworded in `proposed_change`, empty `observations` for the owner). Stamps `observations` from the `observations`/`accept_all` options.
|
|
51
|
+
- `Commenter::Filler` — writes comments into the DOCX template table; maps observation status text (`accept(ed)?`, `noted`, `reject(ed)?`, …) to shading colors.
|
|
52
|
+
- GitHub integration (`lib/commenter/github_integration.rb`) — two classes with parallel structure: `GitHubIssueCreator` (`github-create`) and `GitHubIssueRetriever` (`github-retrieve`). Each loads its own config YAML and Octokit client, and rewrites the comments YAML in place after the run. Duplicate detection searches issue titles for a unique ID rendered from a configurable Liquid `unique_id` template (stage-aware by default: `[DIS] GB-001`), so the same comment ID at different ballot stages creates separate issues.
|
|
53
|
+
- `Commenter::Cli` (`lib/commenter/cli.rb`) — Thor CLI with subcommands `import`, `fill`, `github-create`, `github-retrieve`.
|
|
54
|
+
|
|
55
|
+
### Schemas
|
|
56
|
+
|
|
57
|
+
`schema/iso_comment_2012-03.yaml` and `schema/iso_comment_osd.yaml` are JSON-Schema-style files for IDE validation. `import` copies the matching schema (based on sheet version) into the schema dir and stamps the YAML header with a `yaml-language-server` reference. When the data model changes, update the corresponding schema file.
|
|
58
|
+
|
|
59
|
+
### Templates
|
|
60
|
+
|
|
61
|
+
`data/github_issue_title_template.liquid` and `data/github_issue_body_template.liquid` render issue titles/bodies; both are user-overridable via config. See README.adoc for the full variable list.
|
|
62
|
+
|
|
63
|
+
## Conventions
|
|
64
|
+
|
|
65
|
+
- Ruby style: double-quoted strings (enforced by RuboCop).
|
|
66
|
+
- `data/` files (DOCX template, Liquid templates, sample config) are shipped with the gem and loaded relative to `__dir__` — do not treat them as disposable.
|
|
67
|
+
- Testing GitHub integration: use `--dry-run` with `GITHUB_TOKEN=dummy_token` — exercises template rendering without API calls.
|
|
68
|
+
- `sig/commenter.rbs` is a minimal stub; RBS coverage is not currently maintained.
|
data/README.adoc
CHANGED
|
@@ -7,14 +7,20 @@ image:https://img.shields.io/github/commits-since/metanorma/commenter/latest.svg
|
|
|
7
7
|
|
|
8
8
|
== Purpose
|
|
9
9
|
|
|
10
|
-
Commenter is a Ruby gem for working with ISO comment sheets in DOCX
|
|
10
|
+
Commenter is a Ruby gem for working with ISO comment sheets in DOCX and XLSX
|
|
11
|
+
format.
|
|
11
12
|
|
|
12
13
|
It provides utilities for parsing, manipulating, and serializing ISO comment
|
|
13
|
-
data, converting between DOCX and structured YAML with schema validation.
|
|
14
|
+
data, converting between DOCX/XLSX and structured YAML with schema validation.
|
|
14
15
|
|
|
15
|
-
The
|
|
16
|
+
The supported input formats are:
|
|
16
17
|
|
|
17
18
|
* "ISO/IEC/CEN/CENELEC electronic balloting commenting template/version 2012-03"
|
|
19
|
+
(DOCX)
|
|
20
|
+
* ISO Online Standards Development (OSD) XLSX exports (resolved and unresolved
|
|
21
|
+
comment sheets)
|
|
22
|
+
* Redlined Word documents containing tracked changes (e.g. ISO/CS editor
|
|
23
|
+
redlines), imported with `--format redline`
|
|
18
24
|
|
|
19
25
|
This gem only supports plain text comment extraction and filling. Only use this
|
|
20
26
|
to handle plain text comments and resolutions.
|
|
@@ -49,19 +55,49 @@ $ gem install commenter
|
|
|
49
55
|
|
|
50
56
|
== Usage
|
|
51
57
|
|
|
52
|
-
=== Importing comments
|
|
58
|
+
=== Importing comments
|
|
53
59
|
|
|
54
|
-
Convert an ISO comment sheet DOCX
|
|
60
|
+
Convert an ISO comment sheet (DOCX or XLSX) to structured YAML.
|
|
61
|
+
The format is auto-detected from the file extension; redline DOCX must be
|
|
62
|
+
selected explicitly with `--format redline` because the extension cannot
|
|
63
|
+
distinguish a redline from a comment sheet.
|
|
55
64
|
|
|
56
65
|
[source,shell]
|
|
57
66
|
----
|
|
67
|
+
# From DOCX (ISO 2012-03 template or OSD DOCX export)
|
|
58
68
|
$ commenter import "ISO 80000-2 review comments.docx" -o comments.yaml
|
|
69
|
+
|
|
70
|
+
# From XLSX (ISO OSD resolved comment export)
|
|
71
|
+
$ commenter import "91855-Comments-resolved.xlsx" -o comments.yaml
|
|
72
|
+
|
|
73
|
+
# From XLSX (ISO OSD comments-only export)
|
|
74
|
+
$ commenter import "ISO 5843-6-Comments.xlsx" -o comments.yaml
|
|
75
|
+
|
|
76
|
+
# From a redlined DOCX with tracked changes (ISO/CS editor redline)
|
|
77
|
+
$ commenter import "ISO_DIS 2533 Enquiry Trackchange Word.docx" --format redline \
|
|
78
|
+
--body CS --document "ISO 2533:2026" --stage DIS \
|
|
79
|
+
--observations "Accepted. ISO/CS tracked change accepted." \
|
|
80
|
+
-o comments-cs.yaml
|
|
59
81
|
----
|
|
60
82
|
|
|
83
|
+
Each tracked change (`w:ins`, `w:del`, `w:moveFrom`, `w:moveTo`) becomes a
|
|
84
|
+
comment entry whose `proposed_change` renders the change itself
|
|
85
|
+
(`Insert: "..."` / `Delete: "..."` / `Move ...: "..."`) and whose
|
|
86
|
+
`locality.clause` is resolved from the nearest preceding heading (including
|
|
87
|
+
sub-clauses such as `4.2.1` and `Annex A`), with `locality.element` resolved
|
|
88
|
+
from the nearest `Table`/`Figure`/`Formula`/`NOTE` reference in the same
|
|
89
|
+
clause. Self-closing paragraph-mark markers are skipped because they carry
|
|
90
|
+
no content.
|
|
91
|
+
|
|
92
|
+
Reviewer comment threads (`w:comment`) are emitted after the track changes
|
|
93
|
+
with `-CNNN` ids: the remark verbatim in `comments`, its instruction
|
|
94
|
+
reworded as the `proposed_change` (a leading "Please " is dropped), and
|
|
95
|
+
empty `observations` for the document owner to draft.
|
|
96
|
+
|
|
61
97
|
This will create two files:
|
|
62
98
|
|
|
63
99
|
`comments.yaml`:: The structured comment data
|
|
64
|
-
`schema/iso_comment_2012-03.yaml`:: The YAML schema for validation
|
|
100
|
+
`schema/iso_comment_2012-03.yaml` or `schema/iso_comment_osd.yaml`:: The YAML schema for validation (selected automatically based on input format)
|
|
65
101
|
|
|
66
102
|
==== Import options
|
|
67
103
|
|
|
@@ -75,6 +111,15 @@ Options:
|
|
|
75
111
|
`-o, --output FILE`:: Output YAML file (default: comments.yaml)
|
|
76
112
|
`-e, --exclude-observations`:: Skip the observations column
|
|
77
113
|
`--schema-dir DIR`:: Directory for schema file (default: schema)
|
|
114
|
+
`--format FORMAT`:: Force input format (`docx`, `xlsx` or `redline`; auto-detected by default)
|
|
115
|
+
`--sheet NAME`:: XLSX: sheet name to parse (default: first sheet)
|
|
116
|
+
`--resolved-only`:: XLSX: use resolved comments sheet only
|
|
117
|
+
`--unresolved-only`:: XLSX: use unresolved comments sheet only
|
|
118
|
+
`--body CODE`:: Redline: member body code for comment IDs (default: `CS`)
|
|
119
|
+
`--observations TEXT`:: Redline: observations stamped on all track changes
|
|
120
|
+
`--accept-all`:: Redline: stamp `Accepted. Tracked change accepted.` on all track changes (an explicit `--observations` wins)
|
|
121
|
+
`--document ID`:: Redline: document identifier (e.g. `ISO 2533:2026`)
|
|
122
|
+
`--stage STAGE`:: Redline: approval stage (e.g. `DIS`)
|
|
78
123
|
|
|
79
124
|
==== Metadata (extraction limitation)
|
|
80
125
|
|
|
@@ -118,7 +163,7 @@ comments:
|
|
|
118
163
|
line_number:
|
|
119
164
|
clause: "_whole document"
|
|
120
165
|
element:
|
|
121
|
-
type:
|
|
166
|
+
type: general
|
|
122
167
|
comments: |
|
|
123
168
|
The document should include more examples
|
|
124
169
|
to clarify the implementation requirements.
|
|
@@ -134,7 +179,7 @@ comments:
|
|
|
134
179
|
line_number: "45"
|
|
135
180
|
clause: "5.2.1"
|
|
136
181
|
element: "Table 3"
|
|
137
|
-
type:
|
|
182
|
+
type: technical
|
|
138
183
|
comments: |
|
|
139
184
|
The values in Table 3 appear to be
|
|
140
185
|
inconsistent with the formula in 5.1.
|
|
@@ -455,7 +500,7 @@ comments:
|
|
|
455
500
|
locality:
|
|
456
501
|
clause: "5.2.1"
|
|
457
502
|
element: "Table 3"
|
|
458
|
-
type:
|
|
503
|
+
type: technical
|
|
459
504
|
comments: "The values in Table 3 appear inconsistent..."
|
|
460
505
|
proposed_change: "Correct the values in column 2..."
|
|
461
506
|
observations: ""
|
|
@@ -530,12 +575,17 @@ The comment types are defined as follows:
|
|
|
530
575
|
`te`:: Technical comment
|
|
531
576
|
`ed`:: Editorial comment
|
|
532
577
|
|
|
578
|
+
NOTE: In generated YAML, types are stored in expanded form (`general`,
|
|
579
|
+
`technical`, `editorial`). Short codes are accepted as input anywhere and are
|
|
580
|
+
expanded when the comment is loaded.
|
|
581
|
+
|
|
533
582
|
=== Workflow integration
|
|
534
583
|
|
|
535
584
|
[source,mermaid]
|
|
536
585
|
----
|
|
537
586
|
flowchart LR
|
|
538
|
-
|
|
587
|
+
A1[ISO Comment Sheet DOCX] --> B[commenter import]
|
|
588
|
+
A2[ISO OSD XLSX export] --> B
|
|
539
589
|
B --> C[YAML + Schema]
|
|
540
590
|
C --> D[commenter github-create]
|
|
541
591
|
D --> E[YAML + GitHub Info]
|
|
@@ -566,6 +616,8 @@ recognized and applied to the observations column:
|
|
|
566
616
|
|
|
567
617
|
== Data model
|
|
568
618
|
|
|
619
|
+
=== ISO 2012-03 template (DOCX)
|
|
620
|
+
|
|
569
621
|
The comment structure follows this schema:
|
|
570
622
|
|
|
571
623
|
[source,yaml]
|
|
@@ -593,14 +645,61 @@ comments: # Array of comment objects
|
|
|
593
645
|
updated_at: string # ISO 8601 timestamp (optional)
|
|
594
646
|
----
|
|
595
647
|
|
|
648
|
+
=== ISO OSD format (XLSX)
|
|
649
|
+
|
|
650
|
+
The OSD format includes additional metadata and resolution fields extracted from
|
|
651
|
+
the ISO Online Standards Development platform:
|
|
652
|
+
|
|
653
|
+
[source,yaml]
|
|
654
|
+
----
|
|
655
|
+
version: "osd" # OSD format identifier
|
|
656
|
+
date: string # Date from XLSX header (auto-extracted)
|
|
657
|
+
document: string # Document reference, e.g. "ISO/DIS 5843-6(en)"
|
|
658
|
+
project: string # Project name (auto-extracted)
|
|
659
|
+
stage: string # Stage: WD, CD, DIS, FDIS, PRF, PUB (auto-extracted)
|
|
660
|
+
title_en: string # Document title in English (auto-extracted)
|
|
661
|
+
title_fr: string # Document title in French (auto-extracted)
|
|
662
|
+
comments:
|
|
663
|
+
- id: string # Comment ID (numeric from OSD)
|
|
664
|
+
body: string # User name / member body
|
|
665
|
+
locality:
|
|
666
|
+
clause: string # Clause number
|
|
667
|
+
element: string # Clause title
|
|
668
|
+
type: "ge" | "te" | "ed"
|
|
669
|
+
comments: string # Comment text
|
|
670
|
+
proposed_change: string
|
|
671
|
+
observations: string # Built from resolution_status + motivation
|
|
672
|
+
user_name: string # OSD user name
|
|
673
|
+
comment_type: string # Subtype: Editorial, General, Technical
|
|
674
|
+
resolution_status: string # Accepted, Partially accepted, Rejected, etc.
|
|
675
|
+
resolution_date: string # Date resolved
|
|
676
|
+
feedbacks: string # Discussion replies
|
|
677
|
+
motivation: string # Resolution justification
|
|
678
|
+
created_date: string # Date comment was created
|
|
679
|
+
stage_code: string # ISO stage code (e.g. "40.20")
|
|
680
|
+
----
|
|
681
|
+
|
|
682
|
+
The OSD XLSX format comes in two variants, both auto-detected:
|
|
683
|
+
|
|
684
|
+
*Resolved*: 17 columns starting with `Comment ID`, includes resolution data
|
|
685
|
+
(`Resolution status`, `Motivation`, `Resolution Date`, `Stage code`)
|
|
686
|
+
|
|
687
|
+
*Unresolved/comments-only*: 15 columns starting with `User name`, includes
|
|
688
|
+
`Comment type`, `Comment/Motivation`, `Replies`, `Comment number`
|
|
689
|
+
|
|
596
690
|
|
|
597
691
|
== Schema validation
|
|
598
692
|
|
|
599
|
-
Each exported YAML file includes a schema reference for IDE support
|
|
693
|
+
Each exported YAML file includes a schema reference for IDE support.
|
|
694
|
+
The schema is selected automatically based on the input format:
|
|
600
695
|
|
|
601
696
|
[source,yaml]
|
|
602
697
|
----
|
|
698
|
+
# For ISO 2012-03 DOCX imports:
|
|
603
699
|
# yaml-language-server: $schema=schema/iso_comment_2012-03.yaml
|
|
700
|
+
|
|
701
|
+
# For ISO OSD XLSX imports:
|
|
702
|
+
# yaml-language-server: $schema=schema/iso_comment_osd.yaml
|
|
604
703
|
----
|
|
605
704
|
|
|
606
705
|
This enables:
|
|
@@ -695,7 +794,8 @@ The gem is organized into several key components:
|
|
|
695
794
|
|
|
696
795
|
`Commenter::Comment`:: Represents individual comments with locality, type, and content
|
|
697
796
|
`Commenter::CommentSheet`:: Container for multiple comments with metadata
|
|
698
|
-
`Commenter::Parser`:: Handles DOCX parsing
|
|
797
|
+
`Commenter::Parser`:: Handles DOCX and XLSX parsing with auto-detection
|
|
798
|
+
`Commenter::Parser::OsdXlsxParser`:: Parses ISO OSD XLSX exports (resolved and unresolved variants)
|
|
699
799
|
`Commenter::Filler`:: Fills DOCX templates with comment data
|
|
700
800
|
`Commenter::GitHubIssueCreator`:: Creates GitHub issues from comments
|
|
701
801
|
|
|
@@ -703,7 +803,7 @@ The gem is organized into several key components:
|
|
|
703
803
|
|
|
704
804
|
`Commenter::Cli`:: Thor-based command-line interface with subcommands:
|
|
705
805
|
|
|
706
|
-
** `import` - Convert DOCX to YAML
|
|
806
|
+
** `import` - Convert DOCX or XLSX to YAML
|
|
707
807
|
** `fill` - Fill DOCX template from YAML
|
|
708
808
|
** `github-create` - Create GitHub issues from comments
|
|
709
809
|
** `github-retrieve` - Retrieve observations from GitHub issues
|
|
@@ -714,7 +814,8 @@ The gem is organized into several key components:
|
|
|
714
814
|
* `data/github_issue_title_template.liquid` - GitHub issue title template
|
|
715
815
|
* `data/github_issue_body_template.liquid` - GitHub issue body template
|
|
716
816
|
* `data/github_config_sample.yaml` - Sample GitHub configuration
|
|
717
|
-
* `schema/iso_comment_2012-03.yaml` - YAML schema for validation
|
|
817
|
+
* `schema/iso_comment_2012-03.yaml` - YAML schema for ISO 2012-03 validation
|
|
818
|
+
* `schema/iso_comment_osd.yaml` - YAML schema for ISO OSD format validation
|
|
718
819
|
|
|
719
820
|
=== Debugging
|
|
720
821
|
|
data/commenter.gemspec
CHANGED
|
@@ -12,8 +12,8 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.authors = ["Ribose"]
|
|
13
13
|
spec.email = ["open.source@ribose.com"]
|
|
14
14
|
|
|
15
|
-
spec.summary = "Library to work with ISO comment sheets in DOCX
|
|
16
|
-
spec.description = "Convert between ISO comment
|
|
15
|
+
spec.summary = "Library to work with ISO comment sheets in DOCX and XLSX formats."
|
|
16
|
+
spec.description = "Convert between ISO comment sheets (DOCX and XLSX) and structured YAML with schema validation."
|
|
17
17
|
spec.homepage = "https://github.com/metanorma/commenter"
|
|
18
18
|
spec.license = "BSD-2-Clause"
|
|
19
19
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
|
|
@@ -35,5 +35,6 @@ Gem::Specification.new do |spec|
|
|
|
35
35
|
spec.add_dependency "dotenv", "~> 2.8"
|
|
36
36
|
spec.add_dependency "liquid", "~> 5.0"
|
|
37
37
|
spec.add_dependency "octokit", "~> 6.0"
|
|
38
|
+
spec.add_dependency "roo", ">= 2.10"
|
|
38
39
|
spec.add_dependency "thor", "~> 1.0"
|
|
39
40
|
end
|
data/lib/commenter/cli.rb
CHANGED
|
@@ -3,43 +3,49 @@
|
|
|
3
3
|
require "thor"
|
|
4
4
|
require "yaml"
|
|
5
5
|
require "fileutils"
|
|
6
|
-
require "commenter
|
|
7
|
-
require "commenter/filler"
|
|
8
|
-
require "commenter/github_integration"
|
|
6
|
+
require "commenter"
|
|
9
7
|
|
|
10
8
|
module Commenter
|
|
11
9
|
class Cli < Thor
|
|
12
|
-
desc "import INPUT
|
|
10
|
+
desc "import INPUT", "Convert comment sheet (DOCX or XLSX) or redline DOCX to YAML"
|
|
13
11
|
option :output, type: :string, aliases: :o, default: "comments.yaml", desc: "Output YAML file"
|
|
14
12
|
option :exclude_observations, type: :boolean, aliases: :e, desc: "Exclude observations column"
|
|
15
13
|
option :schema_dir, type: :string, default: "schema", desc: "Directory for schema file"
|
|
16
|
-
|
|
14
|
+
option :format, type: :string, desc: "Force input format (docx, xlsx or redline)"
|
|
15
|
+
option :sheet, type: :string, desc: "XLSX sheet name to parse (default: first sheet)"
|
|
16
|
+
option :resolved_only, type: :boolean, desc: "XLSX: use resolved comments sheet only"
|
|
17
|
+
option :unresolved_only, type: :boolean, desc: "XLSX: use unresolved comments sheet only"
|
|
18
|
+
option :body, type: :string, desc: "Redline: member body code for comment IDs (default: CS)"
|
|
19
|
+
option :observations, type: :string, desc: "Redline: observations stamped on all track changes"
|
|
20
|
+
option :accept_all, type: :boolean, desc: "Redline: stamp 'Accepted. Tracked change accepted.' on all track changes"
|
|
21
|
+
option :document, type: :string, desc: "Redline: document identifier (e.g. ISO 2533:2026)"
|
|
22
|
+
option :stage, type: :string, desc: "Redline: approval stage (e.g. DIS)"
|
|
23
|
+
def import(input_file)
|
|
17
24
|
output_yaml = options[:output]
|
|
18
25
|
schema_dir = options[:schema_dir]
|
|
19
26
|
|
|
20
27
|
# Ensure schema directory exists
|
|
21
28
|
FileUtils.mkdir_p(schema_dir) unless Dir.exist?(schema_dir)
|
|
22
29
|
|
|
23
|
-
# Parse the
|
|
30
|
+
# Parse the input file
|
|
24
31
|
parser = Parser.new
|
|
25
|
-
comment_sheet = parser.parse(
|
|
32
|
+
comment_sheet = parser.parse(input_file, options)
|
|
26
33
|
|
|
27
34
|
# Write the YAML data file with schema reference
|
|
28
|
-
|
|
29
|
-
File.write(output_yaml, yaml_content)
|
|
35
|
+
File.write(output_yaml, comment_sheet.to_yaml_document(schema_dir))
|
|
30
36
|
|
|
31
37
|
# Copy schema file to output directory
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
schema_name = comment_sheet.schema_name
|
|
39
|
+
schema_source = File.join(__dir__, "../../schema/#{schema_name}")
|
|
40
|
+
schema_target = File.join(schema_dir, schema_name)
|
|
34
41
|
|
|
35
42
|
# Only copy if source and target are different
|
|
36
|
-
unless File.expand_path(schema_source) == File.expand_path(schema_target)
|
|
37
|
-
FileUtils.cp(schema_source,
|
|
38
|
-
schema_target)
|
|
39
|
-
end
|
|
43
|
+
FileUtils.cp(schema_source, schema_target) unless File.expand_path(schema_source) == File.expand_path(schema_target)
|
|
40
44
|
|
|
41
|
-
puts "Converted #{
|
|
42
|
-
puts "
|
|
45
|
+
puts "Converted #{input_file} to #{output_yaml}"
|
|
46
|
+
puts " Version: #{comment_sheet.version}"
|
|
47
|
+
puts " Comments: #{comment_sheet.comments.length}"
|
|
48
|
+
puts " Schema: #{schema_target}"
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
desc "fill INPUT.yaml", "Fill DOCX template from YAML comments"
|
|
@@ -206,13 +212,5 @@ module Commenter
|
|
|
206
212
|
def self.exit_on_failure?
|
|
207
213
|
true
|
|
208
214
|
end
|
|
209
|
-
|
|
210
|
-
private
|
|
211
|
-
|
|
212
|
-
def generate_yaml_with_header(data, schema_dir)
|
|
213
|
-
schema_path = File.join(schema_dir, "iso_comment_2012-03.yaml")
|
|
214
|
-
header = "# yaml-language-server: $schema=#{schema_path}\n\n"
|
|
215
|
-
header + data.to_yaml
|
|
216
|
-
end
|
|
217
215
|
end
|
|
218
216
|
end
|
data/lib/commenter/comment.rb
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Commenter
|
|
4
4
|
class Comment
|
|
5
|
-
attr_accessor :id, :body, :locality, :type, :comments, :proposed_change, :observations, :github
|
|
5
|
+
attr_accessor :id, :body, :locality, :type, :comments, :proposed_change, :observations, :github,
|
|
6
|
+
:user_name, :comment_type, :resolution_status, :resolution_date, :feedbacks,
|
|
7
|
+
:motivation, :created_date, :stage_code
|
|
6
8
|
|
|
7
9
|
def initialize(attributes = {})
|
|
8
10
|
# Normalize input to symbols
|
|
@@ -11,20 +13,25 @@ module Commenter
|
|
|
11
13
|
@id = attrs[:id]
|
|
12
14
|
@body = attrs[:body]
|
|
13
15
|
@locality = symbolize_keys(attrs[:locality] || {})
|
|
14
|
-
@type =
|
|
16
|
+
@type = CommentType.full_name(attrs[:type])
|
|
15
17
|
@comments = attrs[:comments]
|
|
16
18
|
@proposed_change = attrs[:proposed_change]
|
|
17
19
|
@observations = attrs[:observations]
|
|
18
20
|
@github = symbolize_keys(attrs[:github] || {})
|
|
21
|
+
|
|
22
|
+
# OSD-specific fields
|
|
23
|
+
@user_name = attrs[:user_name]
|
|
24
|
+
@comment_type = attrs[:comment_type]
|
|
25
|
+
@resolution_status = attrs[:resolution_status]
|
|
26
|
+
@resolution_date = attrs[:resolution_date]
|
|
27
|
+
@feedbacks = attrs[:feedbacks]
|
|
28
|
+
@motivation = attrs[:motivation]
|
|
29
|
+
@created_date = attrs[:created_date]
|
|
30
|
+
@stage_code = attrs[:stage_code]
|
|
19
31
|
end
|
|
20
32
|
|
|
21
33
|
def expand_comment_type(type)
|
|
22
|
-
|
|
23
|
-
when "ge" then "general"
|
|
24
|
-
when "te" then "technical"
|
|
25
|
-
when "ed" then "editorial"
|
|
26
|
-
else type
|
|
27
|
-
end
|
|
34
|
+
CommentType.full_name(type)
|
|
28
35
|
end
|
|
29
36
|
|
|
30
37
|
def line_number
|
|
@@ -117,6 +124,14 @@ module Commenter
|
|
|
117
124
|
comments: @comments,
|
|
118
125
|
proposed_change: @proposed_change,
|
|
119
126
|
observations: @observations,
|
|
127
|
+
user_name: @user_name,
|
|
128
|
+
comment_type: @comment_type,
|
|
129
|
+
resolution_status: @resolution_status,
|
|
130
|
+
resolution_date: @resolution_date,
|
|
131
|
+
feedbacks: @feedbacks,
|
|
132
|
+
motivation: @motivation,
|
|
133
|
+
created_date: @created_date,
|
|
134
|
+
stage_code: @stage_code,
|
|
120
135
|
github: @github.empty? ? nil : @github
|
|
121
136
|
}.compact
|
|
122
137
|
end
|
|
@@ -4,7 +4,7 @@ require_relative "comment"
|
|
|
4
4
|
|
|
5
5
|
module Commenter
|
|
6
6
|
class CommentSheet
|
|
7
|
-
attr_accessor :version, :date, :document, :project, :stage, :comments
|
|
7
|
+
attr_accessor :version, :date, :document, :project, :stage, :comments, :title_en, :title_fr
|
|
8
8
|
|
|
9
9
|
def initialize(attributes = {})
|
|
10
10
|
# Normalize input to symbols
|
|
@@ -15,6 +15,8 @@ module Commenter
|
|
|
15
15
|
@document = attrs[:document]
|
|
16
16
|
@project = attrs[:project]
|
|
17
17
|
@stage = attrs[:stage]
|
|
18
|
+
@title_en = attrs[:title_en]
|
|
19
|
+
@title_fr = attrs[:title_fr]
|
|
18
20
|
@comments = (attrs[:comments] || []).map { |c| c.is_a?(Comment) ? c : Comment.from_hash(c) }
|
|
19
21
|
end
|
|
20
22
|
|
|
@@ -29,12 +31,26 @@ module Commenter
|
|
|
29
31
|
document: @document,
|
|
30
32
|
project: @project,
|
|
31
33
|
stage: @stage,
|
|
34
|
+
title_en: @title_en,
|
|
35
|
+
title_fr: @title_fr,
|
|
32
36
|
comments: @comments.map(&:to_h)
|
|
33
37
|
}
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def to_yaml_h
|
|
37
|
-
|
|
41
|
+
hash = to_h.merge(comments: @comments.map(&:to_yaml_h))
|
|
42
|
+
# Remove nil-valued keys for cleaner YAML output
|
|
43
|
+
hash.delete(:title_en) if hash[:title_en].nil?
|
|
44
|
+
hash.delete(:title_fr) if hash[:title_fr].nil?
|
|
45
|
+
stringify_keys(hash)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def schema_name
|
|
49
|
+
version == "osd" ? "iso_comment_osd.yaml" : "iso_comment_2012-03.yaml"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_yaml_document(schema_dir = "schema")
|
|
53
|
+
"# yaml-language-server: $schema=#{File.join(schema_dir.to_s, schema_name)}\n\n#{to_yaml_h.to_yaml}"
|
|
38
54
|
end
|
|
39
55
|
|
|
40
56
|
def self.from_hash(hash)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Commenter
|
|
4
|
+
# Single home for the comment type vocabulary: short codes (ge/te/ed) and
|
|
5
|
+
# their expanded names (general/technical/editorial). Unrecognized values
|
|
6
|
+
# pass through unchanged; display_name capitalizes recognized types.
|
|
7
|
+
module CommentType
|
|
8
|
+
FULL_NAMES = {
|
|
9
|
+
"ge" => "general",
|
|
10
|
+
"te" => "technical",
|
|
11
|
+
"ed" => "editorial"
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
CODES = FULL_NAMES.invert.freeze
|
|
15
|
+
|
|
16
|
+
DISPLAY_NAMES = FULL_NAMES.each_with_object({}) do |(code, full), display|
|
|
17
|
+
display[code] = full.capitalize
|
|
18
|
+
display[full] = full.capitalize
|
|
19
|
+
end.freeze
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
def code(type)
|
|
24
|
+
CODES.fetch(type.to_s.strip.downcase) { type&.to_s&.strip }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def full_name(type)
|
|
28
|
+
FULL_NAMES.fetch(type.to_s.strip.downcase) { type&.to_s&.strip }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def display_name(type)
|
|
32
|
+
DISPLAY_NAMES.fetch(type.to_s.strip.downcase) { type || "Unknown" }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|