FOSSredder 0.5.0
Loading...
Searching...
No Matches
AnalysisRequest.cpp
Go to the documentation of this file.
1
5
7
11
12#include <algorithm>
13#include <ctime>
14#include <sstream>
15#include <unordered_set>
16
17namespace core::ports::analysis {
18
19namespace {
20
23
24int defaultAnalysisYear() {
25 const std::time_t now = std::time(nullptr);
26 const std::tm *local = std::localtime(&now);
27 if (!local) {
28 return 2000;
29 }
30 return local->tm_year + 1900 - 1;
31}
32
33std::vector<std::string> splitList(const std::string &raw) {
34 std::vector<std::string> out;
35 std::unordered_set<std::string> seen;
36 std::string token;
37 std::istringstream ss(raw);
38 while (std::getline(ss, token, core::application::analysis::filterKeys::separators::kList)) {
39 const std::string trimmed = trim(token);
40 if (trimmed.empty() || seen.contains(trimmed)) {
41 continue;
42 }
43 seen.insert(trimmed);
44 out.push_back(trimmed);
45 }
46 return out;
47}
48
49} // namespace
50
52parseAnalysisFilterSelection(const std::string &filterSpec) {
54 out.year = std::to_string(defaultAnalysisYear());
55 const std::string normalized =
57 if (normalized.empty()) {
58 return out;
59 }
60
61 std::istringstream ss(normalized);
62 std::string clause;
63 while (
65 clause = trim(clause);
66 if (clause.empty()) {
67 continue;
68 }
69
70 if (clause.rfind(
73 0) == 0) {
74 out.dateFrom = clause.substr(6);
75 continue;
76 }
77 if (clause.rfind(
80 0) == 0) {
81 out.dateTo = clause.substr(6);
82 continue;
83 }
84 if (clause.rfind(
87 0) == 0) {
88 const std::string value = lowerAscii(trim(clause.substr(10)));
89 out.dateField = value == "valuta" ? "valuta" : "bookingDate";
90 continue;
91 }
92 if (clause.rfind(
95 0) == 0) {
96 const std::vector<std::string> values = splitList(clause.substr(11));
98 std::find(values.begin(), values.end(),
100 values.end();
101 out.propertyIds.clear();
102 for (const auto &value : values) {
104 out.propertyIds.push_back(value);
105 }
106 }
107 continue;
108 }
109 if (clause.rfind(
112 0) == 0) {
113 const std::vector<std::string> values = splitList(clause.substr(14));
115 std::find(values.begin(), values.end(),
117 values.end();
118 out.contractTypes.clear();
119 for (auto value : values) {
120 value = lowerAscii(value);
122 out.contractTypes.push_back(std::move(value));
123 }
124 }
125 continue;
126 }
127 if (clause.rfind(
130 0) == 0) {
131 const std::string mode = lowerAscii(trim(clause.substr(12)));
132 if (mode == "allocatable" || mode == "non-allocatable") {
133 out.allocatableMode = mode;
134 } else {
135 out.allocatableMode = "all";
136 }
137 continue;
138 }
139 }
140
141 if (out.dateFrom.size() == 10 && out.dateTo.size() == 10 &&
142 out.dateFrom.ends_with("-01-01") && out.dateTo.ends_with("-12-31") &&
143 out.dateFrom.substr(0, 4) == out.dateTo.substr(0, 4)) {
144 out.dateMode = "year";
145 out.year = out.dateFrom.substr(0, 4);
146 } else if (!out.dateFrom.empty() || !out.dateTo.empty()) {
147 out.dateMode = "range";
148 }
149
150 return out;
151}
152
154 std::vector<std::string> clauses;
155 const std::string normalizedDateField = lowerAscii(trim(selection.dateField));
156 if (normalizedDateField == "valuta") {
157 clauses.emplace_back("dateField=valuta");
158 }
159
160 const std::string normalizedDateMode = lowerAscii(trim(selection.dateMode));
161 const std::string year = trim(selection.year);
162 if (normalizedDateMode == "range") {
163 const std::string from = trim(selection.dateFrom);
164 const std::string to = trim(selection.dateTo);
165 bool hasDateClause = false;
166 if (!from.empty()) {
167 clauses.push_back("date>=" + from);
168 hasDateClause = true;
169 }
170 if (!to.empty()) {
171 clauses.push_back("date<=" + to);
172 hasDateClause = true;
173 }
174 if (!hasDateClause) {
175 const std::string fallbackYear = std::to_string(defaultAnalysisYear());
176 clauses.push_back("date>=" + fallbackYear + "-01-01");
177 clauses.push_back("date<=" + fallbackYear + "-12-31");
178 }
179 } else {
180 const std::string resolvedYear =
181 year.empty() ? std::to_string(defaultAnalysisYear()) : year;
182 clauses.push_back("date>=" + resolvedYear + "-01-01");
183 clauses.push_back("date<=" + resolvedYear + "-12-31");
184 }
185
186 std::unordered_set<std::string> propertySeen;
187 std::vector<std::string> propertyValues;
188 for (const auto &raw : selection.propertyIds) {
189 const std::string value = trim(raw);
190 if (value.empty() || propertySeen.contains(value)) {
191 continue;
192 }
193 propertySeen.insert(value);
194 propertyValues.push_back(value);
195 }
196 if (selection.propertyIdsUnassigned &&
197 !propertySeen.contains(
199 propertyValues.push_back(
201 }
202 if (!propertyValues.empty()) {
203 std::ostringstream propertyClause;
204 propertyClause << "propertyId=";
205 for (std::size_t i = 0; i < propertyValues.size(); ++i) {
206 if (i > 0) {
207 propertyClause << ',';
208 }
209 propertyClause << propertyValues[i];
210 }
211 clauses.push_back(propertyClause.str());
212 }
213
214 std::unordered_set<std::string> contractSeen;
215 std::vector<std::string> contractValues;
216 for (auto raw : selection.contractTypes) {
217 raw = lowerAscii(trim(raw));
218 if (raw.empty() || contractSeen.contains(raw)) {
219 continue;
220 }
221 contractSeen.insert(raw);
222 contractValues.push_back(raw);
223 }
224 if (selection.contractTypesUnassigned &&
225 !contractSeen.contains(
227 contractValues.push_back(
229 }
230 if (!contractValues.empty()) {
231 std::ostringstream contractClause;
232 contractClause << "contract.type=";
233 for (std::size_t i = 0; i < contractValues.size(); ++i) {
234 if (i > 0) {
235 contractClause << ',';
236 }
237 contractClause << contractValues[i];
238 }
239 clauses.push_back(contractClause.str());
240 }
241
242 const std::string allocatable = lowerAscii(trim(selection.allocatableMode));
243 if (allocatable == "allocatable" || allocatable == "non-allocatable") {
244 clauses.push_back("allocatable=" + allocatable);
245 }
246
247 std::ostringstream out;
248 for (std::size_t i = 0; i < clauses.size(); ++i) {
249 if (i > 0) {
250 out << ';';
251 }
252 out << clauses[i];
253 }
254 return core::domain::FilterSpec::normalize(out.str());
255}
256
257} // namespace core::ports::analysis
Defines stable analysis type, configuration, filter, and result keys.
Declares analysis use-case request DTOs and filter-spec helpers.
Value object for analysis filter specifications.
Declares private string helpers shared across core implementation files.
static std::string normalize(std::string value)
Trims leading and trailing whitespace from a filter specification.
constexpr std::string_view kDateField
constexpr std::string_view kContractType
constexpr std::string_view kDate
constexpr std::string_view kPropertyId
constexpr std::string_view kAllocatable
constexpr std::string_view kUnassigned
AnalysisFilterSelection parseAnalysisFilterSelection(const std::string &filterSpec)
Parses a serialized analysis filter specification.
std::string buildAnalysisFilterSpec(const AnalysisFilterSelection &selection)
Serializes an analysis filter selection.
std::string lowerAscii(std::string s)
Converts ASCII letters in a string to lowercase.
std::string trim(std::string s)
Removes leading and trailing ASCII whitespace from a string.
std::string lowerAscii(std::string s)
Converts ASCII letters in a string to lowercase.
std::string trim(std::string s)
Removes leading and trailing ASCII whitespace from a string.