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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31fa0d4ee056c833d9f2b329d994e5d6151e3430426e6d4f02dfca4c670d0c18
4
- data.tar.gz: b459baef384850cfa84cfb22c48212771ee57fa0e11cbd803c1db33dfb44aae2
3
+ metadata.gz: 1609bb9eed80e408daebc9c67cda2c657e8484186acce89b823e6b411a5f9ea5
4
+ data.tar.gz: 46316674e2c639b9256390385d2cb3c59ed8946b604b44fbb0be28728475cd38
5
5
  SHA512:
6
- metadata.gz: 989ff9a26a63210438385658edaee8a0d94a35c9d80916dfefe89575ce739f9efc75bdcc372d737a955de236fdefdeaaca0be65146363dea5833ac280cc97cdb
7
- data.tar.gz: 5cdcb2b0ca204b6e419d009f45eeb6dfd4867c7eb51196a04ad2499c7c64e5cb5288b85522f031a7a823e9242c3e8edec771d7c2d1bb8f5f9b079df7e86cc54b
6
+ metadata.gz: 887aa19ab8ca3559fd20caf3e19a9941b631c677faeda86553597671883fc8dbae8c1e1f6d83bd0014e036774743addc16bc538b7404f6939be6636e1d623704
7
+ data.tar.gz: 3b850d0872f2f572b599d0bf7a70a977e1e5e7e797f5fb3447905c09cc7578cff18d99de6b32ae3b539758b7319b07590fdfd9d5249f74024ffdcd842d48d09d
data/.rubocop.yml CHANGED
@@ -8,3 +8,11 @@ Style/StringLiterals:
8
8
 
9
9
  Style/StringLiteralsInInterpolation:
10
10
  EnforcedStyle: double_quotes
11
+
12
+ Metrics/ModuleLength:
13
+ Exclude:
14
+ - "spec/**/*"
15
+
16
+ Metrics/BlockLength:
17
+ Exclude:
18
+ - "spec/**/*"
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 format.
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 format is taken from:
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 from DOCX
58
+ === Importing comments
53
59
 
54
- Convert an ISO comment sheet DOCX file to structured YAML:
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: ge
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: te
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: te
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
- A[ISO Comment Sheet DOCX] --> B[commenter import]
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 and YAML generation
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 format."
16
- spec.description = "Convert between ISO comment sheet DOCX and structured YAML with schema validation."
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/parser"
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.docx", "Convert DOCX comment sheet to YAML"
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
- def import(input_docx)
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 DOCX file
30
+ # Parse the input file
24
31
  parser = Parser.new
25
- comment_sheet = parser.parse(input_docx, options)
32
+ comment_sheet = parser.parse(input_file, options)
26
33
 
27
34
  # Write the YAML data file with schema reference
28
- yaml_content = generate_yaml_with_header(comment_sheet.to_yaml_h, schema_dir)
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
- schema_source = File.join(__dir__, "../../schema/iso_comment_2012-03.yaml")
33
- schema_target = File.join(schema_dir, "iso_comment_2012-03.yaml")
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 #{input_docx} to #{output_yaml}"
42
- puts "Schema file created at #{schema_target}"
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
@@ -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 = expand_comment_type(attrs[: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
- case type&.downcase
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
- stringify_keys(to_h.merge(comments: @comments.map(&:to_yaml_h)))
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