Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Telemetry
KUserFeedback
Commits
85b81bd8
Commit
85b81bd8
authored
Oct 01, 2016
by
Volker Krause
Browse files
First draft of the new schema API/data model
Mostly non-functional still though.
parent
95df1d41
Changes
12
Hide whitespace changes
Inline
Side-by-side
analyzer/CMakeLists.txt
View file @
85b81bd8
set
(
analyzer_lib_srcs
core/product.cpp
core/schemaentry.cpp
core/schemaentryelement.cpp
rest/restapi.cpp
rest/restclient.cpp
...
...
analyzer/core/schemaentry.cpp
View file @
85b81bd8
...
...
@@ -16,7 +16,10 @@
*/
#include "schemaentry.h"
#include "schemaentryelement.h"
#include "util.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QObject>
...
...
@@ -30,10 +33,23 @@ namespace Analyzer {
class
SchemaEntryData
:
public
QSharedData
{
public:
QString
name
;
int
internalId
=
-
1
;
SchemaEntry
::
Type
type
=
SchemaEntry
::
StringType
;
SchemaEntry
::
DataType
dataType
=
SchemaEntry
::
Scalar
;
SchemaEntry
::
AggregationType
aggregation
=
SchemaEntry
::
None
;
QVector
<
SchemaEntryElement
>
elements
;
};
static
const
struct
{
SchemaEntry
::
AggregationType
type
;
const
char
*
name
;
}
aggregation_types_table
[]
{
{
SchemaEntry
::
None
,
"none"
},
{
SchemaEntry
::
Category
,
"category"
},
{
SchemaEntry
::
RatioSet
,
"ratio_set"
},
{
SchemaEntry
::
Numeric
,
"numeric"
},
{
SchemaEntry
::
XY
,
"xy"
}
};
}
...
...
@@ -48,7 +64,10 @@ bool SchemaEntry::operator==(const SchemaEntry &other) const
{
return
d
->
name
==
other
.
d
->
name
&&
d
->
internalId
==
other
.
d
->
internalId
&&
d
->
type
==
other
.
d
->
internalId
;
&&
d
->
type
==
other
.
d
->
type
&&
d
->
dataType
==
other
.
d
->
dataType
&&
d
->
aggregation
==
other
.
d
->
aggregation
&&
d
->
elements
==
other
.
d
->
elements
;
}
int
SchemaEntry
::
internalId
()
const
...
...
@@ -81,6 +100,36 @@ void SchemaEntry::setType(SchemaEntry::Type type)
d
->
type
=
type
;
}
SchemaEntry
::
DataType
SchemaEntry
::
dataType
()
const
{
return
d
->
dataType
;
}
void
SchemaEntry
::
setDataType
(
SchemaEntry
::
DataType
type
)
{
d
->
dataType
=
type
;
}
SchemaEntry
::
AggregationType
SchemaEntry
::
aggregationType
()
const
{
return
d
->
aggregation
;
}
void
SchemaEntry
::
setAggregationType
(
SchemaEntry
::
AggregationType
type
)
{
d
->
aggregation
=
type
;
}
QVector
<
SchemaEntryElement
>
SchemaEntry
::
elements
()
const
{
return
d
->
elements
;
}
void
SchemaEntry
::
setElements
(
const
QVector
<
SchemaEntryElement
>
&
elements
)
{
d
->
elements
=
elements
;
}
QString
SchemaEntry
::
displayString
(
SchemaEntry
::
Type
type
)
{
switch
(
type
)
{
...
...
@@ -110,6 +159,12 @@ QJsonObject SchemaEntry::toJsonObject() const
case
RatioSetType
:
t
=
QStringLiteral
(
"ratio_set"
);
break
;
}
obj
.
insert
(
QStringLiteral
(
"type"
),
t
);
obj
.
insert
(
QStringLiteral
(
"aggregation"
),
QLatin1String
(
aggregation_types_table
[
d
->
aggregation
].
name
));
QJsonArray
array
;
for
(
const
auto
&
element
:
qAsConst
(
d
->
elements
))
array
.
push_back
(
element
.
toJsonObject
());
obj
.
insert
(
QStringLiteral
(
"elements"
),
array
);
return
obj
;
}
...
...
@@ -122,7 +177,7 @@ QVector<SchemaEntry> SchemaEntry::fromJson(const QJsonArray &array)
const
auto
obj
=
v
.
toObject
();
SchemaEntry
entry
;
entry
.
setName
(
obj
.
value
(
QStringLiteral
(
"name"
)).
toString
());
const
auto
t
=
obj
.
value
(
QStringLiteral
(
"type"
)).
toString
();
auto
t
=
obj
.
value
(
QStringLiteral
(
"type"
)).
toString
();
if
(
t
==
QStringLiteral
(
"string"
))
entry
.
setType
(
StringType
);
else
if
(
t
==
QStringLiteral
(
"int"
))
...
...
@@ -131,6 +186,9 @@ QVector<SchemaEntry> SchemaEntry::fromJson(const QJsonArray &array)
entry
.
setType
(
StringListType
);
else
if
(
t
==
QStringLiteral
(
"ratio_set"
))
entry
.
setType
(
RatioSetType
);
entry
.
setAggregationType
(
Util
::
stringToEnum
<
AggregationType
>
(
obj
.
value
(
QLatin1String
(
"aggregation"
)).
toString
(),
aggregation_types_table
));
entry
.
setElements
(
SchemaEntryElement
::
fromJson
(
obj
.
value
(
QLatin1String
(
"elements"
)).
toArray
()));
res
.
push_back
(
entry
);
}
...
...
analyzer/core/schemaentry.h
View file @
85b81bd8
...
...
@@ -29,6 +29,7 @@ namespace UserFeedback {
namespace
Analyzer
{
class
SchemaEntryData
;
class
SchemaEntryElement
;
/** Represents one schema entry.
* A schema entry can be a scalar, a list or a map, and consists of one or
...
...
@@ -58,6 +59,22 @@ class SchemaEntry
{
Q_GADGET
public:
enum
DataType
{
Scalar
,
List
,
Map
};
Q_ENUM
(
DataType
)
enum
AggregationType
{
None
,
Category
,
RatioSet
,
Numeric
,
XY
};
Q_ENUM
(
AggregationType
)
enum
Type
{
InvalidType
,
IntegerType
,
...
...
@@ -83,6 +100,15 @@ public:
Type
type
()
const
;
void
setType
(
Type
type
);
AggregationType
aggregationType
()
const
;
void
setAggregationType
(
AggregationType
type
);
DataType
dataType
()
const
;
void
setDataType
(
DataType
type
);
QVector
<
SchemaEntryElement
>
elements
()
const
;
void
setElements
(
const
QVector
<
SchemaEntryElement
>
&
elements
);
static
QString
displayString
(
Type
type
);
QJsonObject
toJsonObject
()
const
;
...
...
analyzer/core/schemaentryelement.cpp
0 → 100644
View file @
85b81bd8
/*
Copyright (C) 2016 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "schemaentryelement.h"
#include "util.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QSharedData>
#include <QVector>
using
namespace
UserFeedback
::
Analyzer
;
namespace
UserFeedback
{
namespace
Analyzer
{
class
SchemaEntryElementData
:
public
QSharedData
{
public:
QString
name
;
SchemaEntryElement
::
Type
type
=
SchemaEntryElement
::
String
;
};
static
const
struct
{
SchemaEntryElement
::
Type
type
;
const
char
*
name
;
}
element_type_table
[]
{
{
SchemaEntryElement
::
Integer
,
"int"
},
{
SchemaEntryElement
::
Number
,
"number"
},
{
SchemaEntryElement
::
String
,
"string"
},
{
SchemaEntryElement
::
Boolean
,
"bool"
}
};
}
}
SchemaEntryElement
::
SchemaEntryElement
()
:
d
(
new
SchemaEntryElementData
)
{
}
SchemaEntryElement
::
SchemaEntryElement
(
const
SchemaEntryElement
&
)
=
default
;
SchemaEntryElement
::~
SchemaEntryElement
()
=
default
;
SchemaEntryElement
&
SchemaEntryElement
::
operator
=
(
const
SchemaEntryElement
&
)
=
default
;
bool
SchemaEntryElement
::
operator
==
(
const
SchemaEntryElement
&
other
)
const
{
return
d
->
name
==
other
.
d
->
name
&&
d
->
type
==
other
.
d
->
type
;
}
QString
SchemaEntryElement
::
name
()
const
{
return
d
->
name
;
}
void
SchemaEntryElement
::
setName
(
const
QString
&
name
)
{
d
->
name
=
name
;
}
SchemaEntryElement
::
Type
SchemaEntryElement
::
type
()
const
{
return
d
->
type
;
}
void
SchemaEntryElement
::
setType
(
SchemaEntryElement
::
Type
type
)
{
d
->
type
=
type
;
}
QJsonObject
SchemaEntryElement
::
toJsonObject
()
const
{
QJsonObject
obj
;
obj
.
insert
(
QStringLiteral
(
"name"
),
d
->
name
);
obj
.
insert
(
QStringLiteral
(
"type"
),
QLatin1String
(
element_type_table
[
d
->
type
].
name
));
return
obj
;
}
QVector
<
SchemaEntryElement
>
SchemaEntryElement
::
fromJson
(
const
QJsonArray
&
array
)
{
QVector
<
SchemaEntryElement
>
res
;
res
.
reserve
(
array
.
size
());
foreach
(
const
auto
&
v
,
array
)
{
const
auto
obj
=
v
.
toObject
();
SchemaEntryElement
e
;
e
.
setName
(
obj
.
value
(
QLatin1String
(
"name"
)).
toString
());
e
.
setType
(
Util
::
stringToEnum
<
SchemaEntryElement
::
Type
>
(
obj
.
value
(
QLatin1String
(
"type"
)).
toString
(),
element_type_table
));
res
.
push_back
(
e
);
}
return
res
;
}
analyzer/core/schemaentryelement.h
0 → 100644
View file @
85b81bd8
/*
Copyright (C) 2016 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef USERFEEDBACK_ANALYZER_SCHEMAENTRYELEMENT_H
#define USERFEEDBACK_ANALYZER_SCHEMAENTRYELEMENT_H
#include <qobjectdefs.h>
#include <QMetaType>
#include <QSharedDataPointer>
QT_BEGIN_NAMESPACE
class
QJsonArray
;
class
QJsonObject
;
template
<
typename
T
>
class
QVector
;
QT_END_NAMESPACE
namespace
UserFeedback
{
namespace
Analyzer
{
class
SchemaEntryElementData
;
/** One element in a SchemaEntry. */
class
SchemaEntryElement
{
Q_GADGET
public:
enum
Type
{
Integer
,
Number
,
String
,
Boolean
};
Q_ENUM
(
Type
)
SchemaEntryElement
();
SchemaEntryElement
(
const
SchemaEntryElement
&
other
);
~
SchemaEntryElement
();
SchemaEntryElement
&
operator
=
(
const
SchemaEntryElement
&
other
);
bool
operator
==
(
const
SchemaEntryElement
&
other
)
const
;
QString
name
()
const
;
void
setName
(
const
QString
&
name
);
Type
type
()
const
;
void
setType
(
Type
type
);
QJsonObject
toJsonObject
()
const
;
static
QVector
<
SchemaEntryElement
>
fromJson
(
const
QJsonArray
&
array
);
private:
QSharedDataPointer
<
SchemaEntryElementData
>
d
;
};
}
}
Q_DECLARE_METATYPE
(
UserFeedback
::
Analyzer
::
SchemaEntryElement
)
Q_DECLARE_METATYPE
(
UserFeedback
::
Analyzer
::
SchemaEntryElement
::
Type
)
Q_DECLARE_TYPEINFO
(
UserFeedback
::
Analyzer
::
SchemaEntryElement
,
Q_MOVABLE_TYPE
);
#endif // USERFEEDBACK_ANALYZER_SCHEMAENTRYELEMENT_H
analyzer/core/util.h
0 → 100644
View file @
85b81bd8
/*
Copyright (C) 2016 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef USERFEEDBACK_ANALYZER_UTIL_H
#define USERFEEDBACK_ANALYZER_UTIL_H
class
QString
;
namespace
UserFeedback
{
namespace
Analyzer
{
namespace
Util
{
template
<
typename
Enum
,
typename
Table
,
std
::
size_t
N
>
Enum
stringToEnum
(
const
QString
&
s
,
const
Table
(
&
lookupTable
)[
N
])
{
for
(
std
::
size_t
i
=
0
;
i
<
N
;
++
i
)
{
if
(
s
==
QLatin1String
(
lookupTable
[
i
].
name
))
return
lookupTable
[
i
].
type
;
}
return
Enum
();
}
}
}
}
#endif // USERFEEDBACK_ANALYZER_UTIL_H
analyzer/model/schemamodel.cpp
View file @
85b81bd8
...
...
@@ -17,10 +17,16 @@
#include "schemamodel.h"
#include <core/schemaentryelement.h>
#include <limits>
using
namespace
UserFeedback
::
Analyzer
;
static
const
auto
TOPLEVEL
=
std
::
numeric_limits
<
quintptr
>::
max
();
SchemaModel
::
SchemaModel
(
QObject
*
parent
)
:
QAbstract
Table
Model
(
parent
)
QAbstract
Item
Model
(
parent
)
{
}
...
...
@@ -61,19 +67,21 @@ void SchemaModel::deleteEntry(int row)
int
SchemaModel
::
columnCount
(
const
QModelIndex
&
parent
)
const
{
Q_UNUSED
(
parent
);
return
2
;
return
3
;
}
int
SchemaModel
::
rowCount
(
const
QModelIndex
&
parent
)
const
{
if
(
parent
.
isValid
())
return
0
;
return
m_product
.
schema
().
size
();
if
(
!
parent
.
isValid
())
return
m_product
.
schema
().
size
();
if
(
parent
.
internalId
()
==
TOPLEVEL
)
return
m_product
.
schema
().
at
(
parent
.
row
()).
elements
().
size
();
return
0
;
}
QVariant
SchemaModel
::
data
(
const
QModelIndex
&
index
,
int
role
)
const
{
if
(
!
index
.
isValid
())
if
(
!
index
.
isValid
()
||
index
.
internalId
()
!=
TOPLEVEL
)
return
{};
switch
(
index
.
column
())
{
...
...
@@ -87,6 +95,9 @@ QVariant SchemaModel::data(const QModelIndex& index, int role) const
if
(
role
==
Qt
::
EditRole
)
return
QVariant
::
fromValue
(
m_product
.
schema
().
at
(
index
.
row
()).
type
());
break
;
case
2
:
if
(
role
==
Qt
::
DisplayRole
)
return
m_product
.
schema
().
at
(
index
.
row
()).
aggregationType
();
}
return
{};
...
...
@@ -98,14 +109,15 @@ QVariant SchemaModel::headerData(int section, Qt::Orientation orientation, int r
switch
(
section
)
{
case
0
:
return
tr
(
"Name"
);
case
1
:
return
tr
(
"Type"
);
case
2
:
return
tr
(
"Aggregation"
);
}
}
return
QAbstract
Table
Model
::
headerData
(
section
,
orientation
,
role
);
return
QAbstract
Item
Model
::
headerData
(
section
,
orientation
,
role
);
}
Qt
::
ItemFlags
SchemaModel
::
flags
(
const
QModelIndex
&
index
)
const
{
const
auto
baseFlags
=
QAbstract
Table
Model
::
flags
(
index
);
const
auto
baseFlags
=
QAbstract
Item
Model
::
flags
(
index
);
if
(
index
.
isValid
())
return
baseFlags
|
Qt
::
ItemIsEditable
;
return
baseFlags
;
...
...
@@ -133,3 +145,21 @@ bool SchemaModel::setData(const QModelIndex &index, const QVariant &value, int r
emit
dataChanged
(
index
,
index
);
return
false
;
}
QModelIndex
SchemaModel
::
index
(
int
row
,
int
column
,
const
QModelIndex
&
parent
)
const
{
if
(
!
hasIndex
(
row
,
column
,
parent
))
return
{};
if
(
!
parent
.
isValid
())
return
createIndex
(
row
,
column
,
TOPLEVEL
);
if
(
parent
.
internalId
()
==
TOPLEVEL
)
return
createIndex
(
row
,
column
,
parent
.
row
());
return
{};
}
QModelIndex
SchemaModel
::
parent
(
const
QModelIndex
&
index
)
const
{
if
(
!
index
.
isValid
()
||
index
.
internalId
()
==
TOPLEVEL
)
return
{};
return
createIndex
(
index
.
internalId
(),
0
);
}
analyzer/model/schemamodel.h
View file @
85b81bd8
...
...
@@ -27,7 +27,7 @@ namespace Analyzer {
class
Product
;
class
SchemaModel
:
public
QAbstract
Table
Model
class
SchemaModel
:
public
QAbstract
Item
Model
{
Q_OBJECT
public:
...
...
@@ -47,6 +47,9 @@ public:
Qt
::
ItemFlags
flags
(
const
QModelIndex
&
index
)
const
override
;
bool
setData
(
const
QModelIndex
&
index
,
const
QVariant
&
value
,
int
role
=
Qt
::
EditRole
)
override
;
QModelIndex
index
(
int
row
,
int
column
,
const
QModelIndex
&
parent
=
QModelIndex
())
const
override
;
QModelIndex
parent
(
const
QModelIndex
&
index
)
const
override
;
private:
Product
m_product
;
};
...
...
analyzer/schemaeditwidget.ui
View file @
85b81bd8
...
...
@@ -24,7 +24,7 @@
<number>
0
</number>
</property>
<item>
<widget
class=
"QT
abl
eView"
name=
"schemaView"
>
<widget
class=
"QT
re
eView"
name=
"schemaView"
>
<property
name=
"selectionMode"
>
<enum>
QAbstractItemView::SingleSelection
</enum>
</property>
...
...
server/shared/datastore.php
View file @
85b81bd8
...
...
@@ -171,6 +171,7 @@ public function productSchema($productId)
$entry
=
array
();
$entry
[
'name'
]
=
$row
[
'name'
];
$entry
[
'type'
]
=
$row
[
'type'
];
$entry
[
'aggregation'
]
=
$row
[
'aggregation'
];
array_push
(
$schema
,
$entry
);
}
return
$schema
;
...
...
@@ -192,17 +193,18 @@ public function updateProductSchema($product, $schema)
die
(
'Cannot change data type of entry '
.
$entry
[
'name'
]
.
'!'
);
$res
=
$this
->
db
->
exec
(
'UPDATE product_schema SET '
.
'
type
= '
.
$this
->
db
->
quote
(
$entry
[
'
type
'
])
.
' WHERE '
.
'
aggregation
= '
.
$this
->
db
->
quote
(
$entry
[
'
aggregation
'
])
.
' WHERE '
.
'productId = '
.
intval
(
$product
[
'id'
])
.
' AND '
.
'name = '
.
$this
->
db
->
quote
(
$entry
[
'name'
])
);
$this
->
checkError
(
$res
);
}
else
{
// insert
$res
=
$this
->
db
->
exec
(
'INSERT INTO product_schema (productId, name, type) VALUES ('
.
$res
=
$this
->
db
->
exec
(
'INSERT INTO product_schema (productId, name, type
, aggregation
) VALUES ('
.
intval
(
$product
[
'id'
])
.
', '
.
$this
->
db
->
quote
(
$entry
[
'name'
])
.
', '
.
$this
->
db
->
quote
(
$entry
[
'type'
])
.
')'
$this
->
db
->
quote
(
$entry
[
'type'
])
.
', '
.
$this
->
db
->
quote
(
$entry
[
'aggregation'
])
.
')'
);
$this
->
checkError
(
$res
);
...
...
server/shared/schema.json
View file @
85b81bd8
...
...
@@ -23,6 +23,11 @@
{
"version"
:
5
,
"sql"
:
[
"CREATE TABLE product_schema (id INTEGER PRIMARY KEY AUTOINCREMENT, productId INTEGER REFERENCES products (id), name VARCHAR, type VARCHAR)"
]
},
{
"version"
:
6
,
"sql"
:
[
"ALTER TABLE product_schema ADD COLUMN aggregation VARCHAR"
]
}
]
}
tests/auto/productapitest.cpp
View file @
85b81bd8
...
...
@@ -85,17 +85,26 @@ private slots:
RESTClient
client
;
client
.
connectToServer
(
testServer
());
QVERIFY
(
client
.
isConnected
());
Product
newProduct
;
newProduct
.
setName
(
QStringLiteral
(
"org.kde.UserFeedback.UnitTestProduct"
));
// clean up from previous failed runs, if needed
auto
reply
=
RESTApi
::
deleteProduct
(
&
client
,
newProduct
);
waitForFinished
(
reply
);
// list existing products
auto
reply
=
RESTApi
::
getProducts
(
&
client
);
reply
=
RESTApi
::
getProducts
(
&
client
);
QVERIFY
(
waitForFinished
(
reply
));
QCOMPARE
(
reply
->
error
(),
QNetworkReply
::
NoError
);
const
auto
products
=
Product
::
fromJson
(
reply
->
readAll
());
QVERIFY
(
!
findProduct
(
products
,
QLatin1String
(
"org.kde.UserFeedback.UnitTestProduct"
)).
isValid
());
// add new product
Product
newProduct
;
newProduct
.
setName
(
QStringLiteral
(
"org.kde.UserFeedback.UnitTestProduct"
));
SchemaEntry
entry
;
entry
.
setName
(
QStringLiteral
(
"entry1"
));
entry
.
setType
(
SchemaEntry
::
StringType
);
entry
.
setAggregationType
(
SchemaEntry
::
Category
);
newProduct
.
setSchema
({
entry
});
QVERIFY
(
newProduct
.
isValid
());
reply
=
RESTApi
::
createProduct
(
&
client
,
newProduct
);
QVERIFY
(
waitForFinished
(
reply
));
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment