Arachne 1.0
Arachne - the perpetual stitcher of Wikidata entities.
Loading...
Searching...
No Matches
arachne::ariadne::anonymous_namespace{viewer.cpp} Namespace Reference

Functions

const nlohmann::json & array_or_empty (const nlohmann::json &document, std::string_view field)
std::optional< std::string > projection_identifier (const nlohmann::json &value, std::string_view field, std::string_view integer_namespace)
std::optional< std::string > projection_identifier (const nlohmann::json &value, std::string_view integer_namespace)
std::string identifier (const nlohmann::json &value, std::string_view integer_namespace={})
std::string entity_label (const nlohmann::json &value, const std::string &fallback)
void upsert_node (std::map< std::string, nlohmann::ordered_json, std::less<> > &nodes, nlohmann::ordered_json node)
std::string edge_id (std::string_view from, std::string_view to, std::string_view type, std::string_view source_id)
void append_human_edge (nlohmann::ordered_json &edges, std::string from, std::string to, std::string type, std::string assertion_id, std::string snapshot_id, const nlohmann::json &evidence=nlohmann::json::array())
std::string read_file (const std::filesystem::path &path)
void write_file (const std::filesystem::path &path, std::string_view content)
void write_immutable_file (const std::filesystem::path &path, std::string_view content)
void validate_site_root (const std::filesystem::path &root)

Function Documentation

◆ append_human_edge()

void arachne::ariadne::anonymous_namespace{viewer.cpp}::append_human_edge ( nlohmann::ordered_json & edges,
std::string from,
std::string to,
std::string type,
std::string assertion_id,
std::string snapshot_id,
const nlohmann::json & evidence = nlohmann::json::array() )

Definition at line 155 of file viewer.cpp.

159 {
160 nlohmann::ordered_json source_ids = nlohmann::ordered_json::array();
161 nlohmann::ordered_json evidence_ids = nlohmann::ordered_json::array();
162 std::set<std::string, std::less<>> unique_sources;
163 std::set<std::string, std::less<>> unique_evidence;
164 if (!assertion_id.empty()) {
165 unique_sources.insert(assertion_id);
166 }
167 if (evidence.is_array()) {
168 for (const auto& value : evidence) {
169 if (const auto id = projection_identifier(value, "evidence")) {
170 unique_sources.insert(*id);
171 unique_evidence.insert(*id);
172 }
173 }
174 }
175 for (const auto& source : unique_sources) {
176 source_ids.push_back(source);
177 }
178 for (const auto& evidence_id : unique_evidence) {
179 evidence_ids.push_back(evidence_id);
180 }
181 nlohmann::ordered_json provenance {
182 { "origin", "human_authored" },
183 { "snapshot_id", std::move(snapshot_id) },
184 { "source_ids", std::move(source_ids) },
185 { "explanation",
186 "Accepted human-authored research relation with "
187 "assertion-specific evidence." },
188 };
189 edges.push_back(
190 { { "edge_id", edge_id(from, to, type, assertion_id) },
191 { "source", std::move(from) },
192 { "target", std::move(to) },
193 { "edge_type", std::move(type) },
194 { "provenance", std::move(provenance) },
195 { "attributes",
196 { { "derived", false },
197 { "assertion_id", assertion_id },
198 { "evidence", std::move(evidence_ids) } } } }
199 );
200 }
std::optional< std::string > projection_identifier(const nlohmann::json &value, std::string_view field, std::string_view integer_namespace)
Definition viewer.cpp:35
std::string edge_id(std::string_view from, std::string_view to, std::string_view type, std::string_view source_id)
Definition viewer.cpp:143

◆ array_or_empty()

const nlohmann::json & arachne::ariadne::anonymous_namespace{viewer.cpp}::array_or_empty ( const nlohmann::json & document,
std::string_view field )

Definition at line 22 of file viewer.cpp.

22 {
23 static const nlohmann::json empty = nlohmann::json::array();
24 if (!document.is_object() || !document.contains(field)) {
25 return empty;
26 }
27 if (!document.at(field).is_array()) {
28 throw std::invalid_argument(
29 std::string(field) + " must be an array"
30 );
31 }
32 return document.at(field);
33 }

◆ edge_id()

std::string arachne::ariadne::anonymous_namespace{viewer.cpp}::edge_id ( std::string_view from,
std::string_view to,
std::string_view type,
std::string_view source_id )

Definition at line 143 of file viewer.cpp.

146 {
147 return "edge_"
149 std::string(from) + "\n" + std::string(to) + "\n"
150 + std::string(type) + "\n" + std::string(source_id)
151 )
152 .substr(0, 24);
153 }
std::string sha256(std::span< const std::byte > bytes)
Definition crypto.cpp:209

References arachne::crypto::sha256().

Here is the call graph for this function:

◆ entity_label()

std::string arachne::ariadne::anonymous_namespace{viewer.cpp}::entity_label ( const nlohmann::json & value,
const std::string & fallback )

Definition at line 103 of file viewer.cpp.

103 {
104 for (const auto* field : { "label", "name", "title", "value" }) {
105 if (value.contains(field) && value.at(field).is_string()
106 && !value.at(field).get_ref<const std::string&>().empty()) {
107 return value.at(field).get<std::string>();
108 }
109 }
110 return fallback;
111 }

◆ identifier()

std::string arachne::ariadne::anonymous_namespace{viewer.cpp}::identifier ( const nlohmann::json & value,
std::string_view integer_namespace = {} )

Definition at line 89 of file viewer.cpp.

90 {}
91 ) {
92 for (const auto* field :
93 { "id", "entity_id", "candidate_id", "work_id" }) {
94 if (const auto id
95 = projection_identifier(value, field, integer_namespace)) {
96 return *id;
97 }
98 }
99 throw std::invalid_argument("viewer record has no stable identifier");
100 }

◆ projection_identifier() [1/2]

std::optional< std::string > arachne::ariadne::anonymous_namespace{viewer.cpp}::projection_identifier ( const nlohmann::json & value,
std::string_view field,
std::string_view integer_namespace )

Definition at line 35 of file viewer.cpp.

38 {
39 const auto found = value.find(std::string(field));
40 if (found == value.end() || found->is_null()) {
41 return std::nullopt;
42 }
43 if (found->is_string()) {
44 const auto id = found->get<std::string>();
45 return id.empty() ? std::nullopt
46 : std::optional<std::string> { id };
47 }
48 if (found->is_number_integer() && !integer_namespace.empty()) {
49 const auto id = found->get<std::int64_t>();
50 if (id <= 0) {
51 throw std::invalid_argument(
52 std::string(field)
53 + " must be a positive internal database identifier"
54 );
55 }
56 return std::string(integer_namespace) + ":" + std::to_string(id);
57 }
58 throw std::invalid_argument(
59 std::string(field)
60 + " must be a string identifier or a namespaced integer"
61 );
62 }

◆ projection_identifier() [2/2]

std::optional< std::string > arachne::ariadne::anonymous_namespace{viewer.cpp}::projection_identifier ( const nlohmann::json & value,
std::string_view integer_namespace )

Definition at line 64 of file viewer.cpp.

66 {
67 if (value.is_null()) {
68 return std::nullopt;
69 }
70 if (value.is_string()) {
71 const auto id = value.get<std::string>();
72 return id.empty() ? std::nullopt
73 : std::optional<std::string> { id };
74 }
75 if (value.is_number_integer() && !integer_namespace.empty()) {
76 const auto id = value.get<std::int64_t>();
77 if (id <= 0) {
78 throw std::invalid_argument(
79 "internal database identifier must be positive"
80 );
81 }
82 return std::string(integer_namespace) + ":" + std::to_string(id);
83 }
84 throw std::invalid_argument(
85 "projection identifier must be a string or namespaced integer"
86 );
87 }

◆ read_file()

std::string arachne::ariadne::anonymous_namespace{viewer.cpp}::read_file ( const std::filesystem::path & path)

Definition at line 202 of file viewer.cpp.

202 {
203 std::ifstream input(path, std::ios::binary);
204 if (!input) {
205 throw std::runtime_error(
206 "cannot read viewer template: " + path.string()
207 );
208 }
209 return { std::istreambuf_iterator<char>(input),
210 std::istreambuf_iterator<char>() };
211 }

◆ upsert_node()

void arachne::ariadne::anonymous_namespace{viewer.cpp}::upsert_node ( std::map< std::string, nlohmann::ordered_json, std::less<> > & nodes,
nlohmann::ordered_json node )

Definition at line 113 of file viewer.cpp.

116 {
117 const auto id = node.at("node_id").get<std::string>();
118 if (const auto found = nodes.find(id); found == nodes.end()) {
119 nodes.emplace(id, std::move(node));
120 } else {
121 if (node.contains("attributes")
122 && node.at("attributes").is_object()) {
123 auto& attributes = found->second["attributes"];
124 if (!attributes.is_object()) {
125 attributes = nlohmann::ordered_json::object();
126 }
127 for (auto iterator = node.at("attributes").begin();
128 iterator != node.at("attributes").end(); ++iterator) {
129 attributes[iterator.key()] = iterator.value();
130 }
131 }
132 for (auto iterator = node.begin(); iterator != node.end();
133 ++iterator) {
134 if (iterator.key() != "attributes"
135 && (!found->second.contains(iterator.key())
136 || found->second.at(iterator.key()).is_null())) {
137 found->second[iterator.key()] = iterator.value();
138 }
139 }
140 }
141 }

◆ validate_site_root()

void arachne::ariadne::anonymous_namespace{viewer.cpp}::validate_site_root ( const std::filesystem::path & root)

Definition at line 290 of file viewer.cpp.

290 {
291 if (root.empty() || root == root.root_path()) {
292 throw std::invalid_argument("site root must be a scoped directory");
293 }
294 for (const auto& component : root) {
295 if (component == "..") {
296 throw std::invalid_argument(
297 "site root must not contain parent traversal"
298 );
299 }
300 }
301 }

Referenced by arachne::ariadne::viewer_builder::build_site().

Here is the caller graph for this function:

◆ write_file()

void arachne::ariadne::anonymous_namespace{viewer.cpp}::write_file ( const std::filesystem::path & path,
std::string_view content )

Definition at line 214 of file viewer.cpp.

214 {
215 std::filesystem::create_directories(path.parent_path());
216 const auto temporary = path.string() + ".part";
217 {
218 std::ofstream output(temporary, std::ios::binary | std::ios::trunc);
219 if (!output) {
220 throw std::runtime_error(
221 "cannot write viewer artifact: " + path.string()
222 );
223 }
224 output.write(
225 content.data(), static_cast<std::streamsize>(content.size())
226 );
227 if (!output) {
228 throw std::runtime_error(
229 "cannot finish viewer artifact: " + path.string()
230 );
231 }
232 }
233 std::filesystem::rename(temporary, path);
234 }

◆ write_immutable_file()

void arachne::ariadne::anonymous_namespace{viewer.cpp}::write_immutable_file ( const std::filesystem::path & path,
std::string_view content )

Definition at line 236 of file viewer.cpp.

238 {
239 if (path.empty() || path.filename().empty()) {
240 throw std::invalid_argument(
241 "viewer artifact destination must be a file"
242 );
243 }
244 if (!path.parent_path().empty()) {
245 std::filesystem::create_directories(path.parent_path());
246 }
247 if (std::filesystem::exists(path)) {
248 if (!std::filesystem::is_regular_file(path)
249 || std::filesystem::file_size(path) != content.size()
250 || crypto::sha256_file(path) != crypto::sha256(content)) {
251 throw std::runtime_error(
252 "viewer artifact destination already contains different "
253 "bytes"
254 );
255 }
256 return;
257 }
258 auto staging = path;
259 staging += ".part";
260 if (std::filesystem::exists(staging)) {
261 throw std::runtime_error(
262 "viewer artifact staging file already exists"
263 );
264 }
265 try {
266 {
267 std::ofstream output(
268 staging, std::ios::binary | std::ios::trunc
269 );
270 if (!output) {
271 throw std::runtime_error("cannot create viewer artifact");
272 }
273 output.write(
274 content.data(), static_cast<std::streamsize>(content.size())
275 );
276 output.close();
277 if (!output) {
278 throw std::runtime_error("cannot finish viewer artifact");
279 }
280 }
281 std::filesystem::create_hard_link(staging, path);
282 std::filesystem::remove(staging);
283 } catch (...) {
284 std::error_code ignored;
285 std::filesystem::remove(staging, ignored);
286 throw;
287 }
288 }
std::string sha256_file(const std::filesystem::path &path)
Definition crypto.cpp:221

References arachne::crypto::sha256(), and arachne::crypto::sha256_file().

Referenced by arachne::ariadne::viewer_builder::write_projection().

Here is the call graph for this function:
Here is the caller graph for this function: