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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/README.adoc +221 -0
  3. data/lib/openphar/cli/main.rb +137 -0
  4. data/lib/openphar/cli.rb +11 -0
  5. data/lib/openphar/core/slug_generator.rb +107 -0
  6. data/lib/openphar/core.rb +11 -0
  7. data/lib/openphar/errors/configuration_error.rb +8 -0
  8. data/lib/openphar/errors/monograph_not_found_error.rb +15 -0
  9. data/lib/openphar/errors/parse_error.rb +18 -0
  10. data/lib/openphar/errors/unknown_format_error.rb +8 -0
  11. data/lib/openphar/errors/unknown_publisher_error.rb +8 -0
  12. data/lib/openphar/errors/unknown_type_error.rb +8 -0
  13. data/lib/openphar/errors/validation_error.rb +15 -0
  14. data/lib/openphar/errors.rb +17 -0
  15. data/lib/openphar/exporters/coverage_validator.rb +265 -0
  16. data/lib/openphar/exporters/neo4j/model_registry.rb +130 -0
  17. data/lib/openphar/exporters/neo4j/node_builder.rb +84 -0
  18. data/lib/openphar/exporters/neo4j/property_mapper.rb +154 -0
  19. data/lib/openphar/exporters/neo4j/relationship_builder.rb +87 -0
  20. data/lib/openphar/exporters/neo4j.rb +16 -0
  21. data/lib/openphar/exporters/neo4j_exporter.rb +199 -0
  22. data/lib/openphar/exporters.rb +15 -0
  23. data/lib/openphar/linkers/chp/cross_edition_linker.rb +101 -0
  24. data/lib/openphar/linkers/chp.rb +11 -0
  25. data/lib/openphar/linkers/cross_publisher_linker.rb +295 -0
  26. data/lib/openphar/linkers/herbapedia_linker.rb +125 -0
  27. data/lib/openphar/linkers.rb +13 -0
  28. data/lib/openphar/migrators/chp/classifier.rb +86 -0
  29. data/lib/openphar/migrators/chp.rb +15 -0
  30. data/lib/openphar/migrators/chp_migrator.rb +130 -0
  31. data/lib/openphar/migrators/jp_migrator.rb +345 -0
  32. data/lib/openphar/migrators/phint_migrator.rb +426 -0
  33. data/lib/openphar/migrators.rb +15 -0
  34. data/lib/openphar/models/ahp.rb +25 -0
  35. data/lib/openphar/models/amino_acid_monograph.rb +28 -0
  36. data/lib/openphar/models/api.rb +29 -0
  37. data/lib/openphar/models/assay_specification.rb +19 -0
  38. data/lib/openphar/models/base_entity.rb +221 -0
  39. data/lib/openphar/models/biological_substance_monograph.rb +31 -0
  40. data/lib/openphar/models/chemical_drug_monograph.rb +39 -0
  41. data/lib/openphar/models/chp/biologic.rb +28 -0
  42. data/lib/openphar/models/chp/chemical_preparation.rb +28 -0
  43. data/lib/openphar/models/chp/chemical_substance.rb +28 -0
  44. data/lib/openphar/models/chp/general_chapter.rb +30 -0
  45. data/lib/openphar/models/chp/monograph.rb +116 -0
  46. data/lib/openphar/models/chp/section.rb +37 -0
  47. data/lib/openphar/models/chp/tcm_crude_drug.rb +28 -0
  48. data/lib/openphar/models/chp/tcm_extract.rb +29 -0
  49. data/lib/openphar/models/chp/tcm_formulation.rb +28 -0
  50. data/lib/openphar/models/chp.rb +33 -0
  51. data/lib/openphar/models/crude_drug_monograph.rb +31 -0
  52. data/lib/openphar/models/edition.rb +41 -0
  53. data/lib/openphar/models/formulation_monograph.rb +31 -0
  54. data/lib/openphar/models/hkcmms.rb +26 -0
  55. data/lib/openphar/models/identification_specification.rb +19 -0
  56. data/lib/openphar/models/jp/kampo_formula.rb +94 -0
  57. data/lib/openphar/models/jp.rb +21 -0
  58. data/lib/openphar/models/limit.rb +54 -0
  59. data/lib/openphar/models/mineral_substance_monograph.rb +28 -0
  60. data/lib/openphar/models/monograph.rb +58 -0
  61. data/lib/openphar/models/ph_int/buffer_solution.rb +38 -0
  62. data/lib/openphar/models/ph_int/dosage_form_monograph.rb +43 -0
  63. data/lib/openphar/models/ph_int/radiopharmaceutical_monograph.rb +52 -0
  64. data/lib/openphar/models/ph_int/reagent.rb +50 -0
  65. data/lib/openphar/models/ph_int/reference_substance.rb +50 -0
  66. data/lib/openphar/models/ph_int/test_method.rb +50 -0
  67. data/lib/openphar/models/ph_int/test_solution.rb +39 -0
  68. data/lib/openphar/models/ph_int/volumetric_solution.rb +38 -0
  69. data/lib/openphar/models/ph_int.rb +37 -0
  70. data/lib/openphar/models/physical_specification.rb +17 -0
  71. data/lib/openphar/models/publisher.rb +98 -0
  72. data/lib/openphar/models/purity_specification.rb +16 -0
  73. data/lib/openphar/models/supplement.rb +31 -0
  74. data/lib/openphar/models/test_specification.rb +29 -0
  75. data/lib/openphar/models/thp.rb +24 -0
  76. data/lib/openphar/models/vitamin_monograph.rb +28 -0
  77. data/lib/openphar/models.rb +58 -0
  78. data/lib/openphar/monograph_merger.rb +374 -0
  79. data/lib/openphar/parsers/base_monograph_parser.rb +108 -0
  80. data/lib/openphar/parsers/jp_html_parser.rb +92 -0
  81. data/lib/openphar/parsers/jp_html_parser_base.rb +113 -0
  82. data/lib/openphar/parsers/jp_ja_html_parser.rb +220 -0
  83. data/lib/openphar/parsers/phint_json_parser.rb +332 -0
  84. data/lib/openphar/parsers.rb +21 -0
  85. data/lib/openphar/registry/publisher_registry.rb +46 -0
  86. data/lib/openphar/registry/type_registry.rb +147 -0
  87. data/lib/openphar/registry.rb +24 -0
  88. data/lib/openphar/repositories/monograph_repository.rb +350 -0
  89. data/lib/openphar/repositories.rb +11 -0
  90. data/lib/openphar/transformers/jsonld_transformer.rb +106 -0
  91. data/lib/openphar/transformers.rb +11 -0
  92. data/lib/openphar/version.rb +5 -0
  93. data/lib/openphar.rb +119 -0
  94. data/ontology/bibliographic/edition.ttl +267 -0
  95. data/ontology/context/chp.jsonld +69 -0
  96. data/ontology/context/crude-drugs.jsonld +56 -0
  97. data/ontology/context/pharmacopoeia.jsonld +631 -0
  98. data/ontology/core/identification.ttl +456 -0
  99. data/ontology/core/pharmacopoeia.ttl +3427 -0
  100. data/ontology/core/substance-form.ttl +302 -0
  101. data/ontology/core/unit.ttl +493 -0
  102. data/ontology/publisher/chp/extensions.ttl +186 -0
  103. data/ontology/publisher/phint/extensions.ttl +327 -0
  104. data/ontology/quality/test-method.ttl +610 -0
  105. data/shapes/chp/monograph.ttl +153 -0
  106. data/shapes/edition-shapes.ttl +230 -0
  107. data/shapes/monograph-shapes.ttl +1324 -0
  108. metadata +319 -0
@@ -0,0 +1,426 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "fileutils"
5
+
6
+ module Openphar
7
+ module Migrators
8
+ # Migrates International Pharmacopoeia data from data-international-pharmacopoeia
9
+ # to Open Pharmacopoeia ontology format
10
+ #
11
+ # This class reads existing Ph.Int. JSON-LD files and transforms them to
12
+ # the OP ontology structure, preserving radiopharmaceutical data and
13
+ # other Ph.Int.-specific extensions.
14
+ class PhIntMigrator
15
+ attr_reader :source_dir, :output_dir, :edition_id
16
+
17
+ # Category mapping for organizing monographs
18
+ CATEGORIES = {
19
+ "pharmaceutical-substances" => "pharmaceutical-substances",
20
+ "radiopharmaceuticals" => "radiopharmaceuticals",
21
+ "crude-drugs" => "crude-drugs",
22
+ "reagents" => "reagents",
23
+ "methods" => "methods",
24
+ "test-solutions" => "reagents/test-solutions",
25
+ "volumetric-solutions" => "reagents/volumetric-solutions",
26
+ "buffer-solutions" => "reagents/buffer-solutions",
27
+ "reference-substances" => "reagents/reference-substances"
28
+ }.freeze
29
+
30
+ # @param source_dir [String] Directory containing Ph.Int. JSON-LD files
31
+ # @param output_dir [String] Output directory for migrated files
32
+ # @param edition_id [String] Edition IRI (default: Ph.Int.13)
33
+ def initialize(source_dir:, output_dir:, edition_id: "https://www.openphar.org/data/edition/phint/phint13")
34
+ @source_dir = source_dir
35
+ @output_dir = output_dir
36
+ @edition_id = edition_id
37
+ @monographs = []
38
+ @stats = { total: 0, success: 0, failed: 0, categories: Hash.new(0) }
39
+ end
40
+
41
+ # Run the migration
42
+ # @return [Hash] Statistics about the migration
43
+ def run
44
+ puts "Starting Ph.Int. migration..."
45
+ puts " Source: #{source_dir}"
46
+ puts " Output: #{output_dir}"
47
+
48
+ # Ensure output directories exist
49
+ ensure_output_directories
50
+
51
+ # Process JSON-LD files
52
+ process_jsonld_files
53
+
54
+ # Generate aggregate files
55
+ generate_aggregate_files
56
+
57
+ # Print statistics
58
+ print_statistics
59
+
60
+ @stats
61
+ end
62
+
63
+ private
64
+
65
+ def ensure_output_directories
66
+ CATEGORIES.each_value do |category|
67
+ dir = File.join(output_dir, "monographs", category)
68
+ FileUtils.mkdir_p(dir)
69
+ end
70
+ end
71
+
72
+ def process_jsonld_files
73
+ # Find all JSON-LD files in source directory
74
+ jsonld_files = Dir.glob(File.join(source_dir, "**/*.jsonld"))
75
+ puts "Found #{jsonld_files.count} JSON-LD files"
76
+
77
+ jsonld_files.each do |jsonld_file|
78
+ process_jsonld_file(jsonld_file)
79
+ end
80
+ end
81
+
82
+ def process_jsonld_file(file_path)
83
+ puts "Processing #{file_path}..."
84
+
85
+ begin
86
+ content = File.read(file_path, encoding: "UTF-8")
87
+ data = JSON.parse(content)
88
+
89
+ # Handle @graph arrays
90
+ if data["@graph"]
91
+ data["@graph"].each do |node|
92
+ process_node(node)
93
+ end
94
+ else
95
+ process_node(data)
96
+ end
97
+ rescue JSON::ParserError => e
98
+ puts " ERROR: Invalid JSON - #{e.message}"
99
+ @stats[:failed] += 1
100
+ rescue StandardError => e
101
+ puts " ERROR: #{e.message}"
102
+ puts " #{e.backtrace.first(5).join("\n ")}"
103
+ @stats[:failed] += 1
104
+ end
105
+ end
106
+
107
+ def process_node(node)
108
+ return unless node.is_a?(Hash)
109
+ return unless node["@id"] || node["id"]
110
+
111
+ @stats[:total] += 1
112
+
113
+ # Determine the type and category
114
+ type = determine_type(node)
115
+ category = determine_category(node, type)
116
+ @stats[:categories][category] += 1
117
+
118
+ # Transform to OP ontology structure
119
+ transformed = transform_node(node, type, category)
120
+
121
+ # Write individual file
122
+ slug = generate_slug(node)
123
+ output_path = File.join(output_dir, "monographs", category, "#{slug}.jsonld")
124
+ File.write(output_path, JSON.pretty_generate(transformed))
125
+
126
+ @monographs << transformed
127
+ @stats[:success] += 1
128
+
129
+ puts " Created: #{output_path}"
130
+ end
131
+
132
+ def determine_type(node)
133
+ types = node["@type"] || node["type"] || []
134
+ types = [types] unless types.is_a?(Array)
135
+
136
+ # Map Ph.Int. types to OP types
137
+ if types.any? { |t| t.to_s.include?("Radiopharmaceutical") }
138
+ return "RadiopharmaceuticalMonograph"
139
+ end
140
+ if types.any? { |t| t.to_s.include?("CrudeDrug") }
141
+ return "CrudeDrugMonograph"
142
+ end
143
+ if types.any? { |t| t.to_s.include?("TestSolution") }
144
+ return "TestSolution"
145
+ end
146
+ if types.any? { |t| t.to_s.include?("VolumetricSolution") }
147
+ return "VolumetricSolution"
148
+ end
149
+ if types.any? { |t| t.to_s.include?("BufferSolution") }
150
+ return "BufferSolution"
151
+ end
152
+ if types.any? { |t| t.to_s.include?("ReferenceSubstance") }
153
+ return "ReferenceSubstance"
154
+ end
155
+
156
+ "ChemicalDrugMonograph"
157
+ end
158
+
159
+ def determine_category(node, type)
160
+ case type
161
+ when "RadiopharmaceuticalMonograph"
162
+ "radiopharmaceuticals"
163
+ when "CrudeDrugMonograph"
164
+ "crude-drugs"
165
+ when "TestSolution"
166
+ "reagents/test-solutions"
167
+ when "VolumetricSolution"
168
+ "reagents/volumetric-solutions"
169
+ when "BufferSolution"
170
+ "reagents/buffer-solutions"
171
+ when "ReferenceSubstance"
172
+ "reagents/reference-substances"
173
+ else
174
+ # Check for specific characteristics
175
+ name = node["prefLabel"] || node["rdfs:label"] || node["name"]
176
+ if name.is_a?(Hash)
177
+ name = name.values.first
178
+ end
179
+
180
+ if name&.match?(/reagent|solution/i)
181
+ return "reagents"
182
+ end
183
+
184
+ "pharmaceutical-substances"
185
+ end
186
+ end
187
+
188
+ def generate_slug(node)
189
+ # Try to get a name for the slug
190
+ name = node["prefLabel"] || node["rdfs:label"] || node["name"] || node["skos:prefLabel"]
191
+ if name.is_a?(Hash)
192
+ name = name["en"] || name["@value"] || name.values.first
193
+ end
194
+
195
+ return "unknown-#{@stats[:total]}" unless name
196
+
197
+ name.to_s.downcase
198
+ .gsub(/[^a-z0-9\s-]/, "")
199
+ .gsub(/\s+/, "-")
200
+ .gsub(/-+/, "-")
201
+ .strip
202
+ .sub(/^-/, "")
203
+ .sub(/-$/, "")
204
+ end
205
+
206
+ def transform_node(node, type, category)
207
+ slug = generate_slug(node)
208
+ base_iri = "https://www.openphar.org/data/phint/monographs/#{category}/#{slug}"
209
+
210
+ # Get original IRI for reference
211
+ original_id = node["@id"] || node["id"]
212
+
213
+ # Build new JSON-LD structure
214
+ transformed = {
215
+ "@context" => "https://www.openphar.org/ontology/context/pharmacopoeia.jsonld",
216
+ "@id" => base_iri,
217
+ "@type" => type,
218
+ "publisher" => "WHO",
219
+ "belongsToEdition" => @edition_id
220
+ }
221
+
222
+ # Copy over common properties
223
+ copy_property(transformed, node, "prefLabel", "prefLabel")
224
+ copy_property(transformed, node, "skos:prefLabel", "prefLabel")
225
+ copy_property(transformed, node, "rdfs:label", "prefLabel")
226
+ copy_property(transformed, node, "name", "prefLabel")
227
+
228
+ copy_property(transformed, node, "definition", "definition")
229
+ copy_property(transformed, node, "skos:definition", "definition")
230
+
231
+ copy_property(transformed, node, "molecularFormula", "molecularFormula")
232
+ copy_property(transformed, node, "molecularWeight", "molecularWeight")
233
+ copy_property(transformed, node, "casNumber", "casNumber")
234
+
235
+ # Handle chemical identifiers
236
+ copy_property(transformed, node, "inchi", "inchi")
237
+ copy_property(transformed, node, "inchiKey", "inchiKey")
238
+ copy_property(transformed, node, "smiles", "smiles")
239
+
240
+ # Handle test specifications
241
+ if node["testSpecification"] || node["tests"]
242
+ specs = node["testSpecification"] || node["tests"]
243
+ specs = [specs] unless specs.is_a?(Array)
244
+ transformed["testSpecification"] = specs.map do |spec|
245
+ transform_test_specification(spec)
246
+ end
247
+ end
248
+
249
+ # Handle radiopharmaceutical-specific data
250
+ if type == "RadiopharmaceuticalMonograph"
251
+ transform_radiopharmaceutical(transformed, node)
252
+ end
253
+
254
+ # Store reference to original IRI
255
+ if original_id && original_id != base_iri
256
+ transformed["sameAs"] = original_id
257
+ end
258
+
259
+ transformed.compact
260
+ end
261
+
262
+ def copy_property(target, source, source_key, target_key)
263
+ value = source[source_key] || source[source_key.to_sym]
264
+ return unless value
265
+
266
+ # Handle language maps
267
+ if value.is_a?(Hash)
268
+ if value["@value"]
269
+ value = value["@value"]
270
+ elsif value["en"]
271
+ value = { "en" => value["en"] }
272
+ elsif value["@language"]
273
+ lang = value["@language"]
274
+ value = { lang => value["@value"] }
275
+ end
276
+ end
277
+
278
+ target[target_key] = value
279
+ end
280
+
281
+ def transform_test_specification(spec)
282
+ return spec unless spec.is_a?(Hash)
283
+
284
+ transformed = {
285
+ "@type" => spec["@type"] || "TestSpecification"
286
+ }
287
+
288
+ copy_property(transformed, spec, "testName", "testName")
289
+ copy_property(transformed, spec, "name", "testName")
290
+ copy_property(transformed, spec, "testType", "testType")
291
+ copy_property(transformed, spec, "harmonizedMethod", "harmonizedMethod")
292
+ copy_property(transformed, spec, "testConditions", "testConditions")
293
+
294
+ if spec["limit"] || spec["limitValue"]
295
+ transformed["limit"] = transform_limit(spec["limit"] || spec)
296
+ end
297
+
298
+ transformed.compact
299
+ end
300
+
301
+ def transform_limit(limit_data)
302
+ return limit_data unless limit_data.is_a?(Hash)
303
+
304
+ limit = { "@type" => "Limit" }
305
+
306
+ copy_property(limit, limit_data, "limitType", "limitType")
307
+ copy_property(limit, limit_data, "limitValue", "limitValue")
308
+ copy_property(limit, limit_data, "lowerLimit", "lowerLimit")
309
+ copy_property(limit, limit_data, "upperLimit", "upperLimit")
310
+ copy_property(limit, limit_data, "limitUnit", "limitUnit")
311
+
312
+ limit.compact
313
+ end
314
+
315
+ def transform_radiopharmaceutical(transformed, node)
316
+ # Handle radionuclide data
317
+ if node["radionuclide"] || node["hasRadionuclide"]
318
+ rad_data = node["radionuclide"] || node["hasRadionuclide"]
319
+ rad_data = [rad_data] unless rad_data.is_a?(Array)
320
+
321
+ transformed["hasRadionuclide"] = rad_data.map do |rad|
322
+ if rad.is_a?(Hash)
323
+ {
324
+ "@type" => "Radionuclide",
325
+ "prefLabel" => rad["prefLabel"] || rad["name"],
326
+ "halfLife" => rad["halfLife"],
327
+ "emissionType" => rad["emissionType"]
328
+ }.compact
329
+ else
330
+ rad
331
+ end
332
+ end
333
+ end
334
+
335
+ # Handle radioactivity-related specifications
336
+ if node["radiochemicalPurity"]
337
+ transformed["radiochemicalPurity"] = node["radiochemicalPurity"]
338
+ end
339
+
340
+ if node["radionuclidicPurity"]
341
+ transformed["radionuclidicPurity"] = node["radionuclidicPurity"]
342
+ end
343
+
344
+ if node["photonFraction"]
345
+ transformed["photonFraction"] = node["photonFraction"]
346
+ end
347
+ end
348
+
349
+ def generate_aggregate_files
350
+ puts "\nGenerating aggregate files..."
351
+
352
+ # Generate JSON-LD aggregate
353
+ jsonld_aggregate = {
354
+ "@context" => "https://www.openphar.org/ontology/context/pharmacopoeia.jsonld",
355
+ "@id" => "https://www.openphar.org/data/phint/phint-monographs",
356
+ "@type" => "Collection",
357
+ "prefLabel" => { "en" => "International Pharmacopoeia 13 - All Monographs" },
358
+ "belongsToEdition" => @edition_id,
359
+ "@graph" => @monographs
360
+ }
361
+
362
+ jsonld_path = File.join(output_dir, "phint-monographs.jsonld")
363
+ File.write(jsonld_path, JSON.pretty_generate(jsonld_aggregate))
364
+ puts " Created: #{jsonld_path}"
365
+
366
+ # Generate TTL aggregate
367
+ ttl_content = generate_ttl_aggregate
368
+ ttl_path = File.join(output_dir, "phint-monographs.ttl")
369
+ File.write(ttl_path, ttl_content)
370
+ puts " Created: #{ttl_path}"
371
+ end
372
+
373
+ def generate_ttl_aggregate
374
+ ttl = StringIO.new
375
+ ttl.puts "@prefix op: <https://www.openphar.org/ontology/core/> ."
376
+ ttl.puts "@prefix bib: <https://www.openphar.org/ontology/bibliographic/> ."
377
+ ttl.puts "@prefix phint: <https://www.openphar.org/ontology/publisher/phint/> ."
378
+ ttl.puts "@prefix skos: <http://www.w3.org/2004/02/skos/core#> ."
379
+ ttl.puts "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> ."
380
+ ttl.puts ""
381
+ ttl.puts "<https://www.openphar.org/data/phint/phint-monographs>"
382
+ ttl.puts " a op:Collection ;"
383
+ ttl.puts " skos:prefLabel \"International Pharmacopoeia 13 - All Monographs\"@en ;"
384
+ ttl.puts " op:belongsToEdition <#{@edition_id}> ;"
385
+ ttl.puts " op:monographCount #{@monographs.count} ."
386
+ ttl.puts ""
387
+
388
+ @monographs.each do |monograph|
389
+ id = monograph["@id"]
390
+ type = monograph["@type"]
391
+ label = extract_label(monograph)
392
+
393
+ ttl.puts "<#{id}>"
394
+ ttl.puts " a op:#{type} ;"
395
+ ttl.puts " skos:prefLabel #{label.inspect} ;"
396
+ ttl.puts " op:publisher op:WHO ;"
397
+ ttl.puts " op:belongsToEdition <#{@edition_id}> ."
398
+ ttl.puts ""
399
+ end
400
+
401
+ ttl.string
402
+ end
403
+
404
+ def extract_label(monograph)
405
+ label = monograph["prefLabel"]
406
+ return "Unknown" unless label
407
+
408
+ label.is_a?(Hash) ? (label["en"] || label.values.first) : label
409
+ end
410
+
411
+ def print_statistics
412
+ puts "\n" + "=" * 50
413
+ puts "Migration Statistics"
414
+ puts "=" * 50
415
+ puts "Total monographs: #{@stats[:total]}"
416
+ puts "Successfully migrated: #{@stats[:success]}"
417
+ puts "Failed: #{@stats[:failed]}"
418
+ puts "\nBy category:"
419
+ @stats[:categories].each do |category, count|
420
+ puts " #{category}: #{count}"
421
+ end
422
+ puts "=" * 50
423
+ end
424
+ end
425
+ end
426
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openphar
4
+ module Migrators
5
+ # Migrators for converting source data to Openphar format.
6
+ #
7
+ # Handles migration from various pharmacopoeia sources to the
8
+ # unified Openphar ontology.
9
+
10
+ autoload :JpMigrator, 'openphar/migrators/jp_migrator'
11
+ autoload :PhIntMigrator, 'openphar/migrators/phint_migrator'
12
+ autoload :Chp, 'openphar/migrators/chp'
13
+ autoload :ChpMigrator, 'openphar/migrators/chp_migrator'
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openphar
4
+ module Models
5
+ # American Herbal Pharmacopoeia (AHP) specific models.
6
+ #
7
+ # Contains models unique to the American Herbal Pharmacopoeia:
8
+ # - Western herbal monographs with comprehensive therapeutic information
9
+ # - Safety and efficacy profiles
10
+ # - Detailed analytical methods for botanicals
11
+ #
12
+ # @example
13
+ # # Access AHP models
14
+ # Openphar::Models::AHP::HerbalMonograph
15
+ #
16
+ # @see https://www.herbal-ahp.org/
17
+ module AHP
18
+ # Placeholder for AHP-specific models
19
+ # Will include:
20
+ # - HerbalMonograph (Western herbal medicine)
21
+ # - TherapeuticProfile
22
+ # - SafetyProfile
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openphar
4
+ module Models
5
+ # Represents an amino acid monograph
6
+ #
7
+ # Amino acids are the building blocks of proteins.
8
+ class AminoAcidMonograph < ChemicalDrugMonograph
9
+ attribute :amino_acid_type, :string
10
+ attribute :essential, :boolean
11
+
12
+ json do
13
+ map 'aminoAcidType', to: :amino_acid_type
14
+ map 'essential', to: :essential
15
+ end
16
+
17
+ class << self
18
+ def neo4j_labels
19
+ %w[Monograph ChemicalDrug AminoAcid]
20
+ end
21
+
22
+ def jsonld_type
23
+ 'AminoAcidMonograph'
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
+ # Ayurvedic Pharmacopoeia of India (API) specific models.
6
+ #
7
+ # Contains models unique to the Ayurvedic Pharmacopoeia of India:
8
+ # - Dravya (Ayurvedic substances) with Rasa/Guna/Virya/Vipaka/Karma properties
9
+ # - Traditional Ayurvedic formulations (Asava, Arishta, Bhasma, etc.)
10
+ # - Classical preparation methods
11
+ #
12
+ # @example
13
+ # # Access API models
14
+ # Openphar::Models::API::DravyaMonograph
15
+ # Openphar::Models::API::AyurvedicFormula
16
+ #
17
+ # @see https://main.ayush.gov.in/
18
+ module API
19
+ # Placeholder for API-specific models
20
+ # Will include:
21
+ # - DravyaMonograph (Ayurvedic substances)
22
+ # - AyurvedicFormula (classical formulations)
23
+ # - Rasa (taste properties)
24
+ # - Guna (quality properties)
25
+ # - Virya (potency)
26
+ # - Vipaka (post-digestive effect)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openphar
4
+ module Models
5
+ # Represents an assay specification
6
+ #
7
+ # Assay specifications define the quantitative determination of
8
+ # active ingredients or marker compounds.
9
+ class AssaySpecification < TestSpecification
10
+ attribute :assay_target, :hash, default: {}
11
+ attribute :assay_expression, :string
12
+
13
+ json do
14
+ map 'assayTarget', to: :assay_target
15
+ map 'assayExpression', to: :assay_expression
16
+ end
17
+ end
18
+ end
19
+ end