simple_inline_text_annotation 0.1.0 → 1.1.0

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: 725fe23d9066db1aa1846efc6699a8973991cef8a613c8d89b82c5794202fd60
4
- data.tar.gz: c2a8671106444d868a1100bd9c07d4ce88c0693d5fb458e8589cbc8a46e1c4af
3
+ metadata.gz: 54b2eb70ade1f40cc7c1d576c6992dc8dbba0452e95a879e51d090229bcf5011
4
+ data.tar.gz: c2730f877c0e6e07b06d6f011160ad29e45fce0ce9c7e6c8a4e2b0e004b5499b
5
5
  SHA512:
6
- metadata.gz: 0c2a831a71afa277bc4703b0c3c7f2f5580750003fb9dd9b7997af76b0761e19fc31f05b643578c7298f281930e6253af5bc1c87c911932bd673cd003591d2b9
7
- data.tar.gz: db3c7bf4f78a34e314a173a7eece62aa43222c7f103197e2519dfe27dca4da067afcfc4541756f3655289e4fa64764d117a4e3eaff615b58e6931766bd24842c
6
+ metadata.gz: 0ef5000debc433f0013102d5dbec539624f437028a5cdbe3e1cfa3351c7de59c2a0a5d284dfbffdf197c9db0f05a131d42daff7301fbf78ae4ffab9488add300
7
+ data.tar.gz: 4008f849ba4493b9ecf85596a755d7d952c609a64e7a92a4a9d38222494778a675ddf35f32e0330bce3a29765e1147628686e74314f64a3012d2c4283bbd2967
data/README.md CHANGED
@@ -1,43 +1,162 @@
1
- # SimpleInlineTextAnnotation
1
+ # SimpleInlineTextAnnotation (Ruby gem)
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
4
-
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/simple_inline_text_annotation`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ `SimpleInlineTextAnnotation` is a Ruby gem designed for working with inline text annotations. It allows you to parse and generate annotated text in a structured and efficient way.
6
4
 
7
5
  ## Installation
8
6
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
10
-
11
- Install the gem and add to the application's Gemfile by executing:
7
+ To use this gem in a Rails application, add the following line to your application's `Gemfile`:
12
8
 
13
- ```bash
14
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
9
+ ```ruby
10
+ gem 'simple_inline_text_annotation'
15
11
  ```
16
12
 
17
- If bundler is not being used to manage dependencies, install the gem by executing:
13
+ Then, run the following command to install the gem:
18
14
 
19
15
  ```bash
20
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
16
+ bundle install
21
17
  ```
22
18
 
23
19
  ## Usage
24
20
 
25
- TODO: Write usage instructions here
21
+ The `SimpleInlineTextAnnotation` gem provides two main methods: `parse` and `generate`. These methods allow you to work with inline text annotations in a structured way.
22
+
23
+ ### `parse` Method
26
24
 
27
- ## Development
25
+ The `parse` method takes a string with inline annotations and extracts structured information about the annotations, including the character positions and annotation types.
26
+
27
+ #### Example
28
+
29
+ ```ruby
30
+ result = SimpleInlineTextAnnotation.parse('[Elon Musk][Person] is a member of the [PayPal Mafia][Organization].')
31
+ puts result
32
+ # => {
33
+ # text: "Elon Musk is a member of the PayPal Mafia.",
34
+ # denotations: [
35
+ # {span: {begin: 0, end: 9}, obj: "Person"},
36
+ # {span: {begin: 29, end: 41}, obj: "Organization"}
37
+ # ]
38
+ # }
39
+ ```
28
40
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
41
+ #### Explanation
30
42
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
43
+ - The input string `[Elon Musk][Person] is a member of the [PayPal Mafia][Organization].` contains two annotations:
44
+ 1. `[Elon Musk][Person]`: The text `Elon Musk` is annotated as `Person`.
45
+ 2. `[PayPal Mafia][Organization]`: The text `PayPal Mafia` is annotated as `Organization`.
32
46
 
33
- ## Contributing
47
+ - The method returns a hash with:
48
+ - `"text"`: The plain text without annotations.
49
+ - `"denotations"`: An array of hashes, where each hash contains:
50
+ - `"span"`: The character positions (`begin` and `end`) of the annotated text.
51
+ - `"obj"`: The annotation type.
34
52
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/simple_inline_text_annotation. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/simple_inline_text_annotation/blob/main/CODE_OF_CONDUCT.md).
53
+ ### `generate` Method
36
54
 
37
- ## License
55
+ The `generate` method performs the reverse operation of `parse`. It takes a hash containing the plain text and its annotations, and generates a string with inline annotations.
38
56
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
57
+ #### Example
58
+
59
+ ```ruby
60
+ result = SimpleInlineTextAnnotation.generate({
61
+ "text" => "Elon Musk is a member of the PayPal Mafia.",
62
+ "denotations" => [
63
+ { "span" => { "begin" => 0, "end" => 9 }, "obj" => "Person" },
64
+ { "span" => { "begin" => 29, "end" => 41 }, "obj" => "Organization" }
65
+ ]
66
+ })
67
+ puts result
68
+ # => "[Elon Musk][Person] is a member of the [PayPal Mafia][Organization]."
69
+ ```
70
+
71
+ #### Explanation
72
+
73
+ - The input hash contains:
74
+ - `"text"`: The plain text (`"Elon Musk is a member of the PayPal Mafia."`).
75
+ - `"denotations"`: An array of hashes, where each hash specifies:
76
+ - `"span"`: The character positions (`begin` and `end`) of the annotated text.
77
+ - `"obj"`: The annotation type.
78
+ - The method generates a string where:
79
+ - The text specified in `"span"` is enclosed in square brackets `[]`.
80
+ - The annotation type specified in `"obj"` is added in a second set of square brackets `[]`.
81
+
82
+ ## Relation Annotation
83
+
84
+ The `SimpleInlineTextAnnotation` gem supports advanced relation annotation, allowing you to define relationships between annotated entities. This is achieved by interpreting the second set of square brackets (`[]`) based on the number of elements it contains.
85
+
86
+ #### Parsing Rules
87
+
88
+ - If the second `[]` contains **1 element**, it is treated as the annotation type (default behavior).
89
+ - If the second `[]` contains **2 elements**, the first element is interpreted as the `id` of the denotation, and the second element as the `obj` (annotation type).
90
+ - If the second `[]` contains **4 elements**, the elements are interpreted as follows:
91
+ 1. The first element is the `id` of the denotation and the `subj` of the relation.
92
+ 2. The second element is the `obj` (annotation type) of the denotation.
93
+ 3. The third element is the `pred` (predicate) of the relation.
94
+ 4. The fourth element is the `obj` of the relation.
95
+ - Any other cases are ignored.
96
+
97
+ ### Example
98
+
99
+ ```ruby
100
+ source = "[Elon Musk][T1, Person, member_of, T2] is a member of the [PayPal Mafia][T2, Organization]."
101
+ result = SimpleInlineTextAnnotation.parse(source)
102
+ puts result
103
+ # => {
104
+ # text: "Elon Musk is a member of the PayPal Mafia.",
105
+ # denotations: [
106
+ # { id: "T1", span: { begin: 0, end: 9 }, obj: "Person" },
107
+ # { id: "T2", span: { begin: 29, end: 41 }, obj: "Organization" }
108
+ # ],
109
+ # relations: [
110
+ # { pred: "member_of", subj: "T1", obj: "T2" }
111
+ # ]
112
+ # }
113
+ ```
114
+
115
+ ### Explanation
116
+
117
+ - The input string `[Elon Musk][T1, Person, member_of, T2] is a member of the [PayPal Mafia][T2, Organization].` contains:
118
+ 1. Two denotations:
119
+ - `[Elon Musk][T1, Person, member_of, T2]`: The text `Elon Musk` is annotated as `Person` with `id` `T1`. It also serves as the `subj` of the relation.
120
+ - `[PayPal Mafia][T2, Organization]`: The text `PayPal Mafia` is annotated as `Organization` with `id` `T2`.
121
+ 2. One relation:
122
+ - `member_of`: Indicates that `T1` (`Elon Musk`) is a member of `T2` (`PayPal Mafia`).
123
+
124
+ - The method returns a hash with:
125
+ - `"text"`: The plain text without annotations.
126
+ - `"denotations"`: An array of hashes, where each hash contains:
127
+ - `"id"`: The unique identifier of the denotation.
128
+ - `"span"`: The character positions (`begin` and `end`) of the annotated text.
129
+ - `"obj"`: The annotation type.
130
+ - `"relations"`: An array of hashes, where each hash contains:
131
+ - `"pred"`: The predicate or type of the relation.
132
+ - `"subj"`: The `id` of the subject denotation.
133
+ - `"obj"`: The `id` of the object denotation.
134
+
135
+ ### Generating Relation Annotation
136
+
137
+ The `generate` method can also create strings with relation annotations from structured data.
138
+
139
+ ```ruby
140
+ result = SimpleInlineTextAnnotation.generate({
141
+ "text" => "Elon Musk is a member of the PayPal Mafia.",
142
+ "denotations" => [
143
+ { "id" => "T1", "span" => { "begin" => 0, "end" => 9 }, "obj" => "Person" },
144
+ { "id" => "T2", "span" => { "begin" => 29, "end" => 41 }, "obj" => "Organization" }
145
+ ],
146
+ "relations" => [
147
+ { "pred" => "member_of", "subj" => "T1", "obj" => "T2" }
148
+ ]
149
+ })
150
+ puts result
151
+ # => "[Elon Musk][T1, Person, member_of, T2] is a member of the [PayPal Mafia][T2, Organization]."
152
+ ```
40
153
 
41
- ## Code of Conduct
154
+ #### Explanation
42
155
 
43
- Everyone interacting in the SimpleInlineTextAnnotation project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/simple_inline_text_annotation/blob/main/CODE_OF_CONDUCT.md).
156
+ - The input hash includes:
157
+ - `"text"`: The plain text.
158
+ - `"denotations"`: An array of annotations with `id`, `span`, and `obj`.
159
+ - `"relations"`: An array of relationships, where:
160
+ - `"subj"` and `"obj"` reference `id`s in the `denotations` array.
161
+ - `"pred"` specifies the relationship type.
162
+ - The method generates a string with inline annotations and relationships.
@@ -5,12 +5,13 @@ require_relative "generator_error"
5
5
 
6
6
  class SimpleInlineTextAnnotation
7
7
  class Denotation
8
- attr_reader :begin_pos, :end_pos, :obj
8
+ attr_reader :begin_pos, :end_pos, :obj, :id
9
9
 
10
- def initialize(begin_pos, end_pos, obj)
10
+ def initialize(begin_pos, end_pos, obj, id = nil)
11
11
  @begin_pos = begin_pos
12
12
  @end_pos = end_pos
13
13
  @obj = obj
14
+ @id = id
14
15
  end
15
16
 
16
17
  def span
@@ -18,7 +19,7 @@ class SimpleInlineTextAnnotation
18
19
  end
19
20
 
20
21
  def to_h
21
- { span: span, obj: @obj }
22
+ { id: @id, span: span, obj: @obj }.compact
22
23
  end
23
24
 
24
25
  def nested_within?(other)
@@ -2,7 +2,7 @@
2
2
 
3
3
  class SimpleInlineTextAnnotation
4
4
  module DenotationValidator
5
- def validate(denotations, text_length)
5
+ def validate_denotations(denotations, text_length)
6
6
  result = remove_duplicates_from(denotations)
7
7
  result = remove_non_integer_positions_from(result)
8
8
  result = remove_negative_positions_from(result)
@@ -6,6 +6,13 @@ class SimpleInlineTextAnnotation
6
6
  @source = source
7
7
  end
8
8
 
9
+ # get returns the entity type id for a given label.
10
+ # Example:
11
+ # get("Person") => "https://example.com/Person"
12
+ #
13
+ # If the label is not found, it returns nil.
14
+ # Example:
15
+ # get("NonExistentLabel") => nil
9
16
  def get(label)
10
17
  entity_types[label]
11
18
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "denotation"
4
+ require_relative "relation_validator"
4
5
 
5
6
  class SimpleInlineTextAnnotation
6
7
  class Generator
7
8
  include DenotationValidator
9
+ include RelationValidator
8
10
 
9
11
  def initialize(source)
10
12
  @source = source.dup.freeze
@@ -16,9 +18,10 @@ class SimpleInlineTextAnnotation
16
18
  text = @source["text"]
17
19
  raise SimpleInlineTextAnnotation::GeneratorError, 'The "text" key is missing.' if text.nil?
18
20
 
19
- denotations = validate(@denotations, text.length)
21
+ denotations = validate_denotations(@denotations, text.length)
22
+ relations = validate_relations(@source["relations"] || [])
20
23
 
21
- annotated_text = annotate_text(text, denotations)
24
+ annotated_text = annotate_text(text, denotations, relations)
22
25
  label_definitions = build_label_definitions
23
26
 
24
27
  [annotated_text, label_definitions].compact.join("\n\n")
@@ -27,29 +30,47 @@ class SimpleInlineTextAnnotation
27
30
  private
28
31
 
29
32
  def build_denotations(denotations)
30
- denotations.map { |d| Denotation.new(d["span"]["begin"], d["span"]["end"], d["obj"]) }
33
+ denotations.map { |d| Denotation.new(d["span"]["begin"], d["span"]["end"], d["obj"], d["id"]) }
31
34
  end
32
35
 
33
- def annotate_text(text, denotations)
36
+ def annotate_text(text, denotations, relations)
34
37
  # Annotate text from the end to ensure position calculation.
35
38
  denotations.sort_by(&:begin_pos).reverse_each do |denotation|
36
- begin_pos = denotation.begin_pos
37
- end_pos = denotation.end_pos
38
- obj = get_obj(denotation.obj)
39
-
40
- annotated_text = "[#{text[begin_pos...end_pos]}][#{obj}]"
41
- text = text[0...begin_pos] + annotated_text + text[end_pos..]
39
+ text = annotate_text_with_denotation(text, denotation, relations)
42
40
  end
43
41
 
44
42
  text
45
43
  end
46
44
 
45
+ def annotate_text_with_denotation(text, denotation, relations)
46
+ begin_pos = denotation.begin_pos
47
+ end_pos = denotation.end_pos
48
+ annotation = if denotation.id && !denotation.id.empty?
49
+ get_annotations(denotation, relations)
50
+ else
51
+ get_obj(denotation.obj)
52
+ end
53
+
54
+ annotated_text = "[#{text[begin_pos...end_pos]}][#{annotation}]"
55
+ text[0...begin_pos] + annotated_text + text[end_pos..]
56
+ end
57
+
47
58
  def labeled_entity_types
48
59
  return nil unless @config
49
60
 
50
61
  @config["entity types"]&.select { |entity_type| entity_type.key?("label") }
51
62
  end
52
63
 
64
+ def get_annotations(denotation, relations)
65
+ relation = relations.find { |rel| rel["subj"] == denotation.id }
66
+ annotations = [denotation.id, denotation.obj, relation&.dig("pred"), relation&.dig("obj")]
67
+
68
+ return annotations.compact.join(", ") unless labeled_entity_types
69
+
70
+ annotations[1] = get_obj(denotation.obj)
71
+ annotations.compact.join(", ")
72
+ end
73
+
53
74
  def get_obj(obj)
54
75
  return obj unless labeled_entity_types
55
76
 
@@ -7,22 +7,24 @@ class SimpleInlineTextAnnotation
7
7
  class Parser
8
8
  # DENOTATION_PATTERN matches two consecutive pairs of square brackets.
9
9
  # Example: [Annotated Text][Label]
10
- DENOTATION_PATTERN = /(?<!\\)\[([^\[]+?)\]\[([^\]]+?)\]/
10
+ ANNOTATION_PATTERN = /(?<!\\)\[([^\[]+?)\]\[([^\]]+?)\]/
11
11
 
12
12
  def initialize(source)
13
13
  @source = source.dup.freeze
14
- @denotations = []
15
14
  @entity_type_collection = EntityTypeCollection.new(source)
16
15
  end
17
16
 
18
17
  def parse
18
+ @denotations = []
19
+ @relations = []
19
20
  full_text = source_without_references
20
21
 
21
- process_denotations(full_text)
22
+ process_annotations(full_text)
22
23
 
23
24
  SimpleInlineTextAnnotation.new(
24
25
  full_text,
25
26
  @denotations,
27
+ @relations,
26
28
  @entity_type_collection
27
29
  )
28
30
  end
@@ -40,25 +42,54 @@ class SimpleInlineTextAnnotation
40
42
  @entity_type_collection.get(label) || label
41
43
  end
42
44
 
43
- def process_denotations(full_text)
44
- while full_text =~ DENOTATION_PATTERN
45
- match = Regexp.last_match
46
- process_single_denotation(match, full_text)
45
+ def process_annotations(full_text)
46
+ current_pos = 0
47
+
48
+ while (match = ANNOTATION_PATTERN.match(full_text, current_pos))
49
+ current_pos = process_annotation_and_update_position(match, full_text)
47
50
  end
48
51
  end
49
52
 
50
- def process_single_denotation(match, full_text)
53
+ def process_annotation_and_update_position(match, full_text)
51
54
  target_text = match[1]
52
- label = match[2]
53
-
54
55
  begin_pos = match.begin(0)
55
56
  end_pos = begin_pos + target_text.length
56
- obj = get_obj_for(label)
57
57
 
58
- @denotations << Denotation.new(begin_pos, end_pos, obj)
58
+ return match.end(0) unless process_annotation(match[2], begin_pos, end_pos)
59
59
 
60
- # Replace the processed annotation with its text content
61
60
  full_text[match.begin(0)...match.end(0)] = target_text
61
+ end_pos
62
+ end
63
+
64
+ def process_annotation(annotations, begin_pos, end_pos)
65
+ annotations_array = annotations.split(", ")
66
+
67
+ case annotations_array.size
68
+ when 1
69
+ process_denotation(begin_pos, end_pos, annotations_array[0])
70
+ when 2
71
+ process_denotation_with_id(begin_pos, end_pos, annotations_array)
72
+ when 4
73
+ process_denotation_and_relation(begin_pos, end_pos, annotations_array)
74
+ end
75
+ end
76
+
77
+ def process_denotation(begin_pos, end_pos, label)
78
+ obj = get_obj_for(label)
79
+ @denotations << Denotation.new(begin_pos, end_pos, obj)
80
+ end
81
+
82
+ def process_denotation_with_id(begin_pos, end_pos, annotations)
83
+ id, label = annotations
84
+ obj = get_obj_for(label)
85
+ @denotations << Denotation.new(begin_pos, end_pos, obj, id)
86
+ end
87
+
88
+ def process_denotation_and_relation(begin_pos, end_pos, annotations)
89
+ subj, label, pred, obj2 = annotations
90
+ obj = get_obj_for(label)
91
+ @denotations << Denotation.new(begin_pos, end_pos, obj, subj)
92
+ @relations << { pred: pred, subj: subj, obj: obj2 }
62
93
  end
63
94
  end
64
95
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SimpleInlineTextAnnotation
4
+ module RelationValidator
5
+ def validate_relations(relations)
6
+ remove_incomplete_key_relations(relations)
7
+ end
8
+
9
+ private
10
+
11
+ def remove_incomplete_key_relations(relations)
12
+ relations.select { |relation| relation["subj"] && relation["pred"] && relation["obj"] }
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class SimpleInlineTextAnnotation
4
- VERSION = "0.1.0"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -20,9 +20,10 @@ class SimpleInlineTextAnnotation
20
20
  # Example: \[This is a part of][original text]
21
21
  ESCAPE_PATTERN = /\\(?=\[[^\]]+\]\[[^\]]+\])/
22
22
 
23
- def initialize(text, denotations, entity_type_collection)
23
+ def initialize(text, denotations, relations, entity_type_collection)
24
24
  @text = text
25
25
  @denotations = denotations
26
+ @relations = relations
26
27
  @entity_type_collection = entity_type_collection
27
28
  end
28
29
 
@@ -39,6 +40,7 @@ class SimpleInlineTextAnnotation
39
40
  {
40
41
  text: format_text(@text),
41
42
  denotations: @denotations.map(&:to_h),
43
+ relations: @relations.empty? ? nil : @relations,
42
44
  config: config
43
45
  }.compact
44
46
  end
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_inline_text_annotation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - xaiBUh29wX
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 2025-04-03 00:00:00.000000000 Z
11
+ date: 2025-05-08 00:00:00.000000000 Z
11
12
  dependencies: []
12
13
  description: This gem provides inline text annotation functionality, extracted from
13
14
  PubAnnotation, with support for denotations, entity types, and nested spans.
@@ -31,6 +32,7 @@ files:
31
32
  - lib/simple_inline_text_annotation/generator.rb
32
33
  - lib/simple_inline_text_annotation/generator_error.rb
33
34
  - lib/simple_inline_text_annotation/parser.rb
35
+ - lib/simple_inline_text_annotation/relation_validator.rb
34
36
  - lib/simple_inline_text_annotation/version.rb
35
37
  - sig/simple_inline_text_annotation.rbs
36
38
  homepage: https://github.com/Tamada-Arino/simple-inline-text-annotation
@@ -39,6 +41,8 @@ licenses:
39
41
  metadata:
40
42
  homepage_uri: https://github.com/Tamada-Arino/simple-inline-text-annotation
41
43
  changelog_uri: https://github.com/Tamada-Arino/simple-inline-text-annotation/blob/master/CHANGELOG.md
44
+ rubygems_uri: https://rubygems.org/gems/simple_inline_text_annotation
45
+ post_install_message:
42
46
  rdoc_options: []
43
47
  require_paths:
44
48
  - lib
@@ -53,7 +57,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
57
  - !ruby/object:Gem::Version
54
58
  version: '0'
55
59
  requirements: []
56
- rubygems_version: 3.6.2
60
+ rubygems_version: 3.4.20
61
+ signing_key:
57
62
  specification_version: 4
58
63
  summary: A Ruby gem for inline text annotation with denotations and entity types.
59
64
  test_files: []