~aleteoryx/muditaos

6f05c75c885686d89d2f235e3673ea7e93cf6d84 — Mateusz Piesta 3 years ago 7fc6627
[MOS-835] Per product layout of database migration scripts

Implemented DB init migration and version.json update
53 files changed, 435 insertions(+), 385 deletions(-)

M CMakeLists.txt
M cmake/modules/AddDatabases.cmake
M cmake/modules/AddVersionJson.cmake
A module-db/databases/migration/events/0/down.sql
R module-db/databases/{scripts/events_001 => migration/events/0/up}.sql
A module-db/databases/migration/multimedia/0/down.sql
R module-db/databases/{scripts/multimedia_001 => migration/multimedia/0/up}.sql
M module-db/tests/Helpers.cpp
M module-db/tests/Helpers.hpp
M module-services/service-db/agents/quotes/QuotesQueries.hpp
M module-services/service-db/test/test-service-db-quotes.cpp
A products/BellHybrid/services/db/databases/databases.json
A products/BellHybrid/services/db/databases/migration/meditation_stats/0/down.sql
R products/BellHybrid/services/db/databases/{scripts/meditation_stats_001 => migration/meditation_stats/0/up}.sql
A products/BellHybrid/services/db/databases/migration/settings_bell/0/down.sql
R products/BellHybrid/services/db/databases/{scripts/settings_bell_002 => migration/settings_bell/0/up}.sql
D products/BellHybrid/services/db/databases/scripts/settings_bell_001.sql
M products/BellHybrid/services/db/tests/MeditationStatisticsTable_tests.cpp
A products/PurePhone/services/db/databases/databases.json
A products/PurePhone/services/db/databases/migration/alarms/0/down.sql
R products/PurePhone/services/db/databases/{scripts/alarms_001 => migration/alarms/0/up}.sql
R products/PurePhone/services/db/databases/{scripts/calllog_002-devel => migration/calllog/0/devel}.sql
A products/PurePhone/services/db/databases/migration/calllog/0/down.sql
R products/PurePhone/services/db/databases/{scripts/calllog_001 => migration/calllog/0/up}.sql
R products/PurePhone/services/db/databases/{scripts/contacts_003-devel => migration/contacts/0/devel}.sql
A products/PurePhone/services/db/databases/migration/contacts/0/down.sql
R products/PurePhone/services/db/databases/{scripts/contacts_001 => migration/contacts/0/up}.sql
R products/PurePhone/services/db/databases/{scripts/custom_quotes_002-devel => migration/custom_quotes/0/devel}.sql
A products/PurePhone/services/db/databases/migration/custom_quotes/0/down.sql
R products/PurePhone/services/db/databases/{scripts/custom_quotes_001 => migration/custom_quotes/0/up}.sql
R products/PurePhone/services/db/databases/{scripts/notes_002-devel => migration/notes/0/devel}.sql
A products/PurePhone/services/db/databases/migration/notes/0/down.sql
R products/PurePhone/services/db/databases/{scripts/notes_001 => migration/notes/0/up}.sql
A products/PurePhone/services/db/databases/migration/notifications/0/down.sql
R products/PurePhone/services/db/databases/{scripts/notifications_001 => migration/notifications/0/up}.sql
R products/PurePhone/services/db/databases/{scripts/predefined_quotes_002-devel => migration/predefined_quotes/0/devel}.sql
A products/PurePhone/services/db/databases/migration/predefined_quotes/0/down.sql
R products/PurePhone/services/db/databases/{scripts/predefined_quotes_002 => migration/predefined_quotes/0/up}.sql
R products/PurePhone/services/db/databases/{scripts/settings_v2_002-devel => migration/settings_v2/0/devel}.sql
A products/PurePhone/services/db/databases/migration/settings_v2/0/down.sql
R products/PurePhone/services/db/databases/{scripts/settings_v2_002 => migration/settings_v2/0/up}.sql
R products/PurePhone/services/db/databases/{scripts/sms_004-devel => migration/sms/0/devel}.sql
A products/PurePhone/services/db/databases/migration/sms/0/down.sql
R products/PurePhone/services/db/databases/{scripts/sms_001 => migration/sms/0/up}.sql
D products/PurePhone/services/db/databases/scripts/alarms_002.sql
D products/PurePhone/services/db/databases/scripts/contacts_002.sql
D products/PurePhone/services/db/databases/scripts/notifications_002.sql
D products/PurePhone/services/db/databases/scripts/predefined_quotes_001.sql
D products/PurePhone/services/db/databases/scripts/settings_v2_001.sql
D products/PurePhone/services/db/databases/scripts/sms_002.sql
D products/PurePhone/services/db/databases/scripts/sms_003.sql
A tools/add_dbs_to_version_json.py
M tools/init_databases.py
M CMakeLists.txt => CMakeLists.txt +5 -21
@@ 54,7 54,7 @@ endif()

if (${ENABLE_TESTS})
    enable_testing()
    add_custom_target(check ${CMAKE_CTEST_COMMAND} -V DEPENDS create_test_product_databases create_test_databases_common)
    add_custom_target(check ${CMAKE_CTEST_COMMAND} -V DEPENDS create_test_product_databases)
    add_subdirectory(test)
    include(PureCoverage)
endif ()


@@ 189,21 189,13 @@ add_directories(
        DIRECTORIES log assets crash_dumps data bin db scripts
)

# Create and initialize common databases
add_databases_target(
        TARGET create_databases_common
        SOURCE_DIR ${CMAKE_SOURCE_DIR}/module-db/databases/scripts
        DEST_DIR ${SYSROOT_PATH}/system_a/db
        DEVEL ${WITH_DEVELOPMENT_FEATURES}
)

# Create and initialize product-specific databases
add_databases_target(
        TARGET create_product_databases
        SOURCE_DIR ${CMAKE_SOURCE_DIR}/products/${PRODUCT}/services/db/databases/scripts
        COMMON_DIR ${CMAKE_SOURCE_DIR}/module-db/databases
        PRODUCT_DIR ${CMAKE_SOURCE_DIR}/products/${PRODUCT}/services/db/databases
        DEST_DIR ${SYSROOT_PATH}/system_a/db
        DEVEL ${WITH_DEVELOPMENT_FEATURES}
        DEPENDS create_databases_common
)
# Install scripts
add_scripts_target(


@@ 212,21 204,13 @@ add_scripts_target(
        DEST_DIR ${SYSROOT_PATH}/system_a/scripts
)

# Create and initialize common databases for tests
add_databases_target(
        TARGET create_test_databases_common
        SOURCE_DIR ${CMAKE_SOURCE_DIR}/module-db/databases/scripts
        DEST_DIR ${CMAKE_BINARY_DIR}/test-sysroot/system_a/db
        DEVEL ${WITH_DEVELOPMENT_FEATURES}
)

# Create and initialize product-specific databases for tests
add_databases_target(
        TARGET create_test_product_databases
        SOURCE_DIR ${CMAKE_SOURCE_DIR}/products/${PRODUCT}/services/db/databases/scripts
        COMMON_DIR ${CMAKE_SOURCE_DIR}/module-db/databases
        PRODUCT_DIR ${CMAKE_SOURCE_DIR}/products/${PRODUCT}/services/db/databases
        DEST_DIR ${CMAKE_BINARY_DIR}/test-sysroot/system_a/db
        DEVEL ${WITH_DEVELOPMENT_FEATURES}
        DEPENDS create_databases_common
)

add_library(version-header INTERFACE)

M cmake/modules/AddDatabases.cmake => cmake/modules/AddDatabases.cmake +3 -3
@@ 12,7 12,7 @@ function(add_databases_target)
    cmake_parse_arguments(
            _ARG
            ""
            "TARGET;SOURCE_DIR;DEST_DIR;DEVEL;"
            "TARGET;COMMON_DIR;PRODUCT_DIR;DEST_DIR;DEVEL;"
            "DEPENDS"
            ${ARGN}
    )


@@ 25,8 25,8 @@ function(add_databases_target)
            ${_ARG_TARGET}
            DEPENDS ${_ARG_DEPENDS}

            COMMAND python3 ${PROJECT_SOURCE_DIR}/tools/init_databases.py --input_path ${_ARG_SOURCE_DIR} --output_path ${_ARG_DEST_DIR} ${DEVEL}
            COMMAND python3 ${PROJECT_SOURCE_DIR}/tools/init_databases.py --common_path ${_ARG_COMMON_DIR} --product_path ${_ARG_PRODUCT_DIR} --output_path ${_ARG_DEST_DIR} ${DEVEL}
            COMMENT
            "Creating databases using schemas from: ${_ARG_SOURCE_DIR} and storing them under: ${_ARG_DEST_DIR}"
            "Creating databases using schemas from: ${_ARG_COMMON_DIR} and ${_ARG_PRODUCT_DIR}, and storing them under: ${_ARG_DEST_DIR}"
    )
endfunction()
\ No newline at end of file

M cmake/modules/AddVersionJson.cmake => cmake/modules/AddVersionJson.cmake +8 -2
@@ 19,6 19,10 @@ function(add_version_rt1051_json SOURCE_TARGET)
            -DRECOVERY_BIN_VERSION=${RECOVERY_BIN_VERSION}
            -B ${CMAKE_BINARY_DIR}
            -P ${CMAKE_SOURCE_DIR}/cmake/modules/ConfigureVersionJson.cmake
        COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/add_dbs_to_version_json.py
            --input_path ${CMAKE_SOURCE_DIR}/products/${SOURCE_TARGET}/services/db/databases/databases.json
            --output_path ${CMAKE_BINARY_DIR}/${SOURCE_TARGET}-version.json
            --product ${SOURCE_TARGET}
        DEPENDS ecoboot.bin-target
        DEPENDS recovery.bin-target
        DEPENDS ${SOURCE_TARGET}-boot.bin


@@ 35,7 39,6 @@ function(add_version_rt1051_json SOURCE_TARGET)

    add_custom_target(${SOURCE_TARGET}-version.json-target DEPENDS add-${SOURCE_TARGET}-version.json)

#    multicomp_install(FILES ${CMAKE_BINARY_DIR}/${SOURCE_TARGET}-version.json DESTINATION "./" RENAME "version.json" COMPONENTS Standalone Update)
endfunction()

function(add_version_linux_json SOURCE_TARGET)


@@ 50,6 53,10 @@ function(add_version_linux_json SOURCE_TARGET)
            -DOS_VERSION_LABEL=${OS_VERSION_LABEL}
            -B ${CMAKE_BINARY_DIR}
            -P ${CMAKE_SOURCE_DIR}/cmake/modules/ConfigureVersionJson.cmake
COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/add_dbs_to_version_json.py
            --input_path ${CMAKE_SOURCE_DIR}/products/${SOURCE_TARGET}/services/db/databases/databases.json
            --output_path ${CMAKE_BINARY_DIR}/${SOURCE_TARGET}-version.json
            --product ${SOURCE_TARGET}
    )

    add_custom_command(OUTPUT add-${SOURCE_TARGET}-version.json


@@ 63,5 70,4 @@ function(add_version_linux_json SOURCE_TARGET)

    add_custom_target(${SOURCE_TARGET}-version.json-target DEPENDS add-${SOURCE_TARGET}-version.json)

#    multicomp_install(FILES ${CMAKE_BINARY_DIR}/${SOURCE_TARGET}-version.json DESTINATION "./" RENAME "version.json" COMPONENTS Standalone Update)
endfunction()

A module-db/databases/migration/events/0/down.sql => module-db/databases/migration/events/0/down.sql +0 -0
R module-db/databases/scripts/events_001.sql => module-db/databases/migration/events/0/up.sql +15 -15
@@ 1,23 1,23 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS events(
                  _id INTEGER PRIMARY KEY,
                  name TEXT DEFAULT '',
                  start_date DATETIME,
                  end_date DATETIME,
                  duration INTEGER,
                  is_all_day BOOLEAN,
                  rrule TEXT DEFAULT ''
                                     _id INTEGER PRIMARY KEY,
                                     name TEXT DEFAULT '',
                                     start_date DATETIME,
                                     end_date DATETIME,
                                     duration INTEGER,
                                     is_all_day BOOLEAN,
                                     rrule TEXT DEFAULT ''
);

CREATE TABLE IF NOT EXISTS alarms(
                  _id INTEGER PRIMARY KEY,
                  hour INTEGER,
                  minute INTEGER,
                  music_tone TEXT,
                  enabled BOOLEAN,
                  snooze_duration INTEGER,
                  rrule TEXT DEFAULT ''
                                     _id INTEGER PRIMARY KEY,
                                     hour INTEGER,
                                     minute INTEGER,
                                     music_tone TEXT,
                                     enabled BOOLEAN,
                                     snooze_duration INTEGER,
                                     rrule TEXT DEFAULT ''
);


A module-db/databases/migration/multimedia/0/down.sql => module-db/databases/migration/multimedia/0/down.sql +0 -0
R module-db/databases/scripts/multimedia_001.sql => module-db/databases/migration/multimedia/0/up.sql +1 -1
@@ 1,4 1,4 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS files

M module-db/tests/Helpers.cpp => module-db/tests/Helpers.cpp +24 -35
@@ 4,6 4,7 @@
#include "Helpers.hpp"

#include <Utils.hpp>
#include <vector>
#include <set>
#include <algorithm>
#include <utility>


@@ 62,44 63,32 @@ namespace
        return statements;
    }

    std::array<std::string, 3> splitFilename(const std::string &filename)
    std::set<std::filesystem::path> listVersionDirectories(const std::filesystem::path &path, const std::string &dbName)
    {
        const auto name    = filename.substr(0, filename.find("."));
        const auto prefix  = name.substr(0, name.find_last_of("_"));
        const auto postfix = name.substr(name.find_last_of("_") + 1, std::string::npos);

        return {name, prefix, postfix};
    }

    bool isOnExcludedList(const std::string &name, const std::vector<std::string> &excludedList)
    {
        return std::any_of(excludedList.begin(), excludedList.end(), [&name](const auto &entry) {
            return name.find(entry) != std::string::npos;
        });
        std::set<std::filesystem::path> versions;
        for (const auto &entry : std::filesystem::directory_iterator(path)) {
            if (entry.is_directory() and entry.path().filename() == dbName) {
                for (const auto &version : std::filesystem::directory_iterator(entry.path())) {
                    versions.insert(version.path());
                }
            }
        }
        return versions;
    }

    std::vector<std::filesystem::path> listFiles(const std::filesystem::path &path,
                                                 const std::string &prefix,
                                                 const std::vector<std::string> &excludedList)
                                                 const bool withDevelopment)
    {
        std::set<std::pair<int, std::filesystem::path>> orderedFiles;
        for (const auto &entry : std::filesystem::directory_iterator(path)) {
            if (!entry.is_directory() && entry.path().has_filename()) {
                try {
                    const auto parts       = splitFilename(entry.path().filename().string());
                    const auto &filePrefix = parts[1];
                    if (filePrefix == prefix and not isOnExcludedList(parts[0], excludedList)) {
                        orderedFiles.insert({std::stoi(parts[2]), entry.path()});
                    }
                }
                catch (std::invalid_argument &e) {
                    printf("Ignoring file: %s", entry.path().c_str());
                }
        constexpr auto up_sql    = "up.sql";
        constexpr auto devel_sql = "devel.sql";
        std::vector<std::filesystem::path> files;
        for (const auto &version : listVersionDirectories(path, prefix)) {
            files.push_back(version / up_sql);
            if (withDevelopment and std::filesystem::exists(version / devel_sql)) {
                files.push_back(version / devel_sql);
            }
        }

        std::vector<std::filesystem::path> files;
        std::for_each(orderedFiles.begin(), orderedFiles.end(), [&](auto item) { files.push_back(item.second); });
        return files;
    }



@@ 121,7 110,7 @@ namespace db::tests

    std::filesystem::path getScriptsPath()
    {
        return currentFileLocation() / "../databases/scripts";
        return currentFileLocation() / "../databases/migration";
    }

    /// TODO:


@@ 131,7 120,7 @@ namespace db::tests
    /// placed in the correct directory.
    std::filesystem::path getPurePhoneScriptsPath()
    {
        return currentFileLocation() / "../../products/PurePhone/services/db/databases/scripts";
        return currentFileLocation() / "../../products/PurePhone/services/db/databases/migration";
    }

    bool DatabaseScripts::operator()(Database &db)


@@ 139,12 128,12 @@ namespace db::tests
        const std::filesystem::path dbpath = db.getName();
        const std::string dbname           = dbpath.filename().replace_extension();

        const auto scripts = listFiles(directory, dbname, exclude);
        const auto scripts = listFiles(directory, dbname, withDevelopment);
        return std::all_of(scripts.begin(), scripts.end(), [&db](const auto &script) {
            return executeOnDb(db, readCommands(script));
        });
    }
    DatabaseScripts::DatabaseScripts(std::filesystem::path directory, std::vector<std::string> &&exclude)
        : directory{std::move(directory)}, extension{".sql"}, exclude{std::move(exclude)}
    DatabaseScripts::DatabaseScripts(std::filesystem::path directory, bool withDevelopment)
        : directory{std::move(directory)}, withDevelopment{withDevelopment}
    {}
} // namespace db::tests
\ No newline at end of file

M module-db/tests/Helpers.hpp => module-db/tests/Helpers.hpp +3 -9
@@ 15,13 15,12 @@ namespace db::tests
    class DatabaseScripts
    {
      public:
        DatabaseScripts(std::filesystem::path directory, std::vector<std::string> &&exclude);
        DatabaseScripts(std::filesystem::path directory, bool withDevelopment);
        bool operator()(Database &db);

      private:
        std::filesystem::path directory;
        std::string extension;
        std::vector<std::string> exclude;
        bool withDevelopment;
    };

    template <typename Db>


@@ 36,11 35,6 @@ namespace db::tests

            std::filesystem::remove(directory);

            std::vector<std::string> excludeList;
            if (not withDevelopment) {
                excludeList.emplace_back("-devel");
            }

            /// Some databases initialize internal fields in the constructors. To avoid fetching wrong data from
            /// uninitialized database fields spawn temporary database, execute any available scripts and finally
            /// recreate it. This way we are sure database will load correct data.


@@ 51,7 45,7 @@ namespace db::tests
                    throw std::runtime_error("Could not initialize database");
                }

                if (not scriptsPath.empty() and not DatabaseScripts{scriptsPath, std::move(excludeList)}(*tempDb)) {
                if (not scriptsPath.empty() and not DatabaseScripts{scriptsPath, withDevelopment}(*tempDb)) {
                    throw std::runtime_error("Failed to execute database scripts");
                }
            }

M module-services/service-db/agents/quotes/QuotesQueries.hpp => module-services/service-db/agents/quotes/QuotesQueries.hpp +17 -49
@@ 3,20 3,15 @@

#pragma once

#include <Common/Types.hpp>

namespace Quotes::Queries
{
    /// To predefined quotes table

    constexpr auto getAllCategories = R"sql(
                        SELECT CT.category_id, CT.category_name, CT.enabled
                        FROM 
                            category_table as CT,
                            quote_languages as QL
                        WHERE
                            QL.lang_name = '%q'
                            and
                            CT.lang_id = QL.lang_id 
                        )sql";
    constexpr auto getAllCategories =
        " SELECT CT.category_id, CT.category_name, CT.enabled FROM category_table as CT, quote_languages as QL WHERE "
        "QL.lang_name = " str_ " and CT.lang_id = QL.lang_id;";

    constexpr auto getEnabledPredefinedQuotes = R"sql(
                        SELECT QT.quote_id, QT.quote, QT.author, CT.enabled


@@ 39,24 34,12 @@ namespace Quotes::Queries
                            QCM.quote_id   
                        )sql";

    constexpr auto enableCategory = R"sql(
                        UPDATE category_table SET enabled = '%d'
                        WHERE category_id = '%lu';
                        )sql";
    constexpr auto enableCategory = "UPDATE category_table SET enabled=" u32_ " WHERE category_id=" u32_ ";";

    constexpr auto readPredefinedQuote = R"sql(
                        SELECT QT.quote_id, QT.quote, QT.author, CT.enabled
                        FROM
                            quote_table as QT,
                            quote_category_map as QCM,
                            category_table as CT
                        WHERE
                            QT.quote_id = '%lu'
                            and
                            QCM.quote_id = QT.quote_id
                            and 
                            QCM.category_id = CT.category_id
                        )sql";
    constexpr auto readPredefinedQuote =
        "SELECT QT.quote_id, QT.quote, QT.author, CT.enabled "
        "FROM quote_table as QT,quote_category_map as QCM,category_table as CT WHERE "
        "QT.quote_id=" u32_ " and QCM.quote_id = QT.quote_id and QCM.category_id = CT.category_id;";

    /// To custom quotes database



@@ 73,30 56,15 @@ namespace Quotes::Queries
                            enabled = TRUE
                        )sql";

    constexpr auto enableQuote = R"sql(
                        UPDATE quote_table SET enabled = '%d'
                        WHERE quote_id = '%lu';
                        )sql";
    constexpr auto enableQuote = "UPDATE quote_table SET enabled=" u32_ " WHERE quote_id=" u32_ ";";

    constexpr auto addQuote = R"sql(
                        INSERT INTO quote_table (quote, author, enabled)
                        VALUES ('%q' , '%q', '%d');
                        )sql";
    constexpr auto addQuote = "INSERT INTO quote_table (quote, author, enabled) VALUES (" str_c str_c u32_ ");";

    constexpr auto readCustomQuote = R"sql(
                        SELECT quote_id, quote, author, enabled
                        FROM quote_table
                        WHERE quote_id = '%lu';
                        )sql";
    constexpr auto readCustomQuote =
        "SELECT quote_id, quote, author, enabled FROM quote_table WHERE quote_id=" u32_ ";";

    constexpr auto writeQuote = R"sql(
                        UPDATE quote_table
                        SET quote = '%q', author = '%q', enabled = '%d'
                        WHERE quote_id = '%lu';
                        )sql";
    constexpr auto writeQuote =
        "UPDATE quote_table SET quote=" str_c "author=" str_c "enabled=" u32_ " WHERE quote_id=" u32_ ";";

    constexpr auto deleteQuote = R"sql(
                        DELETE FROM quote_table
                        WHERE quote_id = '%lu';
                        )sql";
    constexpr auto deleteQuote = "DELETE FROM quote_table WHERE quote_id=" u32_ ";";
} // namespace Quotes::Queries

M module-services/service-db/test/test-service-db-quotes.cpp => module-services/service-db/test/test-service-db-quotes.cpp +3 -3
@@ 19,7 19,7 @@ namespace
    std::filesystem::path getScriptsPath()
    {
        const std::string path              = __FILE__;
        const std::filesystem::path scripts = "../../../products/PurePhone/services/db/databases/scripts";
        const std::filesystem::path scripts = "../../../products/PurePhone/services/db/databases/migration";
        return std::filesystem::path{path.substr(0, path.rfind('/'))} / scripts;
    }



@@ 32,7 32,7 @@ namespace

TEST_CASE("Quotes")
{
    db::tests::DatabaseUnderTest<Database> predefinedDb{"predefined_quotes.db", getScriptsPath(), true};
    db::tests::DatabaseUnderTest<Database> predefinedDb{"predefined_quotes.db", getScriptsPath()};
    db::tests::DatabaseUnderTest<Database> customDb{"custom_quotes.db", getScriptsPath(), true};

    std::string timestampString{};


@@ 57,7 57,7 @@ TEST_CASE("Quotes")

    SECTION("Enable category by id")
    {
        bool enable;
        bool enable{};
        const unsigned int categoryId = 5;

        // Initial conditions set

A products/BellHybrid/services/db/databases/databases.json => products/BellHybrid/services/db/databases/databases.json +10 -0
@@ 0,0 1,10 @@
{
  "BellHybrid": {
    "databases":[
      {"name": "events", "version": "0"},
      {"name": "multimedia", "version": "0"},
      {"name": "meditation_stats", "version": "0"},
      {"name": "settings_bell", "version": "0"}
    ]
  }
}
\ No newline at end of file

A products/BellHybrid/services/db/databases/migration/meditation_stats/0/down.sql => products/BellHybrid/services/db/databases/migration/meditation_stats/0/down.sql +0 -0
R products/BellHybrid/services/db/databases/scripts/meditation_stats_001.sql => products/BellHybrid/services/db/databases/migration/meditation_stats/0/up.sql +0 -0
A products/BellHybrid/services/db/databases/migration/settings_bell/0/down.sql => products/BellHybrid/services/db/databases/migration/settings_bell/0/down.sql +0 -0
R products/BellHybrid/services/db/databases/scripts/settings_bell_002.sql => products/BellHybrid/services/db/databases/migration/settings_bell/0/up.sql +44 -0
@@ 1,6 1,50 @@
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

--
-- Main settings table, for string application persistent data
--
CREATE TABLE IF NOT EXISTS settings_tab (
                                            path TEXT NOT NULL UNIQUE PRIMARY KEY,
                                            value TEXT
);

--
-- Dictionary table, for variables with fixed set of values
--
CREATE TABLE IF NOT EXISTS dictionary_tab (
                                              id INTEGER PRIMARY KEY,
                                              path TEXT NOT NULL,
                                              value TEXT,
                                              CONSTRAINT dictionary_unique
                                              UNIQUE (path, value) ON CONFLICT REPLACE
    );

--
-- Table contains information who to inform
-- about changes in values.
--
CREATE TABLE IF NOT EXISTS notifications_tab (
                                                 id INTEGER PRIMARY KEY,
                                                 path TEXT NOT NULL,
                                                 service TEXT,
                                                 CONSTRAINT notification_unique
                                                 UNIQUE(path, service)
    );

CREATE TABLE IF NOT EXISTS settings_changed_tab(

                                                   id INTEGER PRIMARY KEY,
                                                   path TEXT NOT NULL,
                                                   value TEXT,
                                                   CONSTRAINT changed_unique
                                                   UNIQUE(path ) ON CONFLICT REPLACE
    );

CREATE TRIGGER IF NOT EXISTS on_update UPDATE OF value ON settings_tab
    WHEN new.value <> old.value BEGIN INSERT OR REPLACE INTO  settings_changed_tab (path, value) VALUES (new.path,new.value); END;


-- ----------- insert default values ----------------------
INSERT OR REPLACE INTO dictionary_tab (path, value) VALUES
    ('system/phone_mode', 'online'),

D products/BellHybrid/services/db/databases/scripts/settings_bell_001.sql => products/BellHybrid/services/db/databases/scripts/settings_bell_001.sql +0 -46
@@ 1,46 0,0 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

--
-- Main settings table, for string application persistent data
--
CREATE TABLE IF NOT EXISTS settings_tab (
    path TEXT NOT NULL UNIQUE PRIMARY KEY,
    value TEXT
);

--
-- Dictionary table, for variables with fixed set of values
--
CREATE TABLE IF NOT EXISTS dictionary_tab (
    id INTEGER PRIMARY KEY,
    path TEXT NOT NULL,
    value TEXT,
    CONSTRAINT dictionary_unique
       UNIQUE (path, value) ON CONFLICT REPLACE
    );

--
-- Table contains information who to inform
-- about changes in values.
--
CREATE TABLE IF NOT EXISTS notifications_tab (
    id INTEGER PRIMARY KEY,
    path TEXT NOT NULL,
    service TEXT,
    CONSTRAINT notification_unique
        UNIQUE(path, service)
    );

CREATE TABLE IF NOT EXISTS settings_changed_tab(

    id INTEGER PRIMARY KEY,
    path TEXT NOT NULL,
    value TEXT,
    CONSTRAINT changed_unique
        UNIQUE(path ) ON CONFLICT REPLACE
);

CREATE TRIGGER IF NOT EXISTS on_update UPDATE OF value ON settings_tab 
WHEN new.value <> old.value BEGIN INSERT OR REPLACE INTO  settings_changed_tab (path, value) VALUES (new.path,new.value); END;


M products/BellHybrid/services/db/tests/MeditationStatisticsTable_tests.cpp => products/BellHybrid/services/db/tests/MeditationStatisticsTable_tests.cpp +1 -1
@@ 27,7 27,7 @@ namespace
    {
        const std::string path = __FILE__;
        const auto dir         = std::filesystem::path{path.substr(0, path.rfind('/'))};
        return dir / "../databases/scripts";
        return dir / "../databases/migration";
    }
} // namespace


A products/PurePhone/services/db/databases/databases.json => products/PurePhone/services/db/databases/databases.json +17 -0
@@ 0,0 1,17 @@
{
  "PurePhone": {
    "databases":[
      {"name": "events", "version": "0"},
      {"name": "multimedia", "version": "0"},
      {"name": "alarms", "version": "0"},
      {"name": "calllog", "version": "0"},
      {"name": "contacts", "version": "0"},
      {"name": "custom_quotes", "version": "0"},
      {"name": "notes", "version": "0"},
      {"name": "notifications", "version": "0"},
      {"name": "predefined_quotes", "version": "0"},
      {"name": "settings_v2", "version": "0"},
      {"name": "sms", "version": "0"}
    ]
  }
}
\ No newline at end of file

A products/PurePhone/services/db/databases/migration/alarms/0/down.sql => products/PurePhone/services/db/databases/migration/alarms/0/down.sql +0 -0
R products/PurePhone/services/db/databases/scripts/alarms_001.sql => products/PurePhone/services/db/databases/migration/alarms/0/up.sql +19 -11
@@ 1,17 1,25 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS alarms(
                  _id INTEGER PRIMARY KEY,
                  time DATETIME,
                  snooze INTEGER,
                  status INTEGER,
                  repeat INTEGER,
                  path TEXT DEFAULT '',
                  FOREIGN KEY(status) REFERENCES alarmStatuses(_id)
);
                                     _id INTEGER PRIMARY KEY,
                                     time DATETIME,
                                     snooze INTEGER,
                                     status INTEGER,
                                     repeat INTEGER,
                                     path TEXT DEFAULT '',
                                     FOREIGN KEY(status) REFERENCES alarmStatuses(_id)
    );

CREATE TABLE IF NOT EXISTS alarmStatuses(
                  _id INTEGER PRIMARY KEY,
                  name TEXT NOT NULL
                                            _id INTEGER PRIMARY KEY,
                                            name TEXT NOT NULL
);

INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (1, 'Off');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (2, 'On');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (3, 'FirstSnooze');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (4, 'SecondSnooze');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (5, 'ThirdSnooze');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (6, 'FourthSnooze');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (7, 'FifthSnooze');

R products/PurePhone/services/db/databases/scripts/calllog_002-devel.sql => products/PurePhone/services/db/databases/migration/calllog/0/devel.sql +0 -0
A products/PurePhone/services/db/databases/migration/calllog/0/down.sql => products/PurePhone/services/db/databases/migration/calllog/0/down.sql +0 -0
R products/PurePhone/services/db/databases/scripts/calllog_001.sql => products/PurePhone/services/db/databases/migration/calllog/0/up.sql +11 -11
@@ 1,17 1,17 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS calls(
    _id INTEGER PRIMARY KEY,
    number TEXT DEFAULT '',
    e164number TEXT DEFAULT '',
    presentation INTEGER DEFAULT 0,
    date INTEGER DEFAULT 0,
    duration INTEGER DEFAULT 0,
    type INTEGER DEFAULT 0,
    name TEXT DEFAULT '',
    contactId INTEGER DEFAULT 0,
    isRead INTEGER DEFAULT 1
                                    _id INTEGER PRIMARY KEY,
                                    number TEXT DEFAULT '',
                                    e164number TEXT DEFAULT '',
                                    presentation INTEGER DEFAULT 0,
                                    date INTEGER DEFAULT 0,
                                    duration INTEGER DEFAULT 0,
                                    type INTEGER DEFAULT 0,
                                    name TEXT DEFAULT '',
                                    contactId INTEGER DEFAULT 0,
                                    isRead INTEGER DEFAULT 1
);
-- calls.contactId should not be used.
-- calls.name should not be used.

R products/PurePhone/services/db/databases/scripts/contacts_003-devel.sql => products/PurePhone/services/db/databases/migration/contacts/0/devel.sql +0 -0
A products/PurePhone/services/db/databases/migration/contacts/0/down.sql => products/PurePhone/services/db/databases/migration/contacts/0/down.sql +0 -0
R products/PurePhone/services/db/databases/scripts/contacts_001.sql => products/PurePhone/services/db/databases/migration/contacts/0/up.sql +24 -10
@@ 1,4 1,4 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS contacts


@@ 11,7 11,7 @@ CREATE TABLE IF NOT EXISTS contacts
    speeddial  TEXT NOT NULL,
    FOREIGN KEY (name_id) REFERENCES contact_name (_id),
    FOREIGN KEY (ring_id) REFERENCES contact_ringtones (_id)
);
    );

CREATE TABLE IF NOT EXISTS contact_address
(


@@ 21,7 21,7 @@ CREATE TABLE IF NOT EXISTS contact_address
    note       TEXT NOT NULL,
    mail       TEXT NOT NULL,
    FOREIGN KEY (contact_id) REFERENCES contacts (_id)
);
    );

CREATE TABLE IF NOT EXISTS contact_groups
(


@@ 35,12 35,12 @@ CREATE TABLE IF NOT EXISTS contact_match_groups
    group_id   INTEGER,
    contact_id INTEGER,
    FOREIGN KEY (group_id) REFERENCES contact_groups (_id)
        ON DELETE CASCADE,
    ON DELETE CASCADE,
    FOREIGN KEY (contact_id) REFERENCES contacts (_id)
        ON DELETE CASCADE,
    ON DELETE CASCADE,
    CONSTRAINT unique_group_contact
        UNIQUE (group_id, contact_id)
);
    UNIQUE (group_id, contact_id)
    );

CREATE TABLE IF NOT EXISTS contact_group_protected
(


@@ 55,7 55,7 @@ CREATE TABLE IF NOT EXISTS contact_name
    name_primary     TEXT NOT NULL,
    name_alternative TEXT NOT NULL,
    FOREIGN KEY (contact_id) REFERENCES contacts (_id)
);
    );

CREATE TABLE IF NOT EXISTS contact_number
(


@@ 65,7 65,7 @@ CREATE TABLE IF NOT EXISTS contact_number
    number_e164 TEXT NOT NULL,
    type        INTEGER,
    FOREIGN KEY (contact_id) REFERENCES contacts (_id)
);
    );

CREATE TABLE IF NOT EXISTS contact_ringtones
(


@@ 73,10 73,24 @@ CREATE TABLE IF NOT EXISTS contact_ringtones
    contact_id INTEGER,
    asset_path TEXT NOT NULL,
    FOREIGN KEY (contact_id) REFERENCES contacts (_id) ON DELETE CASCADE
);
    );

CREATE INDEX IF NOT EXISTS contact_match_group_index_on_group
    ON contact_match_groups (group_id);
CREATE INDEX IF NOT EXISTS contact_match_group_index_on_contact
    ON contact_match_groups (contact_id);


INSERT OR REPLACE INTO contact_group_protected
    (_id, group_id)
VALUES (1, 1),
       (2, 2),
       (3, 3),
       (4, 4);

INSERT OR REPLACE INTO contact_groups
    (_id, name)
VALUES (1, 'Favourites'),
       (2, 'ICE'),
       (3, 'Blocked'),
       (4, 'Temporary');

R products/PurePhone/services/db/databases/scripts/custom_quotes_002-devel.sql => products/PurePhone/services/db/databases/migration/custom_quotes/0/devel.sql +0 -0
A products/PurePhone/services/db/databases/migration/custom_quotes/0/down.sql => products/PurePhone/services/db/databases/migration/custom_quotes/0/down.sql +0 -0
R products/PurePhone/services/db/databases/scripts/custom_quotes_001.sql => products/PurePhone/services/db/databases/migration/custom_quotes/0/up.sql +4 -4
@@ 2,8 2,8 @@
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS quote_table(
    quote_id INTEGER PRIMARY KEY,
    quote TEXT NOT NULL,
    author TEXT,
    enabled BOOLEAN NOT NULL DEFAULT TRUE
                                          quote_id INTEGER PRIMARY KEY,
                                          quote TEXT NOT NULL,
                                          author TEXT,
                                          enabled BOOLEAN NOT NULL DEFAULT TRUE
);

R products/PurePhone/services/db/databases/scripts/notes_002-devel.sql => products/PurePhone/services/db/databases/migration/notes/0/devel.sql +0 -0
A products/PurePhone/services/db/databases/migration/notes/0/down.sql => products/PurePhone/services/db/databases/migration/notes/0/down.sql +0 -0
R products/PurePhone/services/db/databases/scripts/notes_001.sql => products/PurePhone/services/db/databases/migration/notes/0/up.sql +4 -4
@@ 1,8 1,8 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS notes(
    _id INTEGER PRIMARY KEY,
    date INTEGER,
    snippet TEXT DEFAULT ''
                                    _id INTEGER PRIMARY KEY,
                                    date INTEGER,
                                    snippet TEXT DEFAULT ''
);

A products/PurePhone/services/db/databases/migration/notifications/0/down.sql => products/PurePhone/services/db/databases/migration/notifications/0/down.sql +0 -0
R products/PurePhone/services/db/databases/scripts/notifications_001.sql => products/PurePhone/services/db/databases/migration/notifications/0/up.sql +9 -5
@@ 1,9 1,13 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS notifications(
    _id INTEGER PRIMARY KEY,
    key INTEGER UNIQUE DEFAULT 0,
    value INTEGER DEFAULT 0,
    contact_id INTEGER DEFAULT 0
                                            _id INTEGER PRIMARY KEY,
                                            key INTEGER UNIQUE DEFAULT 0,
                                            value INTEGER DEFAULT 0,
                                            contact_id INTEGER DEFAULT 0
);

INSERT OR IGNORE INTO notifications (key, value, contact_id) VALUES
    ('1', '0', '0'),
    ('2', '0', '0');

R products/PurePhone/services/db/databases/scripts/predefined_quotes_002-devel.sql => products/PurePhone/services/db/databases/migration/predefined_quotes/0/devel.sql +0 -0
A products/PurePhone/services/db/databases/migration/predefined_quotes/0/down.sql => products/PurePhone/services/db/databases/migration/predefined_quotes/0/down.sql +0 -0
R products/PurePhone/services/db/databases/scripts/predefined_quotes_002.sql => products/PurePhone/services/db/databases/migration/predefined_quotes/0/up.sql +30 -0
@@ 1,6 1,36 @@
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS quote_languages (
                                               lang_id INTEGER NOT NULL,
                                               lang_name TEXT NOT NULL,
                                               PRIMARY KEY (lang_id)
    );

CREATE TABLE IF NOT EXISTS category_table (
                                              category_id INTEGER NOT NULL,
                                              lang_id INTEGER NOT NULL,
                                              category_name TEXT NOT NULL UNIQUE,
                                              enabled BOOLEAN NOT NULL DEFAULT TRUE,
                                              PRIMARY KEY (category_id),
    FOREIGN KEY (lang_id) REFERENCES quote_languages(lang_id)
    );

CREATE TABLE IF NOT EXISTS quote_table (
                                           quote_id INTEGER NOT NULL,
                                           quote TEXT NOT NULL,
                                           author TEXT,
                                           PRIMARY KEY (quote_id)
    );

CREATE TABLE IF NOT EXISTS quote_category_map (
                                                  category_id INTEGER NOT NULL,
                                                  quote_id INTEGER NOT NULL,
                                                  FOREIGN KEY (category_id) REFERENCES category_table(category_id),
    FOREIGN KEY (quote_id) REFERENCES quote_table(quote_id),
    CONSTRAINT quotes_unique UNIQUE(category_id, quote_id)
    );

-- To add new language please add here string exactly the same as desired display language
INSERT OR REPLACE INTO quote_languages (lang_id, lang_name) VALUES
    (1, 'English'),

R products/PurePhone/services/db/databases/scripts/settings_v2_002-devel.sql => products/PurePhone/services/db/databases/migration/settings_v2/0/devel.sql +0 -0
A products/PurePhone/services/db/databases/migration/settings_v2/0/down.sql => products/PurePhone/services/db/databases/migration/settings_v2/0/down.sql +0 -0
R products/PurePhone/services/db/databases/scripts/settings_v2_002.sql => products/PurePhone/services/db/databases/migration/settings_v2/0/up.sql +43 -0
@@ 1,6 1,49 @@
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

--
-- Main settings table, for string application persistent data
--
CREATE TABLE IF NOT EXISTS settings_tab (
                                            path TEXT NOT NULL UNIQUE PRIMARY KEY,
                                            value TEXT
);

--
-- Dictionary table, for variables with fixed set of values
--
CREATE TABLE IF NOT EXISTS dictionary_tab (
                                              id INTEGER PRIMARY KEY,
                                              path TEXT NOT NULL,
                                              value TEXT,
                                              CONSTRAINT dictionary_unique
                                              UNIQUE (path, value) ON CONFLICT REPLACE
    );

--
-- Table contains information who to inform
-- about changes in values.
--
CREATE TABLE IF NOT EXISTS notifications_tab (
                                                 id INTEGER PRIMARY KEY,
                                                 path TEXT NOT NULL,
                                                 service TEXT,
                                                 CONSTRAINT notification_unique
                                                 UNIQUE(path, service)
    );

CREATE TABLE IF NOT EXISTS settings_changed_tab(

                                                   id INTEGER PRIMARY KEY,
                                                   path TEXT NOT NULL,
                                                   value TEXT,
                                                   CONSTRAINT changed_unique
                                                   UNIQUE(path ) ON CONFLICT REPLACE
    );

CREATE TRIGGER IF NOT EXISTS on_update UPDATE OF value ON settings_tab
    WHEN new.value <> old.value BEGIN INSERT OR REPLACE INTO  settings_changed_tab (path, value) VALUES (new.path,new.value); END;

-- ----------- insert default values ----------------------
INSERT OR REPLACE INTO dictionary_tab (path, value) VALUES
    ('system/phone_mode', 'online'),

R products/PurePhone/services/db/databases/scripts/sms_004-devel.sql => products/PurePhone/services/db/databases/migration/sms/0/devel.sql +0 -0
A products/PurePhone/services/db/databases/migration/sms/0/down.sql => products/PurePhone/services/db/databases/migration/sms/0/down.sql +0 -0
R products/PurePhone/services/db/databases/scripts/sms_001.sql => products/PurePhone/services/db/databases/migration/sms/0/up.sql +11 -2
@@ 1,4 1,4 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS sms


@@ 11,7 11,7 @@ CREATE TABLE IF NOT EXISTS sms
    body       TEXT NOT_NULL,
    type       INTEGER,
    FOREIGN KEY (thread_id) REFERENCES threads (_id) ON DELETE CASCADE
);
    );
-- sms.contact_id should not be used.

CREATE TABLE IF NOT EXISTS templates


@@ 42,3 42,12 @@ CREATE TABLE IF NOT EXISTS threads_count

CREATE TRIGGER IF NOT EXISTS on_thread_insert AFTER INSERT ON threads BEGIN UPDATE threads_count SET count=count+1 WHERE _id=1; END;
CREATE TRIGGER IF NOT EXISTS on_thread_remove AFTER DELETE ON threads BEGIN UPDATE threads_count SET count=count-1 WHERE _id=1; END;

INSERT OR IGNORE INTO threads_count ( _id, count ) VALUES (1,0);

BEGIN TRANSACTION;
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (1,'Thanks for reaching out. I can''t talk right now, I''ll call you later',4);
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (2,'I''ll call you later',3);
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (3,'I''ll be there in 15 minutes',2);
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (4,'Give me 5 minutes',5);
COMMIT;

D products/PurePhone/services/db/databases/scripts/alarms_002.sql => products/PurePhone/services/db/databases/scripts/alarms_002.sql +0 -10
@@ 1,10 0,0 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (1, 'Off');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (2, 'On');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (3, 'FirstSnooze');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (4, 'SecondSnooze');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (5, 'ThirdSnooze');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (6, 'FourthSnooze');
INSERT or IGNORE INTO alarmStatuses (_id, name) VALUES (7, 'FifthSnooze');

D products/PurePhone/services/db/databases/scripts/contacts_002.sql => products/PurePhone/services/db/databases/scripts/contacts_002.sql +0 -16
@@ 1,16 0,0 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

INSERT OR REPLACE INTO contact_group_protected
    (_id, group_id)
VALUES (1, 1),
       (2, 2),
       (3, 3),
       (4, 4);

INSERT OR REPLACE INTO contact_groups
    (_id, name)
VALUES (1, 'Favourites'),
       (2, 'ICE'),
       (3, 'Blocked'),
       (4, 'Temporary');

D products/PurePhone/services/db/databases/scripts/notifications_002.sql => products/PurePhone/services/db/databases/scripts/notifications_002.sql +0 -6
@@ 1,6 0,0 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

INSERT OR IGNORE INTO notifications (key, value, contact_id) VALUES
    ('1', '0', '0'),
    ('2', '0', '0');

D products/PurePhone/services/db/databases/scripts/predefined_quotes_001.sql => products/PurePhone/services/db/databases/scripts/predefined_quotes_001.sql +0 -32
@@ 1,32 0,0 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

CREATE TABLE IF NOT EXISTS quote_languages (
    lang_id INTEGER NOT NULL,
    lang_name TEXT NOT NULL,
    PRIMARY KEY (lang_id)
    );

CREATE TABLE IF NOT EXISTS category_table (
    category_id INTEGER NOT NULL,
    lang_id INTEGER NOT NULL,
    category_name TEXT NOT NULL UNIQUE,
    enabled BOOLEAN NOT NULL DEFAULT TRUE,
    PRIMARY KEY (category_id),
    FOREIGN KEY (lang_id) REFERENCES quote_languages(lang_id)
    );

CREATE TABLE IF NOT EXISTS quote_table (
    quote_id INTEGER NOT NULL,
    quote TEXT NOT NULL,
    author TEXT,
    PRIMARY KEY (quote_id)
    );

CREATE TABLE IF NOT EXISTS quote_category_map (
    category_id INTEGER NOT NULL,
    quote_id INTEGER NOT NULL,
    FOREIGN KEY (category_id) REFERENCES category_table(category_id),
    FOREIGN KEY (quote_id) REFERENCES quote_table(quote_id),
    CONSTRAINT quotes_unique UNIQUE(category_id, quote_id)
    );

D products/PurePhone/services/db/databases/scripts/settings_v2_001.sql => products/PurePhone/services/db/databases/scripts/settings_v2_001.sql +0 -46
@@ 1,46 0,0 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

--
-- Main settings table, for string application persistent data
--
CREATE TABLE IF NOT EXISTS settings_tab (
    path TEXT NOT NULL UNIQUE PRIMARY KEY,
    value TEXT
);

--
-- Dictionary table, for variables with fixed set of values
--
CREATE TABLE IF NOT EXISTS dictionary_tab (
    id INTEGER PRIMARY KEY,
    path TEXT NOT NULL,
    value TEXT,
    CONSTRAINT dictionary_unique
       UNIQUE (path, value) ON CONFLICT REPLACE
    );

--
-- Table contains information who to inform
-- about changes in values.
--
CREATE TABLE IF NOT EXISTS notifications_tab (
    id INTEGER PRIMARY KEY,
    path TEXT NOT NULL,
    service TEXT,
    CONSTRAINT notification_unique
        UNIQUE(path, service)
    );

CREATE TABLE IF NOT EXISTS settings_changed_tab(

    id INTEGER PRIMARY KEY,
    path TEXT NOT NULL,
    value TEXT,
    CONSTRAINT changed_unique
        UNIQUE(path ) ON CONFLICT REPLACE
);

CREATE TRIGGER IF NOT EXISTS on_update UPDATE OF value ON settings_tab 
WHEN new.value <> old.value BEGIN INSERT OR REPLACE INTO  settings_changed_tab (path, value) VALUES (new.path,new.value); END;


D products/PurePhone/services/db/databases/scripts/sms_002.sql => products/PurePhone/services/db/databases/scripts/sms_002.sql +0 -4
@@ 1,4 0,0 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

INSERT OR IGNORE INTO threads_count ( _id, count ) VALUES (1,0);

D products/PurePhone/services/db/databases/scripts/sms_003.sql => products/PurePhone/services/db/databases/scripts/sms_003.sql +0 -8
@@ 1,8 0,0 @@
-- Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
-- For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
BEGIN TRANSACTION;
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (1,'Thanks for reaching out. I can''t talk right now, I''ll call you later',4);
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (2,'I''ll call you later',3);
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (3,'I''ll be there in 15 minutes',2);
INSERT OR REPLACE INTO "templates" ("_id","text","lastUsageTimestamp") VALUES (4,'Give me 5 minutes',5);
COMMIT;

A tools/add_dbs_to_version_json.py => tools/add_dbs_to_version_json.py +70 -0
@@ 0,0 1,70 @@
#!/usr/bin/python3
# Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

# import required module
import argparse
import sys
import json
import logging

log = logging.getLogger(__name__)
logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO)


def get_databases_for_product(input_path, product) -> list:
    try:
        with open(input_path, 'r') as src:
            databases_json_data = json.load(src)
            return databases_json_data[product]
    except Exception as e:
        log.error(e)
        return []


def write_databases_to_json(output_path, databases) -> int:
    try:
        with open(output_path, 'r+') as dst:
            version_json_data = json.load(dst)
            version_json_data.update(databases)
            dst.seek(0)
            json.dump(version_json_data, dst, indent=4)
            return 0
    except Exception as e:
        log.error(e)
        return -1


def main() -> int:
    parser = argparse.ArgumentParser(description='Append databases.json to version.json')
    parser.add_argument('--input_path',
                        metavar='databases_json_path',
                        type=str,
                        help='path to databases.json file',
                        required=True)

    parser.add_argument('--output_path',
                        metavar='version_json_path',
                        type=str,
                        help='path to version.json file',
                        required=True)

    parser.add_argument('--product',
                        metavar='BellHybrid/PurePhone',
                        type=str,
                        help='product which database entries should be loaded',
                        required=True)

    args = parser.parse_args()

    databases = get_databases_for_product(args.input_path, args.product)
    if not databases:
        return -1

    ret = write_databases_to_json(args.output_path, databases)

    return ret


if __name__ == "__main__":
    sys.exit(main())

M tools/init_databases.py => tools/init_databases.py +59 -31
@@ 8,33 8,45 @@ import sqlite3
import argparse
import logging
import sys
import json

log = logging.getLogger(__name__)
logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO)

databases_json_filename = "databases.json"
scripts_folder_name = "scripts"
migration_folder_name = "migration"

# this helper script creates DBs from SQL schema files

def initialize_database(script_path: os.path, dst_directory: os.path) -> int:
    file_name = os.path.basename(script_path)
# this helper script creates DBs from SQL schema files
def migrate_database_up(database: str, migration_path: os.path, dst_directory: os.path, dst_version: int, devel: bool):
    connection = None

    db_name = file_name.split("_0")[0]
    db_name = f"{db_name}.db"
    dst_db_path = os.path.join(dst_directory, db_name)
    log.debug(f"Executing {script_path} script into {dst_db_path} database")
    db_name_full = f"{database}.db"
    dst_db_path = os.path.join(dst_directory, db_name_full)

    ret = 0
    try:
        connection = sqlite3.connect(dst_db_path)
        with open(script_path) as fp:
            connection.executescript(fp.read())
        connection.commit()
        log.info(f"\nPerforming up-migration of {database} to {dst_version}")
        for i in range(dst_version+1):
            migration_script = os.path.join(migration_path, *[database, str(i), "up.sql"])
            devel_script = os.path.join(migration_path, *[database, str(i), "devel.sql"])
            with open(migration_script) as ms:
                connection.executescript(ms.read())
                connection.commit()
                if devel and os.path.exists(devel_script):
                    with open(devel_script) as ds:
                        connection.executescript(ds.read())
                connection.commit()
                connection.execute(f"PRAGMA user_version = {i};")
                connection.commit()

    except OSError as e:
        log.error(f"System error: {e}")
        ret = 1
    except sqlite3.Error as e:
        log.error(f"[SQLite] {db_name} database error: {e}")
        log.error(f"[SQLite] {database} database error: {e}")
        ret = 1
    finally:
        if connection:


@@ 43,12 55,32 @@ def initialize_database(script_path: os.path, dst_directory: os.path) -> int:
    return ret


def migrate_database_wrapper(migration_path: os.path, json: json, dst_directory: os.path, devel: bool) -> int:
    product = list(json.keys())[0]
    databases_json = json[product]["databases"]
    databases = os.listdir(migration_path)
    databases_to_migrate = set([database["name"] for database in databases_json]).intersection(databases)

    for database in databases_json:
        name = database["name"]
        if name in databases_to_migrate:
            migrate_database_up(name, migration_path, dst_directory, int(database["version"]), devel)

    return 0


def main() -> int:
    parser = argparse.ArgumentParser(description='Create databases from schema scripts')
    parser.add_argument('--input_path',
                        metavar='schema_path',
    parser.add_argument('--common_path',
                        metavar='common_path',
                        type=str,
                        help='path to schema scripts',
                        help='path to common databases scripts',
                        required=True)

    parser.add_argument('--product_path',
                        metavar='product_path',
                        type=str,
                        help='path to product-specific databases scripts',
                        required=True)

    parser.add_argument('--output_path',


@@ 65,28 97,24 @@ def main() -> int:

    args = parser.parse_args()

    db_script_files = [
        os.path.join(args.input_path, file)
        for file in os.listdir(args.input_path)
        if os.path.isfile(os.path.join(args.input_path, file)) and ".sql" in file
    ]
    db_script_devel = [file for file in db_script_files if "devel" in file]
    db_script_no_devel = list(set(db_script_files) - set(db_script_devel))
    ret = 0

    json_path = os.path.join(args.product_path, databases_json_filename)
    json_data = None

    db_script_devel.sort()
    db_script_no_devel.sort()
    if os.path.exists(json_path):
        with open(json_path, "r") as json_file:
            json_data = json.load(json_file)
    else:
        log.error("Json file does not exists!")
        return 1

    if not os.path.exists(args.output_path):
        os.makedirs(args.output_path, exist_ok=True)

    ret = 0

    for script in db_script_no_devel:
        ret |= initialize_database(script, args.output_path)

    if args.development:
        for script in db_script_devel:
            ret |= initialize_database(script, args.output_path)
    for database_path in [args.common_path, args.product_path]:
        migration_path = os.path.join(database_path, migration_folder_name)
        ret |= migrate_database_wrapper(migration_path, json_data, args.output_path, args.development)

    return ret