csvpath 0.0.501__tar.gz → 0.0.502__tar.gz

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 (292) hide show
  1. {csvpath-0.0.501 → csvpath-0.0.502}/PKG-INFO +2 -1
  2. csvpath-0.0.502/config/config.ini +68 -0
  3. csvpath-0.0.502/csvpath/managers/integrations/ckan/ckan.py +193 -0
  4. csvpath-0.0.502/csvpath/managers/integrations/ckan/ckan_listener.py +390 -0
  5. csvpath-0.0.502/csvpath/managers/integrations/ckan/datafile.py +70 -0
  6. csvpath-0.0.502/csvpath/managers/integrations/ckan/dataset.py +126 -0
  7. {csvpath-0.0.501/csvpath/managers/files → csvpath-0.0.502/csvpath/managers/integrations/ol}/file_listener_ol.py +1 -1
  8. {csvpath-0.0.501/csvpath/managers/paths → csvpath-0.0.502/csvpath/managers/integrations/ol}/paths_listener_ol.py +1 -1
  9. {csvpath-0.0.501/csvpath/managers/results → csvpath-0.0.502/csvpath/managers/integrations/ol}/result_listener_ol.py +1 -1
  10. {csvpath-0.0.501/csvpath/managers/results → csvpath-0.0.502/csvpath/managers/integrations/ol}/results_listener_ol.py +1 -1
  11. {csvpath-0.0.501/csvpath/managers/run → csvpath-0.0.502/csvpath/managers/integrations/ol}/run_listener_ol.py +1 -1
  12. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/integrations/slack/event.py +16 -9
  13. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/result_registrar.py +1 -0
  14. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/config.py +13 -7
  15. {csvpath-0.0.501 → csvpath-0.0.502}/pyproject.toml +2 -1
  16. csvpath-0.0.501/config/config.ini +0 -50
  17. csvpath-0.0.501/csvpath/managers/integrations/slack/sender copy.py +0 -49
  18. {csvpath-0.0.501 → csvpath-0.0.502}/LICENSE +0 -0
  19. {csvpath-0.0.501 → csvpath-0.0.502}/README.md +0 -0
  20. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/__init__.py +0 -0
  21. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/cli/__init__.py +0 -0
  22. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/cli/cli.py +0 -0
  23. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/cli/drill_down.py +0 -0
  24. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/csvpath.py +0 -0
  25. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/csvpaths.py +0 -0
  26. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/__init__.py +0 -0
  27. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/files/file_cacher.py +0 -0
  28. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/files/file_manager.py +0 -0
  29. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/files/file_metadata.py +0 -0
  30. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/files/file_registrar.py +0 -0
  31. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/integrations/ol/event.py +0 -0
  32. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/integrations/ol/event_result.py +0 -0
  33. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/integrations/ol/job.py +0 -0
  34. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/integrations/ol/ol_listener.py +0 -0
  35. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/integrations/ol/run.py +0 -0
  36. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/integrations/ol/run_state.py +0 -0
  37. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/integrations/ol/sender.py +0 -0
  38. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/integrations/slack/sender.py +0 -0
  39. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/listener.py +0 -0
  40. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/metadata.py +0 -0
  41. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/paths/paths_manager.py +0 -0
  42. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/paths/paths_metadata.py +0 -0
  43. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/paths/paths_registrar.py +0 -0
  44. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/registrar.py +0 -0
  45. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/readers/file_errors_reader.py +0 -0
  46. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/readers/file_lines_reader.py +0 -0
  47. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/readers/file_printouts_reader.py +0 -0
  48. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/readers/file_unmatched_reader.py +0 -0
  49. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/readers/readers.py +0 -0
  50. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/result.py +0 -0
  51. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/result_file_reader.py +0 -0
  52. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/result_metadata.py +0 -0
  53. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/result_serializer.py +0 -0
  54. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/results_manager.py +0 -0
  55. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/results_metadata.py +0 -0
  56. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/results/results_registrar.py +0 -0
  57. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/run/run_listener_stdout.py +0 -0
  58. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/run/run_metadata.py +0 -0
  59. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/managers/run/run_registrar.py +0 -0
  60. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/__init__.py +0 -0
  61. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/__init__.py +0 -0
  62. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/args.py +0 -0
  63. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/all.py +0 -0
  64. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/andf.py +0 -0
  65. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/any.py +0 -0
  66. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/between.py +0 -0
  67. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/empty.py +0 -0
  68. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/exists.py +0 -0
  69. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/inf.py +0 -0
  70. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/no.py +0 -0
  71. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/notf.py +0 -0
  72. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/orf.py +0 -0
  73. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/boolean/yes.py +0 -0
  74. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/count.py +0 -0
  75. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/count_bytes.py +0 -0
  76. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/count_headers.py +0 -0
  77. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/count_lines.py +0 -0
  78. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/count_scans.py +0 -0
  79. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/counter.py +0 -0
  80. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/every.py +0 -0
  81. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/has_matches.py +0 -0
  82. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/increment.py +0 -0
  83. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/tally.py +0 -0
  84. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/counting/total_lines.py +0 -0
  85. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/dates/now.py +0 -0
  86. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/function.py +0 -0
  87. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/function_factory.py +0 -0
  88. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/function_finder.py +0 -0
  89. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/function_focus.py +0 -0
  90. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/append.py +0 -0
  91. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/collect.py +0 -0
  92. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/empty_stack.py +0 -0
  93. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/end.py +0 -0
  94. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/header_name.py +0 -0
  95. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/header_names_mismatch.py +0 -0
  96. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/headers.py +0 -0
  97. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/mismatch.py +0 -0
  98. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/replace.py +0 -0
  99. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/headers/reset_headers.py +0 -0
  100. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/lines/advance.py +0 -0
  101. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/lines/after_blank.py +0 -0
  102. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/lines/dups.py +0 -0
  103. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/lines/first.py +0 -0
  104. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/lines/first_line.py +0 -0
  105. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/lines/last.py +0 -0
  106. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/lines/stop.py +0 -0
  107. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/above.py +0 -0
  108. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/add.py +0 -0
  109. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/divide.py +0 -0
  110. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/equals.py +0 -0
  111. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/intf.py +0 -0
  112. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/mod.py +0 -0
  113. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/multiply.py +0 -0
  114. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/round.py +0 -0
  115. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/subtotal.py +0 -0
  116. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/subtract.py +0 -0
  117. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/math/sum.py +0 -0
  118. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/misc/fingerprint.py +0 -0
  119. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/misc/importf.py +0 -0
  120. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/misc/random.py +0 -0
  121. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/print/jinjaf.py +0 -0
  122. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/print/print_line.py +0 -0
  123. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/print/print_queue.py +0 -0
  124. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/print/printf.py +0 -0
  125. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/print/table.py +0 -0
  126. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/stats/minf.py +0 -0
  127. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/stats/percent.py +0 -0
  128. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/stats/percent_unique.py +0 -0
  129. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/stats/stdev.py +0 -0
  130. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/strings/concat.py +0 -0
  131. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/strings/length.py +0 -0
  132. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/strings/lower.py +0 -0
  133. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/strings/metaphone.py +0 -0
  134. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/strings/regex.py +0 -0
  135. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/strings/starts_with.py +0 -0
  136. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/strings/strip.py +0 -0
  137. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/strings/substring.py +0 -0
  138. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/strings/upper.py +0 -0
  139. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/testing/debug.py +0 -0
  140. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/types/__init__.py +0 -0
  141. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/types/boolean.py +0 -0
  142. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/types/datef.py +0 -0
  143. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/types/decimal.py +0 -0
  144. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/types/nonef.py +0 -0
  145. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/types/string.py +0 -0
  146. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/types/type.py +0 -0
  147. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/validity/fail.py +0 -0
  148. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/validity/failed.py +0 -0
  149. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/validity/line.py +0 -0
  150. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/variables/get.py +0 -0
  151. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/variables/pushpop.py +0 -0
  152. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/variables/put.py +0 -0
  153. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/variables/track.py +0 -0
  154. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/functions/variables/variables.py +0 -0
  155. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/lark_parser.py +0 -0
  156. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/lark_transformer.py +0 -0
  157. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/matcher.py +0 -0
  158. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/productions/__init__.py +0 -0
  159. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/productions/equality.py +0 -0
  160. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/productions/expression.py +0 -0
  161. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/productions/header.py +0 -0
  162. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/productions/matchable.py +0 -0
  163. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/productions/qualified.py +0 -0
  164. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/productions/reference.py +0 -0
  165. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/productions/term.py +0 -0
  166. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/productions/variable.py +0 -0
  167. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/util/exceptions.py +0 -0
  168. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/util/expression_encoder.py +0 -0
  169. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/util/expression_utility.py +0 -0
  170. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/util/lark_print_parser.py +0 -0
  171. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/util/print_parser.py +0 -0
  172. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/matching/util/runtime_data_collector.py +0 -0
  173. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/explain_mode.py +0 -0
  174. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/files_mode.py +0 -0
  175. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/logic_mode.py +0 -0
  176. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/mode_controller.py +0 -0
  177. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/print_mode.py +0 -0
  178. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/return_mode.py +0 -0
  179. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/run_mode.py +0 -0
  180. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/source_mode.py +0 -0
  181. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/transfer_mode.py +0 -0
  182. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/unmatched_mode.py +0 -0
  183. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/modes/validation_mode.py +0 -0
  184. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/scanning/__init__.py +0 -0
  185. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/scanning/exceptions.py +0 -0
  186. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/scanning/parser.out +0 -0
  187. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/scanning/parsetab.py +0 -0
  188. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/scanning/scanner.py +0 -0
  189. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/scanning/scanning_lexer.py +0 -0
  190. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/cache.py +0 -0
  191. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/class_loader.py +0 -0
  192. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/config_exception.py +0 -0
  193. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/error.py +0 -0
  194. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/exceptions.py +0 -0
  195. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/file_readers.py +0 -0
  196. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/last_line_stats.py +0 -0
  197. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/line_counter.py +0 -0
  198. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/line_monitor.py +0 -0
  199. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/line_spooler.py +0 -0
  200. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/log_utility.py +0 -0
  201. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/metadata_parser.py +0 -0
  202. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/pandas_data_reader.py +0 -0
  203. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/printer.py +0 -0
  204. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/reference_parser.py +0 -0
  205. {csvpath-0.0.501 → csvpath-0.0.502}/csvpath/util/s3_data_reader.py +0 -0
  206. {csvpath-0.0.501 → csvpath-0.0.502}/docs/asbool.md +0 -0
  207. {csvpath-0.0.501 → csvpath-0.0.502}/docs/assignment.md +0 -0
  208. {csvpath-0.0.501 → csvpath-0.0.502}/docs/comments.md +0 -0
  209. {csvpath-0.0.501 → csvpath-0.0.502}/docs/config.md +0 -0
  210. {csvpath-0.0.501 → csvpath-0.0.502}/docs/examples.md +0 -0
  211. {csvpath-0.0.501 → csvpath-0.0.502}/docs/files.md +0 -0
  212. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/above.md +0 -0
  213. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/advance.md +0 -0
  214. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/after_blank.md +0 -0
  215. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/all.md +0 -0
  216. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/andor.md +0 -0
  217. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/any.md +0 -0
  218. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/average.md +0 -0
  219. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/between.md +0 -0
  220. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/collect.md +0 -0
  221. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/correlate.md +0 -0
  222. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/count.md +0 -0
  223. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/count_bytes.md +0 -0
  224. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/count_headers.md +0 -0
  225. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/counter.md +0 -0
  226. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/date.md +0 -0
  227. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/empty.md +0 -0
  228. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/empty_stack.md +0 -0
  229. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/end.md +0 -0
  230. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/every.md +0 -0
  231. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/fail.md +0 -0
  232. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/fingerprint.md +0 -0
  233. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/first.md +0 -0
  234. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/get.md +0 -0
  235. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/has_dups.md +0 -0
  236. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/has_matches.md +0 -0
  237. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/header.md +0 -0
  238. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/header_name.md +0 -0
  239. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/header_names_mismatch.md +0 -0
  240. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/implementing_functions.md +0 -0
  241. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/import.md +0 -0
  242. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/in.md +0 -0
  243. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/increment.md +0 -0
  244. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/intf.md +0 -0
  245. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/jinja.md +0 -0
  246. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/last.md +0 -0
  247. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/line.md +0 -0
  248. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/line_number.md +0 -0
  249. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/max.md +0 -0
  250. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/metaphone.md +0 -0
  251. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/mismatch.md +0 -0
  252. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/no.md +0 -0
  253. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/not.md +0 -0
  254. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/now.md +0 -0
  255. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/percent_unique.md +0 -0
  256. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/pop.md +0 -0
  257. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/print.md +0 -0
  258. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/print_line.md +0 -0
  259. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/print_queue.md +0 -0
  260. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/random.md +0 -0
  261. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/regex.md +0 -0
  262. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/replace.md +0 -0
  263. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/reset_headers.md +0 -0
  264. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/stdev.md +0 -0
  265. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/stop.md +0 -0
  266. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/string_functions.md +0 -0
  267. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/subtotal.md +0 -0
  268. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/subtract.md +0 -0
  269. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/sum.md +0 -0
  270. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/tally.md +0 -0
  271. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/total_lines.md +0 -0
  272. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/track.md +0 -0
  273. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/types.md +0 -0
  274. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/variables.md +0 -0
  275. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions/variables_and_headers.md +0 -0
  276. {csvpath-0.0.501 → csvpath-0.0.502}/docs/functions.md +0 -0
  277. {csvpath-0.0.501 → csvpath-0.0.502}/docs/grammar.md +0 -0
  278. {csvpath-0.0.501 → csvpath-0.0.502}/docs/headers.md +0 -0
  279. {csvpath-0.0.501 → csvpath-0.0.502}/docs/images/csvpath-icon-sm.png +0 -0
  280. {csvpath-0.0.501 → csvpath-0.0.502}/docs/images/csvpath-logo-wordmark-tight-2.svg +0 -0
  281. {csvpath-0.0.501 → csvpath-0.0.502}/docs/images/logo-wordmark-3.svg +0 -0
  282. {csvpath-0.0.501 → csvpath-0.0.502}/docs/images/logo-wordmark-4.svg +0 -0
  283. {csvpath-0.0.501 → csvpath-0.0.502}/docs/images/logo-wordmark-white-on-black-trimmed-padded.png +0 -0
  284. {csvpath-0.0.501 → csvpath-0.0.502}/docs/images/logo-wordmark-white-trimmed.png +0 -0
  285. {csvpath-0.0.501 → csvpath-0.0.502}/docs/images/marquez-logo-sm.png +0 -0
  286. {csvpath-0.0.501 → csvpath-0.0.502}/docs/images/openlineage-logo-sm.png +0 -0
  287. {csvpath-0.0.501 → csvpath-0.0.502}/docs/paths.md +0 -0
  288. {csvpath-0.0.501 → csvpath-0.0.502}/docs/printing.md +0 -0
  289. {csvpath-0.0.501 → csvpath-0.0.502}/docs/qualifiers.md +0 -0
  290. {csvpath-0.0.501 → csvpath-0.0.502}/docs/references.md +0 -0
  291. {csvpath-0.0.501 → csvpath-0.0.502}/docs/terms.md +0 -0
  292. {csvpath-0.0.501 → csvpath-0.0.502}/docs/variables.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: csvpath
3
- Version: 0.0.501
3
+ Version: 0.0.502
4
4
  Summary: A declarative language for validating CSV, Excel, and other tabular data files
5
5
  Author: David Kershaw
6
6
  Author-email: dk107dk@hotmail.com
@@ -25,6 +25,7 @@ Classifier: Topic :: Utilities
25
25
  Provides-Extra: pandas
26
26
  Provides-Extra: smartopen
27
27
  Requires-Dist: bullet (>=2.2.0,<3.0.0)
28
+ Requires-Dist: ckanapi (>=4.8,<5.0)
28
29
  Requires-Dist: inflect (>=7.4.0,<8.0.0)
29
30
  Requires-Dist: jinja2 (>=3.1.4,<4.0.0)
30
31
  Requires-Dist: lark (>=1.2.2,<2.0.0)
@@ -0,0 +1,68 @@
1
+ [csvpath_files]
2
+ extensions = txt, csvpath, csvpaths
3
+
4
+ [csv_files]
5
+ extensions = txt, csv, tsv, dat, tab, psv, ssv
6
+
7
+ [errors]
8
+ csvpath = raise, collect, stop, fail, print
9
+ csvpaths = raise, collect
10
+
11
+ [logging]
12
+ csvpath = info
13
+ csvpaths = info
14
+ log_file = logs/csvpath.log
15
+ log_files_to_keep = 100
16
+ log_file_size = 52428800
17
+
18
+ [config]
19
+ path = config/config.ini
20
+
21
+ [cache]
22
+ path = cache
23
+
24
+ [functions]
25
+ imports = config/functions.imports
26
+
27
+ [listeners]
28
+ groups =
29
+ #slack, marquez, ckan
30
+
31
+ # add ckan to the list of groups above for alerts to slack webhooks
32
+ ckan.paths = from csvpath.managers.integrations.ckan.ckan_listener import CkanListener
33
+ ckan.result = from csvpath.managers.integrations.ckan.ckan_listener import CkanListener
34
+ ckan.results = from csvpath.managers.integrations.ckan.ckan_listener import CkanListener
35
+
36
+ #add marquez to the list of groups above for OpenLineage events to a local Marquez
37
+ marquez.file = from csvpath.managers.integrations.ol.file_listener_ol import OpenLineageFileListener
38
+ marquez.paths = from csvpath.managers.integrations.ol.paths_listener_ol import OpenLineagePathsListener
39
+ marquez.result = from csvpath.managers.integrations.ol.result_listener_ol import OpenLineageResultListener
40
+ marquez.results = from csvpath.managers.integrations.ol.results_listener_ol import OpenLineageResultsListener
41
+
42
+ # add slack to the list of groups above for alerts to slack webhooks
43
+ slack.file = from csvpath.managers.integrations.slack.sender import SlackSender
44
+ slack.paths = from csvpath.managers.integrations.slack.sender import SlackSender
45
+ slack.result = from csvpath.managers.integrations.slack.sender import SlackSender
46
+ slack.results = from csvpath.managers.integrations.slack.sender import SlackSender
47
+
48
+ [marquez]
49
+ base_url = http://localhost:5000
50
+ endpoint = api/v1/lineage
51
+ api_key = "none"
52
+ timeout = 5
53
+ verify = False
54
+
55
+ [slack]
56
+ # add your main webhook here. to set webhooks on a csvpath-by-csvpath basis add
57
+ # on-valid-slack: webhook-minus-'https://' and/or
58
+ # on-invalid-slack: webhook-minus-'https://'
59
+ webhook_url =
60
+
61
+ [results]
62
+ archive = archive
63
+
64
+ [inputs]
65
+ files = inputs/named_files
66
+ csvpaths = inputs/named_paths
67
+ on_unmatched_file_fingerprints = halt
68
+
@@ -0,0 +1,193 @@
1
+ from ckanapi import RemoteCKAN
2
+ from ckanapi.errors import NotFound
3
+ from csvpath import CsvPaths
4
+ from csvpath.util.config import Config
5
+ from .dataset import Dataset
6
+ from .datafile import Datafile
7
+ import hashlib
8
+ import os
9
+
10
+
11
+ class CkanException(Exception):
12
+ pass
13
+
14
+
15
+ class Ckan:
16
+ def __init__(self, *, config, manifest, csvpaths=None) -> None:
17
+ self.config = config
18
+ self.csvpaths = csvpaths
19
+ self._manifest = manifest
20
+ self._client = None
21
+
22
+ @property
23
+ def manifest(self):
24
+ return self._manifest
25
+
26
+ @property
27
+ def config(self):
28
+ return self._config
29
+
30
+ @config.setter
31
+ def config(self, config):
32
+ self._config = config
33
+
34
+ @property
35
+ def client(self):
36
+ if self._client is None:
37
+ server = self.config._get("ckan", "server")
38
+ if server is None:
39
+ raise CkanException(
40
+ "CKAN server cannot be None. Check config for [ckan] server"
41
+ )
42
+ token = self.config._get("ckan", "api_token")
43
+ if token is None:
44
+ raise CkanException(
45
+ "API token cannot be None. Check config for [ckan] api_token"
46
+ )
47
+ self._client = RemoteCKAN(server, apikey=token, user_agent="CsvPath")
48
+ return self._client
49
+
50
+ # ==========================
51
+ # ==========================
52
+
53
+ def create_or_update_dataset(self, dataset: Dataset) -> None:
54
+ if self._has_dataset(dataset.name):
55
+ self._update_dataset(dataset)
56
+ else:
57
+ self._create_dataset(dataset)
58
+
59
+ def upload_datafile(self, datafile: Datafile) -> dict[str, ...]:
60
+ with open(datafile.path, "rb") as file:
61
+ id = self._has_resource(datafile.dataset_id, datafile.name)
62
+ if id is not None:
63
+ resource = self.client.action.resource_update(
64
+ id=id,
65
+ package_id=datafile.dataset_id,
66
+ url=datafile.url,
67
+ name=datafile.name,
68
+ mimetype=datafile.mime_type,
69
+ upload=file,
70
+ )
71
+ else:
72
+ resource = self.client.action.resource_create(
73
+ package_id=datafile.dataset_id,
74
+ url=datafile.url,
75
+ name=datafile.name,
76
+ mimetype=datafile.mime_type,
77
+ upload=file,
78
+ )
79
+ return resource
80
+
81
+ def create_group_if(self, *, name: str, title: str) -> None:
82
+ #
83
+ # the archive turns into a ckan group. orgs may run multiple CsvPaths.
84
+ # and each named-group can be a dataset with multiple files.
85
+ #
86
+ thename = self._fix_name_if(name)
87
+ if not self._has_group(thename):
88
+ if title is not None and title != thename:
89
+ self.client.action.group_create(name=thename, title=title)
90
+ else:
91
+ self.client.action.group_create(name=thename)
92
+ return thename
93
+
94
+ def _create_dataset(self, dataset: Dataset) -> None:
95
+ if not self._has_org(dataset.org):
96
+ raise CkanException(
97
+ "You must provide the name of an organization. Ask your CKAN admin to create one for you."
98
+ )
99
+ args = {
100
+ "name": self._fix_name_if(dataset.name),
101
+ "title": dataset.title,
102
+ "owner_org": dataset.org,
103
+ "private": not dataset.visible,
104
+ "url": dataset.url,
105
+ }
106
+ if dataset.tags is not None:
107
+ args["tags"] = dataset.tags
108
+ if dataset.fields is not None:
109
+ args["extras"] = dataset.fields
110
+ if dataset.groups is not None:
111
+ args["groups"] = dataset.groups()
112
+ #
113
+ # author = dataset.author
114
+ # author_email = dataset.author_email
115
+ #
116
+ # create dataset as a ckan package
117
+ #
118
+ self.client.action.package_create(**args)
119
+
120
+ def _update_dataset(self, dataset: Dataset) -> None:
121
+ args = {
122
+ "id": self._fix_name_if(dataset.name),
123
+ "title": dataset.title,
124
+ "owner_org": dataset.org,
125
+ "private": not dataset.visible,
126
+ "url": dataset.url,
127
+ }
128
+ if dataset.tags is not None:
129
+ args["tags"] = dataset.tags
130
+ if dataset.fields is not None:
131
+ args["extras"] = dataset.fields
132
+ if dataset.groups is not None:
133
+ args["groups"] = dataset.groups()
134
+ #
135
+ # version = dataset.version
136
+ # author = dataset.author
137
+ # author_email = dataset.author_email
138
+ #
139
+ self.client.action.package_patch(**args)
140
+
141
+ # ==========================
142
+ # ==========================
143
+
144
+ def _fix_name_if(self, name: str) -> str:
145
+ name = name.strip().lower()
146
+ thename = ""
147
+ for n in name:
148
+ if n.isalnum() or n in ["-", "_"]:
149
+ thename += n
150
+ else:
151
+ thename += "_"
152
+ return thename
153
+
154
+ def _has_org(self, name) -> bool:
155
+ try:
156
+ org = self.client.action.organization_show(id=name)
157
+ except NotFound:
158
+ #
159
+ # not found is expected so we do nothing
160
+ #
161
+ ...
162
+ return org is not None
163
+
164
+ def _has_dataset(self, name) -> bool:
165
+ dataset = None
166
+ try:
167
+ dataset = self.client.action.package_show(id=name)
168
+ except NotFound:
169
+ #
170
+ # not found is expected so we do nothing
171
+ #
172
+ ...
173
+ return dataset is not None
174
+
175
+ def _has_group(self, name: str) -> bool:
176
+ name = self._fix_name_if(name)
177
+ groups = self.client.action.group_list(groups=[name])
178
+ return groups and len(groups) == 1 and groups[0] == name
179
+
180
+ def _has_resource(self, package_name, resource_name: str) -> str:
181
+ dataset = None
182
+ try:
183
+ dataset = self.client.action.package_show(id=package_name)
184
+ if "resources" in dataset:
185
+ for r in dataset["resources"]:
186
+ if r.get("name") == resource_name:
187
+ return r["id"]
188
+ except NotFound:
189
+ #
190
+ # not found is expected so we do nothing
191
+ #
192
+ ...
193
+ return None
@@ -0,0 +1,390 @@
1
+ import os
2
+ import json
3
+ from tempfile import NamedTemporaryFile
4
+ from csvpath.managers.listener import Listener
5
+ from csvpath.managers.metadata import Metadata
6
+ from csvpath.managers.results.results_metadata import ResultsMetadata
7
+ from csvpath.managers.results.result import Result
8
+ from csvpath.matching.util.runtime_data_collector import RuntimeDataCollector
9
+ from .ckan import Ckan, CkanException
10
+ from .dataset import Dataset
11
+ from .datafile import Datafile
12
+
13
+
14
+ class CkanListener(Listener):
15
+
16
+ FILETYPE_MAP = {
17
+ "data": "data.csv",
18
+ "unmatched": "unmatched.csv",
19
+ "errors": "errors.json",
20
+ "vars": "vars.json",
21
+ "meta": "meta.json",
22
+ "manifest": "manifest.json",
23
+ "printouts": "printouts.txt",
24
+ }
25
+ FILETYPES = ["data", "unmatched", "meta", "vars", "errors", "printouts", "manifest"]
26
+
27
+ def __init__(self, config=None, csvpaths=None):
28
+ #
29
+ # we're unlikely to get either of these because
30
+ # we're loading dynamically. we'll get them after
31
+ # init.
32
+ #
33
+ super().__init__(config=config)
34
+ self._csvpaths = None
35
+
36
+ def manifest(self, metadata: Metadata) -> dict[str, ...]:
37
+ path = metadata.manifest_path
38
+ with open(path, "r", encoding="utf-8") as file:
39
+ return json.load(file)
40
+
41
+ @property
42
+ def csvpaths(self):
43
+ return self._csvpaths
44
+
45
+ @csvpaths.setter
46
+ def csvpaths(self, csvpaths) -> None:
47
+ self._csvpaths = csvpaths
48
+
49
+ def metadata_update(self, mdata: Metadata) -> None:
50
+ #
51
+ # CKAN is mainly interested in the output file and/or metadata.
52
+ # we can assume that we don't need to react to file, paths,
53
+ # and run events.
54
+ #
55
+ # CKAN cares about individual csvpath instances within the
56
+ # named-paths group. however, it needs to be able to react to
57
+ # a csvpath based on the results of all the other csvpaths in
58
+ # the group. that means we need to work from the results event
59
+ # rather than the result events. using the results event is
60
+ # easy because it is at the right point in time, already
61
+ # collects the summary good/bad indicators, and we have all
62
+ # the data we need to run through the individual csvpaths to
63
+ # look for CKAN work.
64
+ #
65
+ if isinstance(mdata, ResultsMetadata):
66
+ return self._send_result_files(mdata)
67
+
68
+ def _send_result_files(self, mdata: Metadata) -> None:
69
+ mani = self.manifest(mdata)
70
+ if "time_completed" not in mani or mani["time_completed"] is None:
71
+ #
72
+ # the run has started but not completed
73
+ #
74
+ return
75
+ #
76
+ # if for some reason we don't get back our correct results -- the most recent
77
+ # results available -- we can use results_manager.get_named_results_for_run()
78
+ # to find the right set
79
+ #
80
+ results = self.csvpaths.results_manager.get_named_results(
81
+ mdata.named_paths_name
82
+ )
83
+ if results is None:
84
+ raise CkanException(f"Results cannot be none for {mdata.named_paths_name}")
85
+ if len(results) == 0:
86
+ raise CkanException(f"Results cannot be 0 for {mdata.named_paths_name}")
87
+ if not mdata.run_home.endswith(results[0].run_dir):
88
+ raise CkanException(
89
+ f"Results of {mdata.named_paths_name} from wrong run dir: {results[0].run_dir}"
90
+ )
91
+ #
92
+ # we're good. we iterate all and for each we check if the instance requires
93
+ # all other instances to be valid,complete,no-errors. if so, we iterate for
94
+ # that. if all is good we follow the metadata instructions.
95
+ #
96
+ for result in results:
97
+ self._handle_result(results, result, mdata)
98
+
99
+ def _handle_result(
100
+ self, results: list[Result], result: Result, mdata: Metadata
101
+ ) -> None:
102
+ #
103
+ # give the Dataset object a chance to find any new metadata
104
+ # we want to apply to the dataset in CKAN. we may need to
105
+ # pull the CKAN package to see what's already there, or not,
106
+ # or maybe leave that to the CKAN client to adjudicate, if
107
+ # we're ok with overwriting fields and don't lose existing
108
+ # metadata if we don't present it again during the update?
109
+ #
110
+ """
111
+ Metadata directives for CKAN:
112
+ - ckan-publish: always | on-valid | on-all-valid | never
113
+ - ckan-group: use-archive | use-named-results | some-literal
114
+ - ckan-dataset-name: use-instance | use-named-results | var-value:name | a literal
115
+ - ckan-dataset-title: a-metadata-field-name | var-value:name
116
+ - ckan-visibility: private
117
+ - ckan-tags: static-tag1-n | instance-identity | instance-home | var-value:name
118
+ - ckan-show-fields: a, b, c, d....
119
+ - ckan-send: all, printouts, data, metadata, unmatched, vars, errors, manifest
120
+ - ckan-printouts-title: Background
121
+ - ckan-data-title: Orders
122
+ - ckan-unmatched-title: Orders
123
+ - ckan-vars-title: Orders
124
+ - ckan-meta-title: Orders
125
+ - ckan-errors-title: Orders
126
+ - ckan-split-printouts: (no-)split
127
+ """
128
+ pub = self._publish(results=results, result=result)
129
+ if not pub:
130
+ return
131
+ #
132
+ # get manifest
133
+ #
134
+ mani = self.manifest(mdata)
135
+ dataset = Dataset(listener=self, manifest=mani, metadata=mdata)
136
+ #
137
+ # look in metadata for instructions as to if we use a group, and if so,
138
+ # how it is named.
139
+ #
140
+ self._set_group(dataset=dataset, result=result, mani=mani, mdata=mdata)
141
+ #
142
+ # look in metadata for instructions on how to map to the dataset; what name to use, etc.
143
+ #
144
+ self._set_dataset_name(dataset=dataset, result=result, mani=mani, mdata=mdata)
145
+ #
146
+ # find a title if any
147
+ #
148
+ self._set_dataset_title(dataset=dataset, result=result)
149
+ #
150
+ # find a visibility if any
151
+ #
152
+ self._set_visibility(dataset=dataset, result=result)
153
+ #
154
+ # tags to label the dataset with
155
+ #
156
+ self._set_tags(dataset=dataset, result=result, mdata=mdata)
157
+ #
158
+ # metadata fields to display in the dataset
159
+ #
160
+ self._set_metadata_fields(dataset=dataset, result=result)
161
+ #
162
+ # create the dataset before we load the files, or update it if existing
163
+ #
164
+ ckan = Ckan(config=self.config, manifest=mani, csvpaths=self.csvpaths)
165
+ ckan.create_or_update_dataset(dataset)
166
+ #
167
+ # send any files requested. one or more makes sense, but there is no requirement to send any.
168
+ #
169
+ self._send_files(result=result, mani=mani, mdata=mdata, dataset_id=dataset.name)
170
+
171
+ def _publish(self, *, results, result) -> bool:
172
+ pub = result.csvpath.metadata.get("ckan-publish")
173
+ ret = None
174
+ if pub is None:
175
+ ret = False
176
+ else:
177
+ pub = pub.strip().lower()
178
+ if pub == "never":
179
+ ret = False
180
+ elif pub == "always":
181
+ ret = True
182
+ elif pub == "on-valid":
183
+ ret = result.csvpath.is_valid
184
+ elif pub == "on-all-valid":
185
+ ret = True
186
+ for r in results:
187
+ if not r.is_valid:
188
+ ret = False
189
+ break
190
+ return ret
191
+
192
+ def _send_files(
193
+ self, *, result: Result, mani: dict, mdata: Metadata, dataset_id: str
194
+ ) -> None:
195
+ filesstr = result.csvpath.metadata.get("ckan-send")
196
+ files = []
197
+ if filesstr is not None:
198
+ if filesstr.strip() == "all":
199
+ files = CkanListener.FILETYPES[:]
200
+ else:
201
+ fs = filesstr.split(",")
202
+ for f in fs:
203
+ f = f.strip().lower()
204
+ if f not in CkanListener.FILETYPES:
205
+ raise ValueError(
206
+ f"Incorrect file identifier: {f}. Must be in {CkanListener.FILETYPES}."
207
+ )
208
+ files.append(f)
209
+ for file in files:
210
+ path = os.path.join(mani["run_home"], result.identity_or_index)
211
+ path = os.path.join(path, CkanListener.FILETYPE_MAP[file])
212
+ #
213
+ # move printouts out
214
+ #
215
+ if file == "printouts":
216
+ split = result.csvpath.metadata.get("ckan-split-printouts")
217
+ if split and split.strip().lower() == "split":
218
+ if os.path.exists(path):
219
+ printouts = ""
220
+ with open(path, "r", encoding="utf-8") as file:
221
+ printouts = file.read()
222
+ printouts = printouts.split("---- PRINTOUT:")
223
+ for p in printouts:
224
+ self._send_a_printout(
225
+ p=p,
226
+ result=result,
227
+ mani=mani,
228
+ mdata=mdata,
229
+ dataset_id=dataset_id,
230
+ )
231
+ else:
232
+ datafile = Datafile(
233
+ listener=self,
234
+ result=result,
235
+ manifest=mani,
236
+ metadata=mdata,
237
+ filetype=file,
238
+ path=path,
239
+ )
240
+ datafile.dataset_id = dataset_id
241
+ title = result.csvpath.metadata.get(f"ckan-{file}-title")
242
+ if title is not None:
243
+ datafile.name = title
244
+ ckan = Ckan(config=self.config, manifest=mani, csvpaths=self.csvpaths)
245
+ ckan.upload_datafile(datafile)
246
+
247
+ def _send_a_printout(
248
+ self, *, p: str, result: Result, mani: dict, mdata: Metadata, dataset_id: str
249
+ ) -> None:
250
+ i = p.find("\n")
251
+ name = p[0:i]
252
+ name = name.strip()
253
+ if name == "default":
254
+ name = result.csvpath.metadata.get("ckan-printouts-title")
255
+ if name is None:
256
+ name = "Default printer"
257
+ body = p[i + 1 :]
258
+ body = body.strip()
259
+ if body == "":
260
+ return
261
+ with NamedTemporaryFile(delete_on_close=False) as file:
262
+ file.write(body.encode("utf-8"))
263
+ file.close()
264
+ datafile = Datafile(
265
+ listener=self,
266
+ result=result,
267
+ manifest=mani,
268
+ metadata=mdata,
269
+ filetype="printouts",
270
+ path=file.name,
271
+ )
272
+ datafile.dataset_id = dataset_id
273
+ datafile.name = name
274
+ datafile.mime_type = "text/plain"
275
+ ckan = Ckan(config=self.config, manifest=mani, csvpaths=self.csvpaths)
276
+ ckan.upload_datafile(datafile)
277
+
278
+ def _set_visibility(self, dataset: Dataset, result: Result) -> None:
279
+ visibility = result.csvpath.metadata.get("ckan-visibility")
280
+ if visibility and visibility.strip().lower() == "public": # not private
281
+ dataset.visible = False
282
+
283
+ def _set_group(
284
+ self, *, dataset: Dataset, result: Result, mani: dict, mdata: Metadata
285
+ ):
286
+ g = self._create_group_if(
287
+ dataset=dataset, result=result, mani=mani, mdata=mdata
288
+ )
289
+ dataset.group = g
290
+
291
+ def _create_group_if(
292
+ self, *, dataset: Dataset, result: Result, mani: dict, mdata: Metadata
293
+ ) -> str:
294
+ group, title = self._get_group_name(result, mdata)
295
+ if group is None:
296
+ return
297
+ ckan = Ckan(config=self.config, manifest=mani, csvpaths=self.csvpaths)
298
+ group = ckan.create_group_if(name=group, title=title)
299
+ return group
300
+
301
+ def _get_group_name(self, result: Result, mdata: Metadata) -> str:
302
+ group = result.csvpath.metadata.get("ckan-group")
303
+ title = group
304
+ if group is not None:
305
+ group = group.strip().lower()
306
+ if group == "use-archive":
307
+ group = mdata.archive_name
308
+ title = group
309
+ elif group == "use-named-results":
310
+ group = mdata.named_results_name
311
+ title = group
312
+ group = group.strip().lower()
313
+ return (group, title)
314
+
315
+ def _set_dataset_name(
316
+ self, *, dataset: Dataset, result: Result, mani: dict, mdata: Metadata
317
+ ) -> None:
318
+ slug = result.csvpath.metadata.get("ckan-dataset-name")
319
+ lslug = None
320
+ if slug is not None:
321
+ slug = slug.strip()
322
+ lslug = slug.lower()
323
+ if lslug == "use-instance":
324
+ dataset.name = result.csvpath.identity
325
+ elif lslug == "use-named-results":
326
+ dataset.name = mdata.named_results_name
327
+ elif lslug is not None and lslug.startswith("var-value:"):
328
+ v = lslug[10:]
329
+ val = result.csvpath.variables.get(v)
330
+ if val is not None:
331
+ dataset.name = val if val else v
332
+ elif slug is not None:
333
+ dataset.name = slug
334
+ else:
335
+ dataset.name = mdata.named_results_name
336
+
337
+ def _set_dataset_title(self, *, dataset: Dataset, result: Result) -> None:
338
+ title = result.csvpath.metadata.get("ckan-dataset-title")
339
+ if title is not None:
340
+ if title.startswith("var-value:"):
341
+ v = title[10:]
342
+ v = result.csvpath.variables.get(v)
343
+ if v is not None:
344
+ title = v
345
+ dataset.title = title
346
+
347
+ def _set_tags(self, *, dataset: Dataset, result: Result, mdata: Metadata) -> None:
348
+ tagsstr = result.csvpath.metadata.get("ckan-tags")
349
+ tags = []
350
+ if tagsstr is not None:
351
+ ts = tagsstr.split(",")
352
+ for t in ts:
353
+ t = t.strip()
354
+ if t == "instance-identity":
355
+ tags.append({"name": f"{result.csvpath.identity}"})
356
+ elif t == "instance-home":
357
+ h = mdata.run_home
358
+ if h is not None:
359
+ tags.append({"name": f"{h}"})
360
+ elif t.startswith("var-value:"):
361
+ v = t[10:]
362
+ v = result.csvpath.variables.get(v)
363
+ if v is not None:
364
+ tags.append({"name": f"{v}"})
365
+ else:
366
+ tags.append({"name": f"{t}"})
367
+ if len(tags) > 0:
368
+ dataset.tags = tags
369
+
370
+ def _set_metadata_fields(self, *, dataset: Dataset, result: Result) -> None:
371
+ fieldsstr = result.csvpath.metadata.get("ckan-show-fields")
372
+ fields = []
373
+ if fieldsstr is not None:
374
+ fs = fieldsstr.split(",")
375
+ runt = None
376
+ for f in fs:
377
+ f = f.strip()
378
+ v = result.csvpath.metadata.get(f)
379
+ if v is None and f.startswith("var-value:"):
380
+ f = f[10:]
381
+ v = result.csvpath.variables.get(f)
382
+ if v is None:
383
+ if runt is None:
384
+ runt = {}
385
+ RuntimeDataCollector.collect(result.csvpath, runt, local=True)
386
+ v = runt.get(f)
387
+ if v is not None:
388
+ fields.append({"key": f"{f}", "value": f"{v}"})
389
+ if len(fields) > 0:
390
+ dataset.fields = fields