Skip to content

Commit ef7a0e4

Browse files
authored
Merge pull request #565 from vizzuhq/seriesdescriptor-handling-right
Refactor C++ SeriesIndex based on typescript SeriesDescriptor
2 parents 5b9bc88 + b7374b1 commit ef7a0e4

File tree

12 files changed

+99
-107
lines changed

12 files changed

+99
-107
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
## [Unreleased]
44

5-
### Fixed
5+
### Fixed
66

77
- Area charts with data series started with zero: tooltip fixed.
8+
- Series whose contained ',' and aggregated were not working properly.
89

910
## [0.12.0] - 2024-07-29
1011

src/apps/weblib/ts-api/module/cchart.ts

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,13 @@ import { CString, CFunction, CEventPtr } from '../cvizzu.types'
33
import * as Anim from '../types/anim.js'
44
import * as Config from '../types/config.js'
55
import * as Styles from '../types/styles.js'
6-
import * as Data from '../types/data.js'
76

87
import { CManagedObject, CObject, CEnv } from './cenv.js'
98
import { CPointerClosure } from './objregistry.js'
109
import { CProxy } from './cproxy.js'
1110
import { CCanvas } from './ccanvas.js'
1211
import { CAnimation } from './canimctrl.js'
1312

14-
import { isIterable } from '../utils.js'
15-
1613
/** Stored Chart object. */
1714
export class Snapshot extends CManagedObject {}
1815

@@ -136,42 +133,15 @@ export class CChart extends CManagedObject {
136133
this._wasm._chart_getList,
137134
this._wasm._chart_getValue,
138135
this._wasm._chart_setValue,
139-
(value: unknown): value is Record<string, unknown> =>
140-
isIterable(value) && !this._isSeriesDescriptor(value),
141-
(value: unknown): string => {
142-
// workaround: we should be able to pass series descriptor as two string
143-
if (this._isSeriesDescriptor(value)) {
144-
return value.aggregator ? `${value.aggregator}(${value.name})` : value.name
145-
} else {
146-
return String(value).toString()
147-
}
148-
},
149136
(path: string, value: string): unknown => {
150137
// workaround because channel.*.set returns already json instead of scalars
151138
if (path.startsWith('channels.') && path.endsWith('.set')) {
152-
return JSON.parse(value).map((v: string) => this._toSeriesDescriptor(v))
139+
return JSON.parse(value)
153140
} else return value
154141
}
155142
)
156143
}
157144

158-
private _toSeriesDescriptor(value: string): Data.SeriesDescriptor {
159-
const pattern = /^(\w+)\((.*?)\)$/
160-
const match = value.match(pattern)
161-
if (match) {
162-
return {
163-
name: match[2]!,
164-
aggregator: match[1]! as Data.AggregatorType
165-
}
166-
} else {
167-
return { name: value }
168-
}
169-
}
170-
171-
private _isSeriesDescriptor(value: unknown): value is Data.SeriesDescriptor {
172-
return typeof value === 'object' && value !== null && 'name' in value
173-
}
174-
175145
private _makeStyle(computed: boolean): CStyle {
176146
return new CStyle(
177147
this.getId,

src/apps/weblib/ts-api/module/cproxy.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CPointer, CString } from '../cvizzu.types'
22

33
import { CObject } from './cenv.js'
4-
import { mirrorObject, iterateObject, isIterable, type ShouldIterate } from '../utils.js'
4+
import { mirrorObject, iterateObject } from '../utils.js'
55
import { Mirrored } from '../tsutils.js'
66
import { CPointerClosure } from './objregistry.js'
77

@@ -13,8 +13,6 @@ export class CProxy<T> extends CObject {
1313
private _lister: Lister
1414
private _getter: Getter
1515
private _setter: Setter
16-
private _shouldIterate: ShouldIterate
17-
private _toString: (value: unknown) => string
1816
private _fromString: (path: string, str: string) => unknown
1917

2018
constructor(
@@ -23,21 +21,17 @@ export class CProxy<T> extends CObject {
2321
lister: Lister,
2422
getter: Getter,
2523
setter: Setter,
26-
shouldIterate?: ShouldIterate,
27-
toString?: (value: unknown) => string,
2824
fromString?: (path: string, str: string) => unknown
2925
) {
3026
super(getId, cenv)
3127
this._lister = lister
3228
this._getter = getter
3329
this._setter = setter
34-
this._shouldIterate = shouldIterate || isIterable
35-
this._toString = toString || ((value: unknown): string => String(value).toString())
3630
this._fromString = fromString || ((_path: string, str: string): unknown => str)
3731
}
3832

3933
set(value: T): void {
40-
iterateObject(value, this.setParam.bind(this), this._shouldIterate)
34+
iterateObject(value, this.setParam.bind(this))
4135
}
4236

4337
get(): Mirrored<T> {
@@ -64,7 +58,7 @@ export class CProxy<T> extends CObject {
6458

6559
setParam(path: string, value: unknown): void {
6660
const cpath = this._toCString(path)
67-
const cvalue = this._toCString(this._toString(value))
61+
const cvalue = this._toCString(String(value).toString())
6862
try {
6963
this._call(this._setter)(cpath, cvalue)
7064
} finally {

src/apps/weblib/ts-api/utils.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,18 @@ export function recursiveCopy<T>(value: T, Ignore?: new (...args: never[]) => un
2727
}
2828

2929
type Visitor = (path: string, value: unknown) => void
30-
export type ShouldIterate = (value: unknown) => value is Record<string, unknown>
3130

3231
export function isIterable(value: unknown): value is Record<string, unknown> {
3332
return typeof value === 'object' && value !== null
3433
}
3534

36-
export function iterateObject<T>(
37-
obj: T,
38-
paramHandler: Visitor,
39-
shouldIterate: ShouldIterate = isIterable,
40-
path: string = ''
41-
): void {
35+
export function iterateObject<T>(obj: T, paramHandler: Visitor, path: string = ''): void {
4236
if (obj && obj !== null && typeof obj === 'object') {
4337
Object.keys(obj).forEach((key) => {
4438
const newPath = path + (path.length === 0 ? '' : '.') + key
4539
const value = obj[key as keyof T]
46-
if (shouldIterate(value)) {
47-
iterateObject(value, paramHandler, shouldIterate, newPath)
40+
if (isIterable(value)) {
41+
iterateObject(value, paramHandler, newPath)
4842
} else {
4943
paramHandler(newPath, value)
5044
}

src/base/type/uniquelist.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ template <class T> class UniqueList
141141
return true;
142142
}
143143

144+
[[nodiscard]] T pop_back()
145+
{
146+
return extract(items.find(last->first)).key();
147+
}
148+
144149
[[nodiscard]] iterator<> begin() const noexcept
145150
{
146151
return {first};

src/chart/options/config.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,29 @@ void Config::setChannelParam(const std::string &path,
211211
if (parts.size() == 3 && value == "null")
212212
channel.reset();
213213
else {
214-
if (std::stoi(parts.at(3)) == 0) channel.reset();
215-
channel.addSeries({value, table});
214+
if (const std::string_view command =
215+
parts.size() == 4 ? std::string_view{"name"}
216+
: parts.at(4);
217+
command == "name") {
218+
if (std::stoi(parts.at(3)) == 0) channel.reset();
219+
if (value.empty()) { channel.measureId.emplace(); }
220+
else
221+
channel.addSeries({value, table});
222+
}
223+
else if (command == "aggregator") {
224+
if (value != "null") {
225+
if (!channel.measureId.has_value())
226+
channel.measureId.emplace(
227+
channel.dimensionIds.pop_back());
228+
229+
channel.measureId->setAggr(value);
230+
}
231+
else if (channel.measureId)
232+
channel.measureId->setAggr(
233+
channel.measureId->getColIndex().empty()
234+
? "count"
235+
: "sum");
236+
}
216237
}
217238
return;
218239
}
@@ -239,10 +260,8 @@ std::string Config::getChannelParam(const std::string &path) const
239260
if (property == "set") {
240261
std::string res;
241262
Conv::JSONArr arr{res};
242-
if (auto &&measure = channel.measureId)
243-
arr << measure->toString();
244-
for (auto &&dim : channel.dimensions())
245-
arr << dim.getColIndex();
263+
if (auto &&measure = channel.measureId) arr << *measure;
264+
for (auto &&dim : channel.dimensions()) arr << dim;
246265
return res;
247266
}
248267

src/dataframe/impl/data_source.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ enum class error_type {
2929
internal_error
3030
};
3131

32+
struct series_meta_t
33+
{
34+
std::string_view name;
35+
series_type type;
36+
};
37+
3238
[[maybe_unused]] [[noreturn]] void error(error_type err,
3339
std::string_view arg = {});
3440

src/dataframe/impl/dataframe.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,8 +722,7 @@ std::string dataframe::get_record_id_by_dims(std::size_t my_record,
722722
return get_data_source().get_id(my_record, dimensions);
723723
}
724724

725-
dataframe::series_meta_t dataframe::get_series_meta(
726-
const std::string &id) const
725+
series_meta_t dataframe::get_series_meta(const std::string &id) const
727726
{
728727
switch (auto &&ser = get_data_source().get_series(id)) {
729728
using enum series_type;

src/dataframe/impl/dataframe.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,6 @@ class dataframe
117117
[[nodiscard]] std::span<const std::string> get_categories(
118118
const std::string_view &dimension) const &;
119119

120-
struct series_meta_t
121-
{
122-
std::string_view name;
123-
series_type type;
124-
};
125-
126120
[[nodiscard]] series_meta_t get_series_meta(
127121
const std::string &id) const;
128122

src/dataframe/old/datatable.cpp

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "base/text/funcstring.h"
99
#include "chart/options/options.h"
1010
#include "dataframe/impl/aggregators.h"
11+
#include "dataframe/impl/data_source.h"
1112
#include "dataframe/interface.h"
1213

1314
namespace Vizzu::Data
@@ -53,29 +54,21 @@ const MultiIndex &DataCube::iterator_t::operator*() const
5354
return index;
5455
}
5556

57+
SeriesIndex::SeriesIndex(dataframe::series_meta_t const &meta) :
58+
name{meta.name}
59+
{
60+
if (meta.type == dataframe::series_type::measure)
61+
aggregator.emplace(dataframe::aggregator_type::sum);
62+
}
63+
5664
SeriesIndex::SeriesIndex(std::string const &str,
5765
const DataTable &table) :
58-
orig_name(str)
66+
SeriesIndex(table.getDf().get_series_meta(str))
67+
{}
68+
69+
void SeriesIndex::setAggr(const std::string &aggr)
5970
{
60-
constinit static auto names =
61-
Refl::get_names<dataframe::aggregator_type>();
62-
if (const Text::FuncString func(str, false);
63-
!func.isEmpty()
64-
&& std::find(names.begin(), names.end(), func.getName())
65-
!= names.end()) {
66-
aggr = Refl::get_enum<dataframe::aggregator_type>(
67-
func.getName());
68-
if (!func.getParams().empty())
69-
sid = table.getDf()
70-
.get_series_meta(func.getParams().at(0))
71-
.name;
72-
}
73-
else {
74-
auto &&[s, type] = table.getDf().get_series_meta(str);
75-
sid = s;
76-
if (type == DataTable::Type::measure)
77-
aggr = dataframe::aggregator_type::sum;
78-
}
71+
aggregator = Refl::get_enum<dataframe::aggregator_type>(aggr);
7972
}
8073

8174
DataCube::iterator_t DataCube::begin() const

src/dataframe/old/types.h

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77

88
#include "../interface.h"
99

10+
namespace Vizzu::dataframe
11+
{
12+
struct series_meta_t;
13+
}
14+
1015
namespace Vizzu::Data
1116
{
1217

@@ -27,38 +32,38 @@ struct RowWrapper
2732

2833
class SeriesIndex
2934
{
30-
std::string orig_name;
31-
std::string_view sid;
32-
std::optional<dataframe::aggregator_type> aggr;
35+
std::string_view name{};
36+
std::optional<dataframe::aggregator_type> aggregator;
37+
38+
explicit SeriesIndex(dataframe::series_meta_t const &meta);
3339

3440
public:
41+
SeriesIndex() : aggregator(dataframe::aggregator_type::count) {}
3542
SeriesIndex(std::string const &str, const DataTable &table);
3643

44+
void setAggr(const std::string &aggr);
45+
3746
[[nodiscard]] const dataframe::aggregator_type &getAggr() const
3847
{
39-
return *aggr;
48+
return *aggregator;
4049
}
4150

4251
[[nodiscard]] const std::string_view &getColIndex() const
4352
{
44-
return sid;
53+
return name;
4554
}
4655

47-
[[nodiscard]] bool operator==(const SeriesIndex &rhs) const
48-
{
49-
return sid == rhs.sid && aggr == rhs.aggr;
50-
}
51-
52-
[[nodiscard]] bool operator<(const SeriesIndex &rhs) const
53-
{
54-
return sid < rhs.sid || (sid == rhs.sid && aggr < rhs.aggr);
55-
}
56+
[[nodiscard]] bool operator==(
57+
const SeriesIndex &rhs) const = default;
58+
[[nodiscard]] auto operator<=>(
59+
const SeriesIndex &rhs) const = default;
5660

57-
[[nodiscard]] bool isDimension() const { return !aggr; }
61+
[[nodiscard]] bool isDimension() const { return !aggregator; }
5862

59-
[[nodiscard]] const std::string &toString() const
63+
[[nodiscard]] consteval static auto members()
6064
{
61-
return orig_name;
65+
return std::tuple{&SeriesIndex::name,
66+
&SeriesIndex::aggregator};
6267
}
6368
};
6469

0 commit comments

Comments
 (0)