[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