FOSSredder 0.5.0
Loading...
Searching...
No Matches
AnalysisService.cpp
Go to the documentation of this file.
1
5
7
22
23#include <algorithm>
24#include <cstdlib>
25#include <filesystem>
26#include <map>
27#include <memory>
28#include <sstream>
29#include <unordered_map>
30#include <unordered_set>
31
32#include <utility>
33
34#include <nlohmann/json.hpp>
35
37
38namespace {
39
40double parseNumber(const std::string &value) {
41 char *end = nullptr;
42 const double parsed = std::strtod(value.c_str(), &end);
43 return end != value.c_str() ? parsed : 0.0;
44}
45
46void appendUnique(std::vector<std::string> &values, const std::string &value) {
47 if (value.empty() ||
48 std::find(values.begin(), values.end(), value) != values.end()) {
49 return;
50 }
51 values.push_back(value);
52}
53
54std::vector<core::ports::analysis::AnalysisTransaction>
55projectAnalysisTransactions(
56 const core::domain::catalog::WorkspaceCatalog &state,
57 const std::vector<std::shared_ptr<core::domain::Transaction>>
58 &transactions) {
59 std::vector<core::ports::analysis::AnalysisTransaction> out;
60 out.reserve(transactions.size());
61
62 std::unordered_map<std::string, std::shared_ptr<core::domain::Contract>>
63 contractById;
64 contractById.reserve(state.contracts().size());
65 for (const auto &contract : state.contracts()) {
66 if (!contract) {
67 continue;
68 }
69 contractById.emplace(contract->id(), contract);
70 }
71
72 std::unordered_map<std::string, std::string> propertyNameById;
73 propertyNameById.reserve(state.properties().size());
74 for (const auto &property : state.properties()) {
75 if (!property) {
76 continue;
77 }
78 propertyNameById.emplace(property->id(), property->name());
79 }
80
81 for (const auto &transaction : transactions) {
82 if (!transaction) {
83 continue;
84 }
85
86 core::ports::analysis::AnalysisTransaction projected;
87 projected.id = transaction->id();
88 projected.name = transaction->name();
89 projected.bookingDate = transaction->bookingDate();
90 projected.amount = transaction->amount();
91 projected.contractId = transaction->contractId();
92 projected.propertyIds = transaction->propertyIds();
93 if (!transaction->contractId().empty()) {
94 const auto it = contractById.find(transaction->contractId());
95 if (it != contractById.end() && it->second) {
96 projected.contractType = it->second->type();
97 for (const auto &propertyId : it->second->propertyIds()) {
98 if (std::find(projected.propertyIds.begin(),
99 projected.propertyIds.end(),
100 propertyId) == projected.propertyIds.end()) {
101 projected.propertyIds.push_back(propertyId);
102 }
103 }
104 }
105 }
106 projected.propertyNames.reserve(projected.propertyIds.size());
107 for (const auto &propertyId : projected.propertyIds) {
108 const auto nameIt = propertyNameById.find(propertyId);
109 projected.propertyNames.push_back(nameIt != propertyNameById.end() &&
110 !nameIt->second.empty()
111 ? nameIt->second
112 : propertyId);
113 }
114 out.push_back(std::move(projected));
115 }
116
117 return out;
118}
119
120std::shared_ptr<core::domain::catalog::WorkspaceCatalog> catalogWithAdjustments(
121 const core::domain::catalog::WorkspaceCatalog &source,
122 const std::unordered_map<std::string, double> &adjustments) {
123 if (adjustments.empty()) {
124 return {};
125 }
126
127 auto catalog = std::make_shared<core::domain::catalog::WorkspaceCatalog>();
128 catalog->setContracts(source.contracts());
129 catalog->setProperties(source.properties());
130 catalog->setAnalyses(source.analyses());
131 catalog->setStatements(source.statements());
132
134 transactions.reserve(source.transactions().size());
135 for (const auto &transaction : source.transactions()) {
136 if (!transaction) {
137 continue;
138 }
139 auto clone = std::make_shared<core::domain::Transaction>(
140 transaction->name(), transaction->bookingDate(), transaction->valuta(),
141 transaction->amount(), transaction->isAllocatable());
142 clone->setId(transaction->id());
143 clone->setStatementId(transaction->statementId());
144 clone->setContractId(transaction->contractId());
145 clone->setActorId(transaction->actorId());
146 clone->setPropertyIds(transaction->propertyIds());
147 clone->setStatus(transaction->status());
148 clone->setCreatedAt(transaction->createdAt());
149 clone->setUpdatedAt(transaction->updatedAt());
150 const auto adjustmentIt = adjustments.find(transaction->id());
151 if (adjustmentIt != adjustments.end()) {
152 clone->setAmount(adjustmentIt->second);
153 }
154 transactions.push_back(std::move(clone));
155 }
156 catalog->setTransactions(std::move(transactions));
157 return catalog;
158}
159
160std::string jsonString(const nlohmann::json &object, const char *key) {
161 const auto it = object.find(key);
162 if (it == object.end() || !it->is_string()) {
163 return {};
164 }
165 return it->get<std::string>();
166}
167
168double jsonDouble(const nlohmann::json &object, const char *key) {
169 const auto it = object.find(key);
170 if (it == object.end() || !it->is_number()) {
171 return 0.0;
172 }
173 return it->get<double>();
174}
175
176bool jsonBool(const nlohmann::json &object, const char *key) {
177 const auto it = object.find(key);
178 if (it == object.end() || !it->is_boolean()) {
179 return false;
180 }
181 return it->get<bool>();
182}
183
184std::vector<std::string> jsonStringList(const nlohmann::json &object,
185 const char *key);
186
187core::ports::analysis::AnalysisConfigInput
188analysisConfigFromJson(const std::string &raw, const std::string &type) {
189 core::ports::analysis::AnalysisConfigInput out;
190 out.type = type;
191 if (raw.empty()) {
192 return out;
193 }
194 nlohmann::json config;
195 try {
196 config = nlohmann::json::parse(raw);
197 } catch (...) {
198 return out;
199 }
200 if (!config.is_object()) {
201 return out;
202 }
203 out.plotType = jsonString(config, core::application::analysis::keys::kPlotTypeKey.data());
204 out.plotMeasure =
206 out.propertyIds =
207 jsonStringList(config, core::application::analysis::keys::kPropertiesKey.data());
208 out.contractTypes = jsonStringList(
210 out.taxPercent =
212 return out;
213}
214
215std::vector<std::string> jsonStringList(const nlohmann::json &object,
216 const char *key) {
217 std::vector<std::string> out;
218 const auto it = object.find(key);
219 if (it == object.end() || !it->is_array()) {
220 return out;
221 }
222 for (const auto &item : *it) {
223 if (!item.is_string()) {
224 continue;
225 }
226 const auto value = item.get<std::string>();
227 if (!value.empty() &&
228 std::find(out.begin(), out.end(), value) == out.end()) {
229 out.push_back(value);
230 }
231 }
232 return out;
233}
234
235std::shared_ptr<core::domain::catalog::WorkspaceCatalog>
236catalogFromAnalysisSnapshot(const core::domain::Analysis &analysis) {
237 if (!analysis.hasSnapshotTransactions()) {
238 return {};
239 }
240
241 nlohmann::json rows;
242 try {
243 rows = nlohmann::json::parse(analysis.snapshotTransactionsJson());
244 } catch (...) {
245 return {};
246 }
247 if (rows.is_object()) {
248 const auto transactions = rows.find("transactions");
249 rows = transactions != rows.end() ? *transactions : nlohmann::json{};
250 }
251 if (!rows.is_array()) {
252 return {};
253 }
254
255 auto catalog = std::make_shared<core::domain::catalog::WorkspaceCatalog>();
259 std::unordered_set<std::string> seenContracts;
260 std::unordered_set<std::string> seenProperties;
261
262 for (std::size_t index = 0; index < rows.size(); ++index) {
263 const auto &row = rows[index];
264 if (!row.is_object()) {
265 continue;
266 }
267
268 const std::string explicitId = jsonString(row, "id");
269 const std::string transactionId = jsonString(row, "transactionId");
270 const std::string id =
271 !explicitId.empty()
272 ? explicitId
273 : (!transactionId.empty()
274 ? transactionId
275 : "snapshot-transaction-" + std::to_string(index));
276 const std::string name = jsonString(row, "name").empty()
277 ? jsonString(row, "transactionName")
278 : jsonString(row, "name");
279 const std::string bookingDate = jsonString(row, "date");
280 const std::string valuta = jsonString(row, "valuta");
281 const double amount = jsonDouble(row, "amount");
282 const std::string contractId = jsonString(row, "contractId");
283 const std::string contractType = jsonString(row, "contractType");
284 const auto propertyIds = jsonStringList(row, "propertyIds");
285 const auto propertyNames = jsonStringList(row, "propertyNames");
286
287 auto transaction = std::make_shared<core::domain::Transaction>(
288 name, bookingDate, valuta, amount, jsonBool(row, "allocatable"));
289 transaction->setId(id);
290 transaction->setStatementId(jsonString(row, "statementId"));
291 transaction->setContractId(contractId);
292 transaction->setPropertyIds(propertyIds);
293 transactions.push_back(std::move(transaction));
294
295 if (!contractId.empty() && seenContracts.insert(contractId).second) {
296 auto contract = std::make_shared<core::domain::Contract>();
297 contract->setId(contractId);
298 contract->rename(jsonString(row, "contractName"));
299 contract->setType(contractType);
300 contract->setPropertyIds(propertyIds);
301 contracts.push_back(std::move(contract));
302 }
303
304 for (std::size_t p = 0; p < propertyIds.size(); ++p) {
305 const auto &propertyId = propertyIds[p];
306 if (propertyId.empty() || !seenProperties.insert(propertyId).second) {
307 continue;
308 }
309 auto property = std::make_shared<core::domain::Property>();
310 property->setId(propertyId);
311 property->rename(p < propertyNames.size() && !propertyNames[p].empty()
312 ? propertyNames[p]
313 : propertyId);
314 properties.push_back(std::move(property));
315 }
316 }
317
318 catalog->setTransactions(std::move(transactions));
319 catalog->setContracts(std::move(contracts));
320 catalog->setProperties(std::move(properties));
321 return catalog;
322}
323
324std::filesystem::path analysisPreviewPath(
325 const core::ports::analysis::AnalysisRequest &request,
326 const core::ports::analysis::AnalysisResult &result) {
327 std::ostringstream fingerprint;
328 fingerprint << request.analysisId << '\0'
330 << '\0' << result.type << '\0' << result.config.plotType << '\0'
331 << result.config.plotMeasure << '\0';
332 for (const auto &row : result.table) {
333 for (const auto &column : row) {
334 fingerprint << column << '\0';
335 }
336 fingerprint << '\n';
337 }
338
339 const auto hash = std::hash<std::string>{}(fingerprint.str());
340 std::ostringstream fileName;
341 fileName << std::hex << hash << ".png";
342
343 auto directory =
344 std::filesystem::temp_directory_path() / "fossredder-analysis-preview";
345 std::error_code error;
346 std::filesystem::create_directories(directory, error);
347 return directory / fileName.str();
348}
349
350} // namespace
351
353 std::shared_ptr<
355 imageRenderer)
356 : imageRenderer_(std::move(imageRenderer)) {}
357
367
374
377 const std::string &analysisId, const std::string &filterSpec) const {
378 for (const auto &analysis : state.analyses()) {
379 if (!analysis) {
380 continue;
381 }
382 if (analysis->id() != analysisId) {
383 continue;
384 }
385
386 const std::string effectiveFilter =
387 filterSpec.empty() ? analysis->filterSpec() : filterSpec;
388 return computeAnalysis(*analysis, state, effectiveFilter);
389 }
390
392}
393
397 const std::string &filterSpec) const {
399 const auto frozenCatalog = catalogFromAnalysisSnapshot(analysis);
400 const auto adjustedCatalog =
401 analysis.includeCalculationAdjustments()
402 ? catalogWithAdjustments(frozenCatalog ? *frozenCatalog : state,
403 analysis.adjustments())
404 : std::shared_ptr<core::domain::catalog::WorkspaceCatalog>{};
405 const auto &sourceState = adjustedCatalog
406 ? *adjustedCatalog
407 : (frozenCatalog ? *frozenCatalog : state);
410
411 const std::string key = analysis.executionType();
412
415 sourceState, filter);
418 analysis, sourceState, filter);
421 analysis, sourceState, filter);
422 } else {
424 analysis, sourceState, filter);
425 }
426
427 out.type = analysis.outputType();
428 out.config = analysisConfigFromJson(analysis.configJson(), analysis.type());
429 out.transactions = core::application::analysis::projectAnalysisTransactions(
431 sourceState, filter));
432 out.generatedAt = analysis.updatedAt().empty() ? analysis.createdAt()
433 : analysis.updatedAt();
434 out.found = true;
435 return out;
436}
437
438core::ports::analysis::AnalysisResult AnalysisService::withRenderedArtifacts(
441 if (!imageRenderer_ || !result.found) {
442 return result;
443 }
444
445 const auto outputPath = analysisPreviewPath(request, result);
446 std::error_code error;
447 if (std::filesystem::exists(outputPath, error) ||
448 imageRenderer_->renderToImage(outputPath, request.analysisId,
449 result)) {
450 result.artifacts.push_back(outputPath.string());
451 }
452 return result;
453}
454
455std::vector<std::shared_ptr<core::domain::Transaction>>
458 const std::string &filterSpec) const {
459 return collectAnalysisTransactions(state,
460 parseAnalysisFilterSpec(filterSpec));
461}
462
467 const auto state =
469 const std::string filterSpec =
471
472 std::unordered_map<std::string, std::shared_ptr<core::domain::Contract>>
473 contractById;
474 contractById.reserve(state.contracts().size());
475 for (const auto &contract : state.contracts()) {
476 if (!contract || contract->id().empty()) {
477 continue;
478 }
479 contractById.emplace(contract->id(), contract);
480 }
481
482 std::unordered_map<std::string, std::string> propertyNameById;
483 propertyNameById.reserve(state.properties().size());
484 for (const auto &property : state.properties()) {
485 if (!property || property->id().empty()) {
486 continue;
487 }
488 propertyNameById.emplace(property->id(), property->name());
489 }
490
491 std::unordered_map<std::string, std::string> actorNameById;
492 actorNameById.reserve(workspace.actors.size());
493 for (const auto &actor : workspace.actors) {
494 if (!actor.id.empty()) {
495 actorNameById.emplace(actor.id, actor.name);
496 }
497 }
498
499 std::unordered_map<std::string, std::string> statementNameById;
500 statementNameById.reserve(workspace.statements.size());
501 for (const auto &statement : workspace.statements) {
502 if (!statement.id.empty()) {
503 statementNameById.emplace(statement.id, statement.name);
504 }
505 }
506
507 const auto filtered = filterTransactions(state, filterSpec);
509 result.transactions.reserve(filtered.size());
510 std::unordered_set<std::string> statementIds;
511 double amountSum = 0.0;
512
513 for (const auto &transaction : filtered) {
514 if (!transaction) {
515 continue;
516 }
517
519 row.id = transaction->id();
520 row.name = transaction->name();
521 row.bookingDate = transaction->bookingDate();
522 row.valuta = transaction->valuta();
523 row.amount = transaction->amount();
524 row.statementId = transaction->statementId();
525 row.actorId = transaction->actorId();
526 row.contractId = transaction->contractId();
527 row.allocatable = transaction->isAllocatable();
528 row.propertyIds = transaction->propertyIds();
529
530 const auto actorIt = actorNameById.find(row.actorId);
531 if (actorIt != actorNameById.end()) {
532 row.actorName = actorIt->second;
533 }
534 const auto statementIt = statementNameById.find(row.statementId);
535 if (statementIt != statementNameById.end()) {
536 row.statementName = statementIt->second;
537 }
538
539 const auto contractIt = contractById.find(row.contractId);
540 if (contractIt != contractById.end() && contractIt->second) {
541 row.contractName = contractIt->second->name();
542 row.contractType = contractIt->second->type();
543 for (const auto &propertyId : contractIt->second->propertyIds()) {
544 if (std::find(row.propertyIds.begin(), row.propertyIds.end(),
545 propertyId) == row.propertyIds.end()) {
546 row.propertyIds.push_back(propertyId);
547 }
548 }
549 }
550
551 row.propertyNames.reserve(row.propertyIds.size());
552 for (const auto &propertyId : row.propertyIds) {
553 const auto nameIt = propertyNameById.find(propertyId);
554 row.propertyNames.push_back(nameIt != propertyNameById.end() &&
555 !nameIt->second.empty()
556 ? nameIt->second
557 : propertyId);
558 }
559
560 if (!row.statementId.empty()) {
561 statementIds.insert(row.statementId);
562 }
563 amountSum += row.amount;
564 result.transactions.push_back(std::move(row));
565 }
566
568 static_cast<int>(result.transactions.size());
569 result.metrics.statementCount = static_cast<int>(statementIds.size());
570 result.metrics.amountSum = amountSum;
571 return result;
572}
573
576 const std::string &dateField, const std::string &dateMode,
577 const std::string &year, const std::string &dateFrom,
578 const std::string &dateTo, const std::vector<std::string> &propertyIds,
579 const std::vector<std::string> &contractTypes,
580 const std::string &allocatableMode) const {
582 dateField, dateMode, year, dateFrom, dateTo, propertyIds, contractTypes,
583 allocatableMode);
584}
585
587AnalysisService::parseFilterSpec(const std::string &filterSpec) const {
589}
590
595
600
603 const std::vector<core::ports::analysis::AnalysisAdjustmentTransactionInput>
604 &transactions,
605 const std::vector<std::string> &selectedTransactionIds,
606 double taxPercent) const {
607 std::vector<AnalysisAdjustmentTransactionInput> coreTransactions;
608 coreTransactions.reserve(transactions.size());
609 for (const auto &transaction : transactions) {
610 coreTransactions.push_back({transaction.id, transaction.amount});
611 }
614 selectedTransactionIds,
615 taxPercent));
616}
617
618std::optional<double>
619AnalysisService::parseAnalysisPercentText(const std::string &text) const {
621}
622
625 const std::string &analysisId, bool includeCalculationAdjustments,
626 const core::ports::analysis::AnalysisAdjustmentAmounts &adjustments) const {
628 workspace, analysisId, includeCalculationAdjustments, adjustments);
629}
630
634 bool includeCalculationAdjustments,
635 const std::string &unassignedLabel) const {
637
638 std::map<std::string, std::map<std::string, double>> amountsByProperty;
639 std::map<std::string, double> totalsByProperty;
640
641 auto appendAmount = [&](const std::string &transactionId,
642 const std::string &contractType,
643 const std::vector<std::string> &propertyNames,
644 double baseAmount) {
645 const auto adjustmentIt =
646 std::find_if(adjustments.begin(), adjustments.end(),
647 [&](const auto &adjustment) {
648 return adjustment.first == transactionId;
649 });
650 const double amount = includeCalculationAdjustments &&
651 adjustmentIt != adjustments.end()
652 ? adjustmentIt->second
653 : baseAmount;
654 const std::string effectiveContract =
655 contractType.empty() ? unassignedLabel : contractType;
656
657 state.grandTotal += amount;
658 appendUnique(state.contractTypes, effectiveContract);
659
660 if (propertyNames.empty()) {
661 amountsByProperty[unassignedLabel][effectiveContract] += amount;
662 totalsByProperty[unassignedLabel] += amount;
663 return;
664 }
665 for (const auto &propertyName : propertyNames) {
666 const std::string effectiveProperty =
667 propertyName.empty() ? unassignedLabel : propertyName;
668 amountsByProperty[effectiveProperty][effectiveContract] += amount;
669 totalsByProperty[effectiveProperty] += amount;
670 }
671 };
672
673 if (!result.transactions.empty()) {
674 for (const auto &transaction : result.transactions) {
675 std::vector<std::string> propertyNames = transaction.propertyNames;
676 if (propertyNames.empty()) {
677 propertyNames = transaction.propertyIds;
678 }
679 appendAmount(transaction.id, transaction.contractType, propertyNames,
680 transaction.amount);
681 }
682 } else {
683 for (std::size_t i = 0; i < result.table.size(); ++i) {
684 const auto &row = result.table[i];
685 if (row.size() < 3) {
686 continue;
687 }
688 appendAmount("table-row-" + std::to_string(i), unassignedLabel,
689 {unassignedLabel}, parseNumber(row[2]));
690 }
691 }
692
693 std::sort(state.contractTypes.begin(), state.contractTypes.end());
694
695 for (const auto &[propertyName, amountsByContract] : amountsByProperty) {
697 row.propertyName = propertyName;
698 row.total = totalsByProperty[propertyName];
699 row.amounts.reserve(state.contractTypes.size());
700 for (const auto &contractType : state.contractTypes) {
701 const auto it = amountsByContract.find(contractType);
702 row.amounts.push_back(it != amountsByContract.end() ? it->second : 0.0);
703 }
704 state.propertyRows.push_back(std::move(row));
705 }
706 return state;
707}
708
713
714} // namespace core::application::analysis
Declares adjustment analysis calculation helpers.
Domain model for persisted analysis definitions.
Declares internal analysis filter predicates and collection helpers.
Defines stable analysis type, configuration, filter, and result keys.
Declares the analysis execution result port contract.
Declares the application service that resolves and executes analyses.
Shared analysis request/config helpers for UI and application layers.
Domain model for contracts.
Declares the analysis rendering port used by the export layer.
Declares plot-oriented analysis aggregation helpers.
Domain model for properties.
Declares tabular analysis result projection helpers.
Domain model for transactions.
Domain aggregate that stores catalog entities for one workspace.
Maps workspace port snapshots into internal catalog state.
Aggregates transaction predicates parsed from one analysis filter specification.
std::string buildFilterSpec(const core::ports::analysis::AnalysisFilterSelection &selection) const override
Serializes a filter selection through the analysis use case.
core::ports::analysis::AnalysisFilterSelection filterSelectionFromFields(const std::string &dateField, const std::string &dateMode, const std::string &year, const std::string &dateFrom, const std::string &dateTo, const std::vector< std::string > &propertyIds, const std::vector< std::string > &contractTypes, const std::string &allocatableMode) const override
Builds a normalized filter selection from raw field values.
core::ports::analysis::AnalysisResult runAnalysisById(const core::domain::catalog::WorkspaceCatalog &state, const std::string &analysisId, const std::string &filterSpec={}) const
Resolves an analysis by identifier and executes it.
core::ports::analysis::AnalysisAdjustmentAmounts buildAnalysisAdjustments(const std::vector< core::ports::analysis::AnalysisAdjustmentTransactionInput > &transactions, const std::vector< std::string > &selectedTransactionIds, double taxPercent) const override
std::optional< double > parseAnalysisPercentText(const std::string &text) const override
Parses user-entered analysis percentage text.
std::vector< std::shared_ptr< core::domain::Transaction > > filterTransactions(const core::domain::catalog::WorkspaceCatalog &state, const std::string &filterSpec={}) const
Returns the transactions matched by one analysis filter specification.
core::ports::analysis::AnalysisResult runAnalysis(const core::domain::catalog::WorkspaceCatalog &state, const core::ports::analysis::AnalysisRequest &request) const
Runs one analysis request against the supplied workspace state.
std::vector< std::string > contractTypes(const core::ports::workspace::WorkspaceSnapshot &workspace) const override
Returns available contract types for analysis filters.
core::ports::analysis::AnalysisResult computeAnalysis(const core::domain::Analysis &analysis, const core::domain::catalog::WorkspaceCatalog &state, const std::string &filterSpec={}) const
Computes a concrete analysis definition against the supplied workspace state.
core::ports::analysis::AnalysisTableState projectTableState(const core::ports::analysis::AnalysisResult &result, const core::ports::analysis::AnalysisAdjustmentAmounts &adjustments, bool includeCalculationAdjustments, const std::string &unassignedLabel) const override
Projects analysis result table state for UI consumption.
core::ports::analysis::AnalysisPreviewResult previewTransactions(const core::ports::workspace::WorkspaceSnapshot &workspace, const core::ports::analysis::AnalysisFilterSelection &filter) const override
Returns port-native transaction preview rows for one filter specification.
void applyAnalysisPreviewOverrides(core::ports::workspace::WorkspaceSnapshot &workspace, const std::string &analysisId, bool includeCalculationAdjustments, const core::ports::analysis::AnalysisAdjustmentAmounts &adjustments) const override
Applies preview-only analysis fields to a workspace snapshot.
core::ports::analysis::AnalysisFilterSelection parseFilterSpec(const std::string &filterSpec) const override
Parses a serialized filter spec through the analysis use case.
std::string buildAnalysisConfigJson(const core::ports::analysis::AnalysisConfigInput &input) const override
Builds serialized analysis configuration from typed input.
const std::string & snapshotTransactionsJson() const noexcept
Definition Analysis.cpp:201
bool hasSnapshotTransactions() const noexcept
Checks whether snapshot transactions are present.
Definition Analysis.cpp:149
EntityList< core::domain::Contract > ContractList
EntityList< core::domain::Transaction > TransactionList
const PropertyList & properties() const noexcept
Returns all properties in the catalog.
EntityList< core::domain::Property > PropertyList
const StatementList & statements() const noexcept
Returns all statements in the catalog.
const TransactionList & transactions() const noexcept
Returns all transactions in the catalog.
const ContractList & contracts() const noexcept
Returns all contracts in the catalog.
const AnalysisList & analyses() const noexcept
Returns all analyses in the catalog.
Renders analysis results to image artifacts.
constexpr std::string_view kTypeCalculation
constexpr std::string_view kTypePlot
constexpr std::string_view kPlotTypeKey
constexpr std::string_view kTypeTab
constexpr std::string_view kPropertiesKey
constexpr std::string_view kPlotMeasureKey
constexpr std::string_view kContractTypesKey
std::vector< std::string > contractTypesFromSnapshot(const core::ports::workspace::WorkspaceSnapshot &workspace)
core::ports::analysis::AnalysisFilterSelection filterSelectionFromFields(const std::string &dateField, const std::string &dateMode, const std::string &year, const std::string &dateFrom, const std::string &dateTo, const std::vector< std::string > &propertyIds, const std::vector< std::string > &contractTypes, const std::string &allocatableMode)
core::ports::analysis::AnalysisResult computeTableAnalysis(const core::domain::Analysis &analysis, const core::domain::catalog::WorkspaceCatalog &state, const AnalysisFilter &filter)
Computes table rows for transactions matched by one analysis.
std::string buildAnalysisAdjustmentsJson(const std::vector< AnalysisAdjustmentTransactionInput > &transactions, const std::vector< std::string > &selectedTransactionIds, double taxPercent)
core::ports::analysis::AnalysisResult computePlotAnalysis(const core::domain::Analysis &analysis, const core::domain::catalog::WorkspaceCatalog &state, const AnalysisFilter &filter)
Computes plot metrics and table rows for one analysis definition.
std::vector< std::shared_ptr< core::domain::Transaction > > collectAnalysisTransactions(const core::domain::catalog::WorkspaceCatalog &state, const AnalysisFilter &filter)
Collects transactions that satisfy the supplied filter.
std::optional< double > parseAnalysisPercentText(const std::string &text)
Parses a user-entered analysis percentage.
core::ports::analysis::AnalysisResult computeAdjustmentAnalysis(const core::domain::Analysis &analysis, const core::domain::catalog::WorkspaceCatalog &state, const AnalysisFilter &filter)
Computes adjusted transaction rows for one analysis definition.
AnalysisFilter parseAnalysisFilterSpec(const std::string &spec)
Parses a raw filter specification into predicate form.
void applyAnalysisPreviewOverrides(core::ports::workspace::WorkspaceSnapshot &workspace, const std::string &analysisId, bool includeCalculationAdjustments, const std::vector< std::pair< std::string, double > > &adjustments)
std::vector< std::pair< std::string, double > > parseAnalysisAdjustmentsJson(const std::string &adjustmentsJson)
std::string buildAnalysisConfigJson(const core::ports::analysis::AnalysisConfigInput &input)
core::domain::catalog::WorkspaceCatalog toWorkspaceCatalog(const core::ports::workspace::WorkspaceSnapshot &snapshot)
AnalysisFilterSelection parseAnalysisFilterSelection(const std::string &filterSpec)
Parses a serialized analysis filter specification.
std::string buildAnalysisFilterSpec(const AnalysisFilterSelection &selection)
Serializes an analysis filter selection.
std::vector< std::pair< std::string, double > > AnalysisAdjustmentAmounts
Adjustment amount pairs keyed by transaction id.
std::vector< AnalysisPreviewTransaction > transactions
std::vector< std::string > artifacts
std::vector< AnalysisTransaction > transactions
std::vector< std::vector< std::string > > table
std::vector< std::string > contractTypes
std::vector< AnalysisTablePropertyRow > propertyRows
std::vector< std::string > propertyNames