openphar 0.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 +7 -0
- data/README.adoc +221 -0
- data/lib/openphar/cli/main.rb +137 -0
- data/lib/openphar/cli.rb +11 -0
- data/lib/openphar/core/slug_generator.rb +107 -0
- data/lib/openphar/core.rb +11 -0
- data/lib/openphar/errors/configuration_error.rb +8 -0
- data/lib/openphar/errors/monograph_not_found_error.rb +15 -0
- data/lib/openphar/errors/parse_error.rb +18 -0
- data/lib/openphar/errors/unknown_format_error.rb +8 -0
- data/lib/openphar/errors/unknown_publisher_error.rb +8 -0
- data/lib/openphar/errors/unknown_type_error.rb +8 -0
- data/lib/openphar/errors/validation_error.rb +15 -0
- data/lib/openphar/errors.rb +17 -0
- data/lib/openphar/exporters/coverage_validator.rb +265 -0
- data/lib/openphar/exporters/neo4j/model_registry.rb +130 -0
- data/lib/openphar/exporters/neo4j/node_builder.rb +84 -0
- data/lib/openphar/exporters/neo4j/property_mapper.rb +154 -0
- data/lib/openphar/exporters/neo4j/relationship_builder.rb +87 -0
- data/lib/openphar/exporters/neo4j.rb +16 -0
- data/lib/openphar/exporters/neo4j_exporter.rb +199 -0
- data/lib/openphar/exporters.rb +15 -0
- data/lib/openphar/linkers/chp/cross_edition_linker.rb +101 -0
- data/lib/openphar/linkers/chp.rb +11 -0
- data/lib/openphar/linkers/cross_publisher_linker.rb +295 -0
- data/lib/openphar/linkers/herbapedia_linker.rb +125 -0
- data/lib/openphar/linkers.rb +13 -0
- data/lib/openphar/migrators/chp/classifier.rb +86 -0
- data/lib/openphar/migrators/chp.rb +15 -0
- data/lib/openphar/migrators/chp_migrator.rb +130 -0
- data/lib/openphar/migrators/jp_migrator.rb +345 -0
- data/lib/openphar/migrators/phint_migrator.rb +426 -0
- data/lib/openphar/migrators.rb +15 -0
- data/lib/openphar/models/ahp.rb +25 -0
- data/lib/openphar/models/amino_acid_monograph.rb +28 -0
- data/lib/openphar/models/api.rb +29 -0
- data/lib/openphar/models/assay_specification.rb +19 -0
- data/lib/openphar/models/base_entity.rb +221 -0
- data/lib/openphar/models/biological_substance_monograph.rb +31 -0
- data/lib/openphar/models/chemical_drug_monograph.rb +39 -0
- data/lib/openphar/models/chp/biologic.rb +28 -0
- data/lib/openphar/models/chp/chemical_preparation.rb +28 -0
- data/lib/openphar/models/chp/chemical_substance.rb +28 -0
- data/lib/openphar/models/chp/general_chapter.rb +30 -0
- data/lib/openphar/models/chp/monograph.rb +116 -0
- data/lib/openphar/models/chp/section.rb +37 -0
- data/lib/openphar/models/chp/tcm_crude_drug.rb +28 -0
- data/lib/openphar/models/chp/tcm_extract.rb +29 -0
- data/lib/openphar/models/chp/tcm_formulation.rb +28 -0
- data/lib/openphar/models/chp.rb +33 -0
- data/lib/openphar/models/crude_drug_monograph.rb +31 -0
- data/lib/openphar/models/edition.rb +41 -0
- data/lib/openphar/models/formulation_monograph.rb +31 -0
- data/lib/openphar/models/hkcmms.rb +26 -0
- data/lib/openphar/models/identification_specification.rb +19 -0
- data/lib/openphar/models/jp/kampo_formula.rb +94 -0
- data/lib/openphar/models/jp.rb +21 -0
- data/lib/openphar/models/limit.rb +54 -0
- data/lib/openphar/models/mineral_substance_monograph.rb +28 -0
- data/lib/openphar/models/monograph.rb +58 -0
- data/lib/openphar/models/ph_int/buffer_solution.rb +38 -0
- data/lib/openphar/models/ph_int/dosage_form_monograph.rb +43 -0
- data/lib/openphar/models/ph_int/radiopharmaceutical_monograph.rb +52 -0
- data/lib/openphar/models/ph_int/reagent.rb +50 -0
- data/lib/openphar/models/ph_int/reference_substance.rb +50 -0
- data/lib/openphar/models/ph_int/test_method.rb +50 -0
- data/lib/openphar/models/ph_int/test_solution.rb +39 -0
- data/lib/openphar/models/ph_int/volumetric_solution.rb +38 -0
- data/lib/openphar/models/ph_int.rb +37 -0
- data/lib/openphar/models/physical_specification.rb +17 -0
- data/lib/openphar/models/publisher.rb +98 -0
- data/lib/openphar/models/purity_specification.rb +16 -0
- data/lib/openphar/models/supplement.rb +31 -0
- data/lib/openphar/models/test_specification.rb +29 -0
- data/lib/openphar/models/thp.rb +24 -0
- data/lib/openphar/models/vitamin_monograph.rb +28 -0
- data/lib/openphar/models.rb +58 -0
- data/lib/openphar/monograph_merger.rb +374 -0
- data/lib/openphar/parsers/base_monograph_parser.rb +108 -0
- data/lib/openphar/parsers/jp_html_parser.rb +92 -0
- data/lib/openphar/parsers/jp_html_parser_base.rb +113 -0
- data/lib/openphar/parsers/jp_ja_html_parser.rb +220 -0
- data/lib/openphar/parsers/phint_json_parser.rb +332 -0
- data/lib/openphar/parsers.rb +21 -0
- data/lib/openphar/registry/publisher_registry.rb +46 -0
- data/lib/openphar/registry/type_registry.rb +147 -0
- data/lib/openphar/registry.rb +24 -0
- data/lib/openphar/repositories/monograph_repository.rb +350 -0
- data/lib/openphar/repositories.rb +11 -0
- data/lib/openphar/transformers/jsonld_transformer.rb +106 -0
- data/lib/openphar/transformers.rb +11 -0
- data/lib/openphar/version.rb +5 -0
- data/lib/openphar.rb +119 -0
- data/ontology/bibliographic/edition.ttl +267 -0
- data/ontology/context/chp.jsonld +69 -0
- data/ontology/context/crude-drugs.jsonld +56 -0
- data/ontology/context/pharmacopoeia.jsonld +631 -0
- data/ontology/core/identification.ttl +456 -0
- data/ontology/core/pharmacopoeia.ttl +3427 -0
- data/ontology/core/substance-form.ttl +302 -0
- data/ontology/core/unit.ttl +493 -0
- data/ontology/publisher/chp/extensions.ttl +186 -0
- data/ontology/publisher/phint/extensions.ttl +327 -0
- data/ontology/quality/test-method.ttl +610 -0
- data/shapes/chp/monograph.ttl +153 -0
- data/shapes/edition-shapes.ttl +230 -0
- data/shapes/monograph-shapes.ttl +1324 -0
- metadata +319 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
# Base entity class for all pharmacopoeia models.
|
|
6
|
+
#
|
|
7
|
+
# Provides model-centric serialization methods where the model IS the schema.
|
|
8
|
+
# Each model knows how to serialize itself to:
|
|
9
|
+
# - Neo4j (to_neo4j, to_cypher)
|
|
10
|
+
# - JSON-LD (to_jsonld)
|
|
11
|
+
#
|
|
12
|
+
# Subclasses should override:
|
|
13
|
+
# - neo4j_labels: Array of Neo4j labels
|
|
14
|
+
# - jsonld_type: The @type value for JSON-LD
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# class MyMonograph < BaseEntity
|
|
18
|
+
# attribute :name, :string
|
|
19
|
+
#
|
|
20
|
+
# def self.neo4j_labels
|
|
21
|
+
# ['Monograph', 'MyType']
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# def self.jsonld_type
|
|
25
|
+
# 'MyMonograph'
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
class BaseEntity < Lutaml::Model::Serializable
|
|
29
|
+
# Common attributes for all entities
|
|
30
|
+
attribute :id, :string
|
|
31
|
+
|
|
32
|
+
json do
|
|
33
|
+
map '@id', to: :id
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
# Returns Neo4j labels for this entity type.
|
|
38
|
+
# Override in subclasses to specify custom labels.
|
|
39
|
+
#
|
|
40
|
+
# @return [Array<String>] Neo4j node labels
|
|
41
|
+
def neo4j_labels
|
|
42
|
+
['Entity']
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns the primary Neo4j label (first in the list).
|
|
46
|
+
#
|
|
47
|
+
# @return [String] Primary Neo4j label
|
|
48
|
+
def neo4j_label
|
|
49
|
+
neo4j_labels.first
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns Neo4j label string for Cypher (e.g., "Monograph:CrudeDrug").
|
|
53
|
+
#
|
|
54
|
+
# @return [String] Colon-separated labels
|
|
55
|
+
def neo4j_labels_string
|
|
56
|
+
neo4j_labels.join(':')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns the JSON-LD @type for this entity.
|
|
60
|
+
# Override in subclasses.
|
|
61
|
+
#
|
|
62
|
+
# @return [String] JSON-LD type
|
|
63
|
+
def jsonld_type
|
|
64
|
+
name&.split('::')&.last || 'Entity'
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Returns the JSON-LD context for this entity.
|
|
68
|
+
#
|
|
69
|
+
# @return [String] Context URL
|
|
70
|
+
def jsonld_context
|
|
71
|
+
'https://www.openphar.org/ontology/context/pharmacopoeia.jsonld'
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns a hash representation for Neo4j node.
|
|
76
|
+
#
|
|
77
|
+
# @return [Hash] Node data with id, labels, and properties
|
|
78
|
+
def to_neo4j_node
|
|
79
|
+
{
|
|
80
|
+
id: id,
|
|
81
|
+
labels: self.class.neo4j_labels,
|
|
82
|
+
properties: to_neo4j_properties
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Converts model attributes to Neo4j-compatible properties.
|
|
87
|
+
#
|
|
88
|
+
# @return [Hash] Neo4j properties
|
|
89
|
+
def to_neo4j_properties
|
|
90
|
+
properties = {}
|
|
91
|
+
|
|
92
|
+
self.class.attributes.each_key do |attr_name|
|
|
93
|
+
value = send(attr_name)
|
|
94
|
+
next if value.nil?
|
|
95
|
+
|
|
96
|
+
prop_name = neo4j_property_name(attr_name)
|
|
97
|
+
properties[prop_name] = neo4j_property_value(value)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
properties
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Returns a JSON-LD representation of this entity.
|
|
104
|
+
#
|
|
105
|
+
# @return [Hash] JSON-LD data
|
|
106
|
+
def to_jsonld
|
|
107
|
+
data = {
|
|
108
|
+
'@id' => id,
|
|
109
|
+
'@type' => self.class.jsonld_type,
|
|
110
|
+
'@context' => self.class.jsonld_context
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Add all attributes except id (already included)
|
|
114
|
+
self.class.attributes.each_key do |attr_name|
|
|
115
|
+
next if attr_name == :id
|
|
116
|
+
|
|
117
|
+
value = send(attr_name)
|
|
118
|
+
next if value.nil?
|
|
119
|
+
|
|
120
|
+
json_key = jsonld_property_name(attr_name)
|
|
121
|
+
data[json_key] = value
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
data
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Generates a Cypher CREATE or MERGE statement for this entity.
|
|
128
|
+
#
|
|
129
|
+
# @param use_merge [Boolean] Use MERGE instead of CREATE
|
|
130
|
+
# @param variable [String] Cypher variable name (default: 'n')
|
|
131
|
+
# @return [String] Cypher statement
|
|
132
|
+
def to_cypher(use_merge: false, variable: 'n')
|
|
133
|
+
keyword = use_merge ? 'MERGE' : 'CREATE'
|
|
134
|
+
node = to_neo4j_node
|
|
135
|
+
labels = node[:labels].join(':')
|
|
136
|
+
props = cypher_properties_string(node[:properties])
|
|
137
|
+
|
|
138
|
+
"#{keyword} (#{variable}:#{labels} {#{props}})"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Generates a Cypher MATCH statement for finding this entity.
|
|
142
|
+
#
|
|
143
|
+
# @param variable [String] Cypher variable name
|
|
144
|
+
# @return [String] Cypher MATCH statement
|
|
145
|
+
def to_cypher_match(variable: 'n')
|
|
146
|
+
labels = self.class.neo4j_labels_string
|
|
147
|
+
"MATCH (#{variable}:#{labels} {id: '#{id}'})"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
private
|
|
151
|
+
|
|
152
|
+
# Converts attribute name to Neo4j property name.
|
|
153
|
+
#
|
|
154
|
+
# @param name [Symbol] Attribute name
|
|
155
|
+
# @return [String] Neo4j property name
|
|
156
|
+
def neo4j_property_name(name)
|
|
157
|
+
# Convert snake_case to camelCase for Neo4j
|
|
158
|
+
name.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Converts attribute name to JSON-LD property name.
|
|
162
|
+
#
|
|
163
|
+
# @param name [Symbol] Attribute name
|
|
164
|
+
# @return [String] JSON-LD property name
|
|
165
|
+
def jsonld_property_name(name)
|
|
166
|
+
# Use camelCase for JSON-LD properties
|
|
167
|
+
neo4j_property_name(name)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Converts a value to Neo4j-compatible format.
|
|
171
|
+
#
|
|
172
|
+
# @param value [Object] The value to convert
|
|
173
|
+
# @return [Object] Neo4j-compatible value
|
|
174
|
+
def neo4j_property_value(value)
|
|
175
|
+
case value
|
|
176
|
+
when Hash
|
|
177
|
+
value.to_json
|
|
178
|
+
when Array
|
|
179
|
+
value.map { |v| neo4j_property_value(v) }
|
|
180
|
+
when Lutaml::Model::Serializable
|
|
181
|
+
value.id || value.to_s
|
|
182
|
+
else
|
|
183
|
+
value
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Formats properties as Cypher property string.
|
|
188
|
+
#
|
|
189
|
+
# @param properties [Hash] Properties hash
|
|
190
|
+
# @return [String] Cypher property string
|
|
191
|
+
def cypher_properties_string(properties)
|
|
192
|
+
properties.map do |key, value|
|
|
193
|
+
"#{key}: #{cypher_escape_value(value)}"
|
|
194
|
+
end.join(', ')
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Escapes a value for Cypher.
|
|
198
|
+
#
|
|
199
|
+
# @param value [Object] The value to escape
|
|
200
|
+
# @return [String] Escaped Cypher value
|
|
201
|
+
def cypher_escape_value(value)
|
|
202
|
+
case value
|
|
203
|
+
when String
|
|
204
|
+
"'#{value.gsub("'", "\\'")}'"
|
|
205
|
+
when NilClass
|
|
206
|
+
'null'
|
|
207
|
+
when TrueClass, FalseClass
|
|
208
|
+
value.to_s
|
|
209
|
+
when Numeric
|
|
210
|
+
value.to_s
|
|
211
|
+
when Array
|
|
212
|
+
"[#{value.map { |v| cypher_escape_value(v) }.join(', ')}]"
|
|
213
|
+
when Hash
|
|
214
|
+
"'#{value.to_json.gsub("'", "\\'")}'"
|
|
215
|
+
else
|
|
216
|
+
"'#{value.to_s.gsub("'", "\\'")}'"
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
# Represents a biological substance monograph
|
|
6
|
+
#
|
|
7
|
+
# Biological substances are derived from living organisms or
|
|
8
|
+
# produced by biotechnology methods.
|
|
9
|
+
class BiologicalSubstanceMonograph < Monograph
|
|
10
|
+
attribute :biological_source, :hash, default: {}
|
|
11
|
+
attribute :production_method, :string
|
|
12
|
+
attribute :potency, :string
|
|
13
|
+
|
|
14
|
+
json do
|
|
15
|
+
map 'biologicalSource', to: :biological_source
|
|
16
|
+
map 'productionMethod', to: :production_method
|
|
17
|
+
map 'potency', to: :potency
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
def neo4j_labels
|
|
22
|
+
%w[Monograph Biological]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def jsonld_type
|
|
26
|
+
'BiologicalSubstanceMonograph'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
# Represents a chemical drug monograph
|
|
6
|
+
#
|
|
7
|
+
# Chemical drugs are synthesized or purified chemical compounds
|
|
8
|
+
# with defined molecular structures.
|
|
9
|
+
class ChemicalDrugMonograph < Monograph
|
|
10
|
+
attribute :molecular_formula, :string
|
|
11
|
+
attribute :molecular_weight, :decimal
|
|
12
|
+
attribute :cas_number, :string
|
|
13
|
+
attribute :systematic_name, :hash, default: {}
|
|
14
|
+
attribute :inchi, :string
|
|
15
|
+
attribute :inchi_key, :string
|
|
16
|
+
attribute :smiles, :string
|
|
17
|
+
|
|
18
|
+
json do
|
|
19
|
+
map 'molecularFormula', to: :molecular_formula
|
|
20
|
+
map 'molecularWeight', to: :molecular_weight
|
|
21
|
+
map 'casNumber', to: :cas_number
|
|
22
|
+
map 'systematicName', to: :systematic_name
|
|
23
|
+
map 'inchi', to: :inchi
|
|
24
|
+
map 'inchiKey', to: :inchi_key
|
|
25
|
+
map 'smiles', to: :smiles
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
def neo4j_labels
|
|
30
|
+
%w[Monograph ChemicalDrug]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def jsonld_type
|
|
34
|
+
'ChemicalDrugMonograph'
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
module Chp
|
|
6
|
+
# Biologic — 生物制品 (Vol III).
|
|
7
|
+
#
|
|
8
|
+
# Vaccines, blood products, recombinant proteins, cell therapies, etc.
|
|
9
|
+
# Maps to the cross-publisher BiologicalSubstance concept.
|
|
10
|
+
#
|
|
11
|
+
class Biologic < Monograph
|
|
12
|
+
class << self
|
|
13
|
+
def wire_key
|
|
14
|
+
'biologic'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def harmonized_type
|
|
18
|
+
'BiologicalSubstance'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def jsonld_type
|
|
22
|
+
'ChpBiologic'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
module Chp
|
|
6
|
+
# Chemical preparation — 品种正文 第二部分 (Vol II).
|
|
7
|
+
#
|
|
8
|
+
# Finished chemical drug products (tablets, injections, etc.). Maps
|
|
9
|
+
# to the cross-publisher Formulation concept.
|
|
10
|
+
#
|
|
11
|
+
class ChemicalPreparation < Monograph
|
|
12
|
+
class << self
|
|
13
|
+
def wire_key
|
|
14
|
+
'chemical_preparation'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def harmonized_type
|
|
18
|
+
'Formulation'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def jsonld_type
|
|
22
|
+
'ChpChemicalPreparation'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
module Chp
|
|
6
|
+
# Chemical substance — 品种正文 第一部分 (Vol II).
|
|
7
|
+
#
|
|
8
|
+
# Drug substances (APIs) of chemical origin. Maps to the cross-publisher
|
|
9
|
+
# ChemicalDrug concept.
|
|
10
|
+
#
|
|
11
|
+
class ChemicalSubstance < Monograph
|
|
12
|
+
class << self
|
|
13
|
+
def wire_key
|
|
14
|
+
'chemical_substance'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def harmonized_type
|
|
18
|
+
'ChemicalDrug'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def jsonld_type
|
|
22
|
+
'ChpChemicalSubstance'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
module Chp
|
|
6
|
+
# General chapter — 通则/制剂通则/凡例/指导原则 (Vol IV).
|
|
7
|
+
#
|
|
8
|
+
# General notices, analytical methods (通则 0100–9900), guiding
|
|
9
|
+
# principles (9000 series), reagent/test-solution definitions, etc.
|
|
10
|
+
# No harmonized equivalent in other pharmacopoeias (publisher-specific
|
|
11
|
+
# structural unit).
|
|
12
|
+
#
|
|
13
|
+
class GeneralChapter < Monograph
|
|
14
|
+
class << self
|
|
15
|
+
def wire_key
|
|
16
|
+
'general_chapter'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def harmonized_type
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def jsonld_type
|
|
24
|
+
'ChpGeneralChapter'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
module Chp
|
|
6
|
+
# Base class for all Chinese Pharmacopoeia monographs.
|
|
7
|
+
#
|
|
8
|
+
# Adds ChP-specific identity fields (entryId from the network-edition
|
|
9
|
+
# API, bookId 1..4 for volumes I–IV, directoryTitle for the TOC node,
|
|
10
|
+
# pageNum for the printed-edition cross-reference, pinyin title) on
|
|
11
|
+
# top of the harmonized Openphar::Models::Monograph.
|
|
12
|
+
#
|
|
13
|
+
# Concrete subclasses (TcmCrudeDrug, ChemicalSubstance, Biologic,
|
|
14
|
+
# GeneralChapter, etc.) declare their semantic @type via class methods.
|
|
15
|
+
# The classifier at Openphar::Migrators::Chp::Classifier is the
|
|
16
|
+
# single dispatch point — never use `case/when` on type elsewhere.
|
|
17
|
+
#
|
|
18
|
+
class Monograph < Openphar::Models::Monograph
|
|
19
|
+
attribute :entry_id, :integer
|
|
20
|
+
attribute :book_id, :integer
|
|
21
|
+
attribute :book_name, :string
|
|
22
|
+
attribute :directory_id, :integer
|
|
23
|
+
attribute :directory_title, :string
|
|
24
|
+
attribute :title, :string
|
|
25
|
+
attribute :page_num, :integer
|
|
26
|
+
attribute :pinyin_title, :string
|
|
27
|
+
attribute :e_title, :string
|
|
28
|
+
attribute :html_content, :string
|
|
29
|
+
attribute :part_titles, :string, collection: true, default: []
|
|
30
|
+
attribute :sections, Section, collection: true, default: []
|
|
31
|
+
attribute :visit_count, :integer
|
|
32
|
+
attribute :edition, :string
|
|
33
|
+
attribute :fetched_at, :string
|
|
34
|
+
|
|
35
|
+
json do
|
|
36
|
+
map 'entryId', to: :entry_id
|
|
37
|
+
map 'bookId', to: :book_id
|
|
38
|
+
map 'bookName', to: :book_name
|
|
39
|
+
map 'directoryId', to: :directory_id
|
|
40
|
+
map 'directoryTitle', to: :directory_title
|
|
41
|
+
map 'title', to: :title
|
|
42
|
+
map 'pageNum', to: :page_num
|
|
43
|
+
map 'pinyinTitle', to: :pinyin_title
|
|
44
|
+
map 'eTitle', to: :e_title
|
|
45
|
+
map 'htmlContent', to: :html_content
|
|
46
|
+
map 'partTitle', to: :part_titles
|
|
47
|
+
map 'section', to: :sections
|
|
48
|
+
map 'visitCount', to: :visit_count
|
|
49
|
+
map 'edition', to: :edition
|
|
50
|
+
map 'fetchedAt', to: :fetched_at
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class << self
|
|
54
|
+
# Snake_case identifier used as the JSON-LD node-directory name
|
|
55
|
+
# and as the wire-key for type routing. Override in subclasses.
|
|
56
|
+
#
|
|
57
|
+
# @return [String]
|
|
58
|
+
def wire_key
|
|
59
|
+
'monograph'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Harmonized RDF type this ChP type maps to. Override in subclasses
|
|
63
|
+
# to declare the cross-publisher semantic type (e.g., 'CrudeDrug').
|
|
64
|
+
#
|
|
65
|
+
# @return [String, nil]
|
|
66
|
+
def harmonized_type
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Array of types to emit in JSON-LD @type. Computed from
|
|
71
|
+
# jsonld_type + harmonized_type + the base 'Monograph'.
|
|
72
|
+
#
|
|
73
|
+
# @return [Array<String>]
|
|
74
|
+
def jsonld_types
|
|
75
|
+
types = ['Monograph']
|
|
76
|
+
types << harmonized_type if harmonized_type
|
|
77
|
+
types << jsonld_type unless jsonld_type == 'Monograph'
|
|
78
|
+
types.uniq
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def neo4j_labels
|
|
82
|
+
labels = %w[Monograph Chp]
|
|
83
|
+
labels << harmonized_type if harmonized_type
|
|
84
|
+
labels << name&.split('::')&.last
|
|
85
|
+
labels.compact.uniq
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def jsonld_type
|
|
89
|
+
'ChpMonograph'
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def slug
|
|
94
|
+
base = (pinyin_title || title || "entry-#{entry_id}")
|
|
95
|
+
.to_s
|
|
96
|
+
.downcase
|
|
97
|
+
.gsub(/\s+/, '-')
|
|
98
|
+
.gsub(/[^\p{L}\p{N}\-]/, '')
|
|
99
|
+
.gsub(/\-+/, '-')
|
|
100
|
+
.gsub(/^\-|\-$/, '')
|
|
101
|
+
base = "entry-#{entry_id}" if base.empty?
|
|
102
|
+
"#{base}-#{entry_id}"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def iri
|
|
106
|
+
edition_tag = (edition || 'unknown').to_s.downcase.gsub(/[^a-z0-9]/, '-')
|
|
107
|
+
"#{Openphar.base_iri}/data/chp/#{edition_tag}/entry/#{entry_id}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def jsonld_filename
|
|
111
|
+
"#{slug}.jsonld"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
module Chp
|
|
6
|
+
# Section within a ChP monograph.
|
|
7
|
+
#
|
|
8
|
+
# ChP monographs are structured by 【X】 headings (e.g., 【性状】,
|
|
9
|
+
# 【鉴别】, 【检查】). Each monograph has an ordered list of sections
|
|
10
|
+
# carrying the heading name + the HTML body + a plain-text rendering.
|
|
11
|
+
#
|
|
12
|
+
class Section < Openphar::Models::BaseEntity
|
|
13
|
+
attribute :name, :string
|
|
14
|
+
attribute :sequence, :integer
|
|
15
|
+
attribute :html_content, :string
|
|
16
|
+
attribute :text_content, :string
|
|
17
|
+
|
|
18
|
+
json do
|
|
19
|
+
map 'sectionName', to: :name
|
|
20
|
+
map 'sequence', to: :sequence
|
|
21
|
+
map 'htmlContent', to: :html_content
|
|
22
|
+
map 'textContent', to: :text_content
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
def neo4j_labels
|
|
27
|
+
%w[Section ChpSection]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def jsonld_type
|
|
31
|
+
'ChpSection'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
module Chp
|
|
6
|
+
# TCM crude drug — 药材和饮片 (Vol I).
|
|
7
|
+
#
|
|
8
|
+
# Crude drugs and their decoction pieces. Maps harmonically to the
|
|
9
|
+
# cross-publisher CrudeDrug concept.
|
|
10
|
+
#
|
|
11
|
+
class TcmCrudeDrug < Monograph
|
|
12
|
+
class << self
|
|
13
|
+
def wire_key
|
|
14
|
+
'tcm_crude_drug'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def harmonized_type
|
|
18
|
+
'CrudeDrug'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def jsonld_type
|
|
22
|
+
'TcmCrudeDrug'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
module Chp
|
|
6
|
+
# TCM extract — 植物油脂和提取物 (Vol I).
|
|
7
|
+
#
|
|
8
|
+
# Processed plant extracts and fixed oils. Semantically a sub-type of
|
|
9
|
+
# crude drug (processed form) but kept distinct at the ChP layer for
|
|
10
|
+
# routing fidelity.
|
|
11
|
+
#
|
|
12
|
+
class TcmExtract < Monograph
|
|
13
|
+
class << self
|
|
14
|
+
def wire_key
|
|
15
|
+
'tcm_extract'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def harmonized_type
|
|
19
|
+
'CrudeDrug'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def jsonld_type
|
|
23
|
+
'TcmExtract'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
module Chp
|
|
6
|
+
# TCM formulation — 成方制剂和单味制剂 (Vol I).
|
|
7
|
+
#
|
|
8
|
+
# Classical TCM formulas and single-herb preparations. Maps to the
|
|
9
|
+
# cross-publisher Formulation concept.
|
|
10
|
+
#
|
|
11
|
+
class TcmFormulation < Monograph
|
|
12
|
+
class << self
|
|
13
|
+
def wire_key
|
|
14
|
+
'tcm_formulation'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def harmonized_type
|
|
18
|
+
'Formulation'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def jsonld_type
|
|
22
|
+
'TcmFormulation'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Openphar
|
|
4
|
+
module Models
|
|
5
|
+
# Chinese Pharmacopoeia (ChP) specific models.
|
|
6
|
+
#
|
|
7
|
+
# Contains models unique to the Chinese Pharmacopoeia:
|
|
8
|
+
# - TCM crude drugs (药材和饮片)
|
|
9
|
+
# - TCM extracts (植物油脂和提取物)
|
|
10
|
+
# - TCM formulations (成方制剂和单味制剂)
|
|
11
|
+
# - Chemical substances (品种正文 第一部分)
|
|
12
|
+
# - Chemical preparations (品种正文 第二部分)
|
|
13
|
+
# - Biologics (生物制品)
|
|
14
|
+
# - General chapters (通则/制剂通则/凡例 etc.)
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# Openphar::Models::Chp::TcmCrudeDrug
|
|
18
|
+
# Openphar::Models::Chp::ChemicalSubstance
|
|
19
|
+
#
|
|
20
|
+
# @see https://2025.chp.org.cn
|
|
21
|
+
module Chp
|
|
22
|
+
autoload :Monograph, 'openphar/models/chp/monograph'
|
|
23
|
+
autoload :Section, 'openphar/models/chp/section'
|
|
24
|
+
autoload :TcmCrudeDrug, 'openphar/models/chp/tcm_crude_drug'
|
|
25
|
+
autoload :TcmExtract, 'openphar/models/chp/tcm_extract'
|
|
26
|
+
autoload :TcmFormulation, 'openphar/models/chp/tcm_formulation'
|
|
27
|
+
autoload :ChemicalSubstance, 'openphar/models/chp/chemical_substance'
|
|
28
|
+
autoload :ChemicalPreparation, 'openphar/models/chp/chemical_preparation'
|
|
29
|
+
autoload :Biologic, 'openphar/models/chp/biologic'
|
|
30
|
+
autoload :GeneralChapter, 'openphar/models/chp/general_chapter'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|