[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [krecipes] src/api_v2_design: Add draft for a new API
From: José_Manuel_Santamaría_Lema <panfaust () gmail ! com>
Date: 2016-04-07 20:25:06
Message-ID: E1aoGUE-0007O4-Sp () scm ! kde ! org
[Download RAW message or body]
Git commit df1e1822762d9be62944c1d7031794cb0d80f0e9 by José Manuel \
Santamaría Lema. Committed on 07/04/2016 at 06:31.
Pushed by joselema into branch 'master'.
Add draft for a new API
A +198 -0 src/api_v2_design/README
A +73 -0 src/api_v2_design/kreelements.h [License: UNKNOWN] *
A +76 -0 src/api_v2_design/kremodels.h [License: UNKNOWN] *
The files marked with a * at the end have a non valid license. Please read: \
http://techbase.kde.org/Policies/Licensing_Policy and use the headers which \
are listed at that page.
http://commits.kde.org/krecipes/df1e1822762d9be62944c1d7031794cb0d80f0e9
diff --git a/src/api_v2_design/README b/src/api_v2_design/README
new file mode 100644
index 0000000..2daec6a
--- /dev/null
+++ b/src/api_v2_design/README
@@ -0,0 +1,198 @@
+------------------------------
+Krecipes API and design issues
+------------------------------
+
+There are various flaws in the Krecipes API, i.e. the classes inside these
+directories:
+- src/datablocks/
+- src/backends/
+- src/importers/
+- src/exporters/
+
+List of issues:
+
+#1 The handling of database Id's:
+ - The ids are int's or, in the best case of type RecipeDB::IdType, \
which is a + typedef for int. According to the SQLite documentation the \
numbers used for + primary keys are 64-bit signed integers, so, if I'm \
not mistaken it's not + guaranteed that the database id will fit in the \
int. + - Also having the database Id's as numbers makes more difficult to \
handle + foreign keys which are set to NULL. Usually this is handled as \
-1 or + RecipeDB::InvalidId (which is -1), not only in the program \
logic, but also + on the database itself; sometimes external keys are \
stored as -1 while + actually they should be stored as NULL
+
+#2 Classes inside src/datablocks doesn't have getters or setters except \
for Unit +
+#3 The class Ingredient in src/datablocks is not a class to represent a \
row in + the 'ingredients' SQL table; it's a class to represent an \
ingredient entry + for a recipe. To represent a row in the ingredients \
table the class Element. + This various issues:
+ - It's confusing
+ - It's difficult to reference ingredient subsitutes (see the Ingredient \
vs + IngredientData hack to have a list of substitutes)
+ - As mentioned the class Element is used to represent a row of the
+ ingredients table, consider this RecipeDB signal:
+ void ingredientCreated( const Element & );
+ So what happens if I want to change the program and database schema \
to + support singular/plural names? I would have to create a new class \
to + represent a row in the 'ingredients' table, let's call it \
'IngredientRow' + and change the api of RecipeDB changing the signal \
mentioned above like + this:
+ void ingredientCreated( const IngredientRow & );
+ It would have been better to have a class to represent row from the \
table + 'ingredients' (even if it were an empty subclass of the \
'Element' class). +
+#4 Right now, Krecipes has an option to limit the number of elements to \
show; + configuring this option has this effect: program dialogs shown \
when + clicking "Data..." in the left panel display the items in a \
"paged" fashion; + a couple of buttons appear in the bottom of the \
QTreeView to go to the next + page or the previous page.
+ However this is kind of futile, because when we edit a recipe we
+ are reading all the ingredients, units and preparation methods of the
+ database for autocompletion objects and comboboxes in the recipe \
editor. + So maybe it would be nice to have something to load all the \
external elements + needed for a recipe just once when the program starts \
and then use this + elements.
+ Viewing the things under "Data..." in a paged fashion has also other \
issues: + - When creating a new element there is no other way to update \
the QTreeView + properly than reloading the current "page": when we \
insert a new element, + for example, there is no way to know properly \
the insertion point because + the sorting algorigthm of the database \
and the sorting algorithm of + QSortFilterProxyModel could differ. This \
means that when we import recipes + from a file and we need to create \
new ingredients/authors/whatever we + reload the "Data..." -> \
Ingredients/Authors/whatever QTreeView. + - Since the QTreeview's under \
the "Data..." are using a model containing only + the elements of the \
current page the search text box returns the matches + only for the \
current page and not for the whole database (which would be + more \
appropiate). +
+#5 Viewing/exporting recipes without saving them in the database:
+ See this bug report: https://bugs.kde.org/show_bug.cgi?id=292099
+ Right now, there is no way to display a recipe wihtout saving it; if \
you edit + a recipe and you click "Show Recipe" you will be asked to save \
the recipe in + the database before displaying it; if you don't save, you \
won't be able to + view the modified recipe.
+ To view a recipe it's exported to HTML by the "XSLT exporter" to a \
temporary + directory under "/tmp" and then the resulting HTML is \
displayed in a KHTML + part.
+ So maybe it would be nice to load into memory everything we would need \
to + export a recipe so the exporters would take a recipe object without
+ accessing the database at all.
+
+#6 Use of QLinkedList: sometimes QLinkedList is used to transfer data from \
the + database, this is because sometimes we need persistent iterators to \
populate + some classes from 'src/datablocks' with data, see for instance \
the log of + the commit e5ceac80bdd0c9ac4e4d5178f98345c17021b4f1
+ "RecipeList should be a subclass of QLinkedList because in
+ QSqlRecipeDB::loadRecipes method we are keeping iterators to \
RecipeList + elements in recipeIterators hasmap after doing non-const \
operations against + the RecipeList. This is wrong if RecipeList is a \
QList but fine if + RecipeList is a QLinkedList.
+ This commit fixes some random segfaults when showing recipes and when \
using + the ingredient matcher."
+
+
+----------------------------------------
+New API design to solve the above issues
+----------------------------------------
+
+* Solution for #1: use QVariant's to store id's. This way we will be able \
to + manage NULL foreign keys with QVariant::isNull() and also it will be
+ guaranteed that the integer type used by the DBMS for id's will fit in \
our id + variables.
+
+* Solution for #2: make getters and setters in the new classes instead of \
having + public data members.
+
+* Solution for #3: create two separate classes:
+ - KreIngredient to represent an ingredient object i.e. a row in the
+ 'ingredients' SQL table
+ - KreRecipeIngredientEntry to represent an ingredient entry of a recipe; \
a + recipe would have from 0 to n ingredient entries.
+
+* Solution for #4 and #5: keep some global models in RecipeDB; this models
+ would be updated automatically with changes in the database connecting
+ themselves against RecipeDB. For instance we could have a class named
+ KreAllIngredientsModel providing a set of models which would be \
populated + by RecipeDB and updated connecting to recipeDB signals. \
Regarding the issue + #5 "Viewing/exporting recipes without saving them in \
the database" see below + the section "Data needed to edit/view/export a \
recipe without saving it" for + more details. Also this global models \
could be used as a cache for the + database.
+ - NOTE 1: if we have a QStandardItemModel for all ingredients with 2 \
columns: + id and ingredient name we could filter out the id one for \
comboboxes using + this technique:
+ https://wiki.qt.io/Filter_Columns_in_a_QFileSystemModel*
+ - NOTE 2: To transfer data from the database QSqlRecipeDB will use \
either: + a) dtos or QLists of dtos
+ b) models such as QStandardItemModel or KCompletion objects
+ For instance, to read all the ingredients from the database we will \
use the + approach b) for efficiency reasons. However to load the needed \
data to edit + a recipe we will use the the approach b) and then we will \
use the data from + the recipe DTO (a KreRecipe object) to populate \
RecipeGeneralInfoEditor or + the model of IngredientsEditor.
+
+* Solution for #6: Use QList instead of QLinkedList and avoid the use of
+ non-const iterators to alter lists as much as possible. Probably, this \
will + reduce the number of mallocs when reading stuff from the database.
+
+
+------------------------
+Mockup of the new design
+------------------------
+
+* Inside this directory there is a file called 'datablocks_v2.h' with some
+ drafts of the classes meant to replace the current stuff in \
'src/datablocks'. +* There is also a file called 'api_v2_models.h' with \
some drafs of the model + classes meant to be filled by RecipeDB and used \
all over the program when + needed.
+* The classes in 'datablocks_v2.h' would be located in 'src/dtos/'. DTO \
means + "Data Transfer Object"
+* The classes in 'api_v2_models.h' would be located mostly in \
'src/models/' +
+
+----------------------------------------------------------
+Data needed to edit/view/export a recipe without saving it
+----------------------------------------------------------
+
+A recipe would be represented by the KreRecipe class; this class should be
+populated with all the "external data" needed to edit/view/export a \
recipe. +
+The table below lists the "external data" needed and where we could get \
it. All +that data should be stored in the recipe DTO (the KreRecipe \
class). +
++--------------------------------------------------------------------------------------+
+| DATA NEEDED | WE COULD GET IT FROM: \
| ++--------------------------------------------------------------------------------------+
+| Name of the ingredients used | KreAllIngredientsModels global \
object | ++--------------------------------------------------------------------------------------+
+| Name of the headers used | KreAllHeadersModels global object \
| ++--------------------------------------------------------------------------------------+
+| Name singular/plural/abbreviations | KreAllUnitsModels global object \
| +| of the units used | \
| ++--------------------------------------------------------------------------------------+
+| Name of the preparation methods | KreAllPrepMethodsModels global \
object | ++--------------------------------------------------------------------------------------+
+| Properties of the ingredients used | The database: when viewing/editing \
a recipe we | +| | should have a map \
indexed by | +| | \
ingredient id like this: | +| \
| QHash<QVariant,KrePropertiesForIngredientModel> | \
++--------------------------------------------------------------------------------------+
+| Weights of the ingredients used | The database: when viewing/editing \
a recipe we | +| | should have a map \
indexed by | +| | \
ingredient id like this: | +| \
| QHash<QVariant,KreWeightsForIngredientModel> | \
++--------------------------------------------------------------------------------------+
+| Unit conversions | KreUnitConversionsModel global \
object | ++--------------------------------------------------------------------------------------+
+| Category names | KreAllRecipesTreeModel global \
object | ++--------------------------------------------------------------------------------------+
+| Names of the authors | KreAllAuthorsModels global object \
| ++--------------------------------------------------------------------------------------+
+| Ratings | The database \
| ++--------------------------------------------------------------------------------------+
+| Yield type | KreAllYieldsTypesModels global \
object | ++--------------------------------------------------------------------------------------+
+
+
diff --git a/src/api_v2_design/kreelements.h \
b/src/api_v2_design/kreelements.h new file mode 100644
index 0000000..3222d95
--- /dev/null
+++ b/src/api_v2_design/kreelements.h
@@ -0,0 +1,73 @@
+class KreElement {
+ public:
+ KreElement();
+ KreElement( const QVariant & id, const QString & name );
+
+ QVariant id() const;
+ void setId( const QVariant & id );
+
+ QString name() const;
+ void setName( const QString & name );
+
+ private:
+ QVariant id;
+ QVariant name;
+};
+
+class KreIngredient: public KreElement {
+ public:
+ KreIngredient();
+ KreIngredient( const QVariant & id, const QString & name );
+}
+
+class KreIngHeader: public KreElement {
+ public
+ KreIngHeader();
+ KreIngHeader( const QVariant & id, const QString & name );
+}
+
+class KreUnit: public KreElement {
+ ...
+}
+
+class KreIngredientPropertyEntry {
+ public:
+ KrePropertyEntry();
+
+ QVariant propertyId();
+ void setPropertyId( const QVariant & propertyId );
+
+ double amount();
+ void setAmount( double amount );
+
+ QVariant unitId()
+ void setUnitId( const QVariant & unitId );
+
+ private:
+ ...
+}
+
+class KreRecipeIngredientEntry {
+ public:
+ KreIngredientEntry();
+ KreIngredientEntry( const KreIngredient & ingredient,
+ const KreMixedNumberRange & amount,
+ const KreUnit & unit,
+ const QList<KrePrepMethod> & prepMethods,
+ const QList<KreIngredient> & substitutesList = QList<KreIngredient>(),
+ const KreIngHeader & ingHeader = KreIngHeader() );
+ ...
+}
+
+class KreRecipe: public KreElement {
+ public:
+ ...
+ QList<KreIngredientEntry> ingredientEntryList() const;
+ void addIngredientEntry( const KreIngredientEntry & ingredientEntry );
+ ...
+
+ private:
+ ...
+ QList<KreIngredientEntry> m_ingredientEntryList;
+ ...
+}
diff --git a/src/api_v2_design/kremodels.h b/src/api_v2_design/kremodels.h
new file mode 100644
index 0000000..8d9374c
--- /dev/null
+++ b/src/api_v2_design/kremodels.h
@@ -0,0 +1,76 @@
+template<T> class KreAllElementsModels {
+ public:
+ KreGenericModel( RecipeDB * database );
+
+ QStringListModel * nameModel();
+ QSortFilterProxyModel * nameModelSorted();
+ KCompletion * nameCompletion();
+
+ QList<QVariant> idsForName( const QString & name );
+
+ protected:
+ QHash<QVariant,T> m_idToElementMap;
+ QMultiHash<QString,QVariant> m_nameToIdMap;
+
+ QStringListModel * m_nameModel;
+ QSortFilterProxyModel * m_nameModelSorted;
+ KCompletion * m_nameCompletion;
+}
+
+
+class KreAllIngredientsModels: public KreGenericModels<KreIngredient> {
+ public:
+ KreIngredientModel( RecipeDB * database );
+
+}
+
+class KreAllHeadersModels
+
+class KreAllUnitsModels: public KreAllElementsModels {
+ public:
+ KreAllUnitsModels( RecipeDB * database );
+
+ QStringListModel * pluralNameModel();
+ QSortFilterProxyModel * pluralNameModelSorted();
+ KCompletion * pluralNameCompletion();
+
+ QList<QVariant> idsForPlural( const QString & pluralName );
+
+ protected:
+ QMultiHash<QString,QVariant> m_pluralNameToIdMap;
+
+ QStringListModel * m_pluralNameModel;
+ QSortFilterProxyModel * m_pluralNameModelSorted;
+ KCompletion * m_pluralNameCompletion;
+}
+
+class KreAllMassUnitConversionsModel
+class KreAllVolumeConversionsModel
+
+class KreAllPrepMethodsModels
+
+class KreAllPropertiesModels
+
+class KreAllAuthorsModels
+
+class KreRecipeTreeModel .... //would store the categories/recipe tree
+
+//Model to get the set of properties for a given ingredient
+class KrePropertiesForIngredientModel {
+ public:
+ KrePropertiesForIngredientModel( RecipeDB * database.
+ const QVariant & ingredientId );
+
+ ...
+ private:
+ QHash<QVariant,KreIngredientPropertyEntry> m_propertyIdToEntryMap;
+}
+
+//Similar ro KrePropertiesForIngredientModel
+class KreWeightsForIngredientModel
+
+class KreRecipeIngredientsEditorModel {
+ ...
+ private:
+ QHash<QVariant,KrePropertiesForIngredientModel> \
m_ingredientIdToPropertiesMap; +}
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic