Giter Club home page Giter Club logo

evillord666 / reportgenerator Goto Github PK

View Code? Open in Web Editor NEW
9.0 2.0 6.0 512 KB

A small cross-database tool for building excel documents (reports) based on data from database that extacts via View or Stored Procedures with parametres, ordering e.t.c.

Home Page: https://evillord666.github.io/ReportGenerator/

License: GNU General Public License v3.0

C# 88.38% PLpgSQL 2.24% TSQL 9.38%
reports generator database excel reportgenerator database-reporting excel-export smart-reporting excel-to-sql sql-to-excel

reportgenerator's Introduction

ReportGenerator

1 Overview

A small tool (library) for generating reports (plain dataset) from Stored Procedures or Views following databases:

  • MS SQL (SQL Server)
  • MySQL
  • Postgres

These reports could be generated in MS Excel or CSV formats with templates files (typically Headers only). Logic of data extracting depends on parameters (variables) Stored procedures or Views (WHERE parameters, ORDER BY and GROUP BY parameters).

Report generator core passes parameters for data filtering in View or Variables for Stored Procedures, parameters are taking from:

    1. ExecutionConfig (.xml file)
    1. in runtime (as instance of ExecutionConfig class).

There are two ways for getting data:

  • from stored procedure (passing variables)
  • from view (passing where clauses, ordering and grouping conditions)

In both 2 ways there are a possibility to manipulate data selection by setting parameters (for stored procedure) and automatic sql generation for filtering View data. It should be noted that there is a configuration of View and StoredProcedure in xml.

1.1 Configuration

For Stored Procedure Configuration we must set StoredProcedure

Name to stored procedure name

And also if we have aparameters we should specify collection of StoredProcedureParameters like, ParameterType is a integer value which is specific for databaseEngine :

SQL Server - SqlDbType (https://msdn.microsoft.com/ru-ru/library/system.data.sqldbtype(v=vs.110).aspx)

MySQL Server - MySqlDbType (https://dev.mysql.com/doc/dev/connector-net/8.0/html/T_MySql_Data_MySqlClient_MySqlDbType.htm)

Postgres - NpgsqlDbType (https://www.npgsql.org/doc/api/NpgsqlTypes.NpgsqlDbType.html) ` Example of config for SQL server (examples of config could be found in ReportGenerator.Core.Tests/ExampleConfig)

<?xml version="1.0"?>
<ExecutionConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <!-- 
       If use SQL Server see for parameters Type : https://docs.microsoft.com/ru-ru/dotnet/api/system.data.sqldbtype 
    -->
    <DataSource>StoredProcedure</DataSource>
    <Name>GetSitizensByCityAndDateOfBirth</Name>
    <StoredProcedureParameters>
        <!-- SQL Server NVarChar enum value is 12-->
        <ParameterType>12</ParameterType> 
        <ParameterName>City</ParameterName>
        <ParameterValue xsi:type="xsd:string">N'Yekaterinburg</ParameterValue>
    </StoredProcedureParameters>
    <StoredProcedureParameters>
        <!-- SQL Server Int enum value is 8-->
        <ParameterType>8</ParameterType>
        <ParameterName>PostalCode</ParameterName>
        <ParameterValue xsi:type="xsd:string">620000</ParameterValue>
    </StoredProcedureParameters>
    <StoredProcedureParameters>
        <!-- SQL Server Date enum value is 4-->
        <ParameterType>4</ParameterType>
        <ParameterName>DateOfBirth</ParameterName>
        <ParameterValue xsi:type="xsd:string">'2018-01-01'</ParameterValue>
    </StoredProcedureParameters>
</ExecutionConfig>

For View Configuration is a little similar as DataSource we must note View:

View

Name is a name of View

But here we should also specify parameters for rows selection (WHERE, ORDER BY and GROUP BY parameters). All of them are represented with only one type - DbQueryParameter.

DbQueryParameter consist of following:

-collection of JoinCondition (works only for WHERE parameters) - is way of how parameters joined in WHERE statement by logical AND or logical OR, or maybe AND/OR with inversion - AND NOT Condition ... OR NOT Condition (see example below). Should be ommited for first parameter in WHERE statement.

-parameter Name make sense for every type of parameter (WHERE, ORDER BY and GROUP BY)

-comparison operator (make sense only for WHERE parameters) - is any valid SQL condition operator like >, <, =, IS, IS NOT, IN, BETWEEN, LIKE and so on...

-parameter value works for WHERE (value for comparison) and ORDER BY (ASC or DESC), THESE are DEFAULT values, they OF COURSE could be set during the runtime

Example:

<?xml version="1.0"?>
<ExecutionConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <DataSource>View</DataSource>
    <Name>CitizensView</Name>
    <ViewParameters>
        <WhereParameters>
            <DbQueryParameter>
                <ParameterName>FirstName</ParameterName>
                <ParameterValue>N'Michael'</ParameterValue>
                <ComparisonOperator>=</ComparisonOperator>
            </DbQueryParameter>
            <DbQueryParameter>
                <Conditions>
                    <JoinCondition>And</JoinCondition>
                    <JoinCondition>Not</JoinCondition>
                </Conditions>
                <ParameterName>City</ParameterName>
                <ParameterValue>N'Yekaterinburg'</ParameterValue>
                <ComparisonOperator>=</ComparisonOperator>
            </DbQueryParameter>
            <DbQueryParameter>
                <Conditions>
                    <JoinCondition>Between</JoinCondition>
                </Conditions>
                <ParameterName>Age</ParameterName>
                <ParameterValue>18 AND 60</ParameterValue>
            </DbQueryParameter>
            <DbQueryParameter>
                <Conditions>
                    <JoinCondition>In</JoinCondition>
                </Conditions>
                <ParameterName>District</ParameterName>
                <ParameterValue>(N'D1', N'A3', N'A5', N'C7')</ParameterValue>
            </DbQueryParameter>
            <DbQueryParameter>
                <Conditions>
                    <JoinCondition>Or</JoinCondition>
                </Conditions>
                <ParameterName>Region</ParameterName>
                <ParameterValue>N'Sverdlovskaya oblast'</ParameterValue>
                <ComparisonOperator>!=</ComparisonOperator>
            </DbQueryParameter>
        </WhereParameters>
        <OrderByParameters>
            <DbQueryParameter>
                <ParameterName>FirstName</ParameterName>
                <ParameterValue>ASC</ParameterValue>
            </DbQueryParameter>
            <DbQueryParameter>
                <ParameterName>LastName</ParameterName>
                <ParameterValue>DESC</ParameterValue>
            </DbQueryParameter>
        </OrderByParameters>
        <GroupByParameters>
            <DbQueryParameter>
                <ParameterName>District</ParameterName>
            </DbQueryParameter>
        </GroupByParameters>
    </ViewParameters>
</ExecutionConfig>

2 Example of usage

Top interface is - IReportGeneratorManager. For all functionality were written Unit tests with xUnit (sucks), full example could be found in unit test project (ReportGenerator.Core.Tests/ReportsGenerator/TestExcelReportGeneratorManager):

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Logging;
    using ReportGenerator.Core.Database;
    using ReportGenerator.Core.Database.Managers;
    using ReportGenerator.Core.Database.Utils;
    using ReportGenerator.Core.Helpers;
    using ReportGenerator.Core.ReportsGenerator;
    using Xunit;

    namespace ReportGenerator.Core.Tests.ReportsGenerator
    {
        public class TestExcelReportGeneratorManager
        {
            [Fact]
            public void TestGenerateReportSqlServer()
            {
                _testSqlServerDbName = TestSqlServerDatabasePattern + "_" + DateTime.Now.Millisecond.ToString();
                SetUpSqlServerTestData();
                // executing extraction ...
                object[] parameters = ExcelReportGeneratorHelper.CreateParameters(1, 2, 3);
                ILoggerFactory loggerFactory = new LoggerFactory();
                IReportGeneratorManager manager = new ExcelReportGeneratorManager(loggerFactory, DbEngine.SqlServer, 
                                                                                  TestSqlServerHost, _testSqlServerDbName);
                Task<int> result = manager.GenerateAsync(TestExcelTemplate, SqlServerDataExecutionConfig, ReportFile, parameters);
                result.Wait();
                Assert.True(result.Result > 0);
                TearDownSqlServerTestData();
            }

            [Fact]
            public void TestGenerateReportSqLite()
            {
                SetUpSqLiteTestData();
                object[] parameters = ExcelReportGeneratorHelper.CreateParameters(1, 2, 3);
                ILoggerFactory loggerFactory = new LoggerFactory();
                loggerFactory.AddConsole();
                loggerFactory.AddDebug();
                IReportGeneratorManager manager = new ExcelReportGeneratorManager(loggerFactory, DbEngine.SqLite, _connectionString);
                Task<int> result = manager.GenerateAsync(TestExcelTemplate, SqLiteDataExecutionConfig, ReportFile, parameters);
                result.Wait();
                Assert.True(result.Result > 0);
                TearDownSqLiteTestData();
            }
        
            [Fact]
            public void TestGenerateReportPostgres()
            {
                SetUpPostgresSqlTestData();
                object[] parameters = ExcelReportGeneratorHelper.CreateParameters(1, 2, 3);
                ILoggerFactory loggerFactory = new LoggerFactory();
                loggerFactory.AddConsole();
                loggerFactory.AddDebug();
                IReportGeneratorManager manager = new ExcelReportGeneratorManager(loggerFactory, DbEngine.PostgresSql, _connectionString);
                Task<int> result = manager.GenerateAsync(TestExcelTemplate, PostgresSqlViewDataExecutionConfig, ReportFile, parameters);
                result.Wait();
                Assert.True(result.Result > 0);
                TearDownPostgresSqlTestData();
            }
        
            [Fact]
            public void TestGenerateReportMySql()
            {
                SetUpMySqlTestData();
                object[] parameters = ExcelReportGeneratorHelper.CreateParameters(1, 2, 3);
                ILoggerFactory loggerFactory = new LoggerFactory();
                loggerFactory.AddConsole();
                loggerFactory.AddDebug();
                IReportGeneratorManager manager = new ExcelReportGeneratorManager(loggerFactory, DbEngine.MySql, _connectionString);
                Task<int> result = manager.GenerateAsync(TestExcelTemplate, MySqlDataExecutionConfig, ReportFile, parameters);
                result.Wait();
                Assert.True(result.Result > 0);
                TearDownMySqlTestData();
            }

            private void SetUpSqlServerTestData()
            {
                _dbManager = new CommonDbManager(DbEngine.SqlServer, _loggerFactory.CreateLogger<CommonDbManager>());
                IDictionary<string, string> connectionStringParams = new Dictionary<string, string>()
                {
                    {DbParametersKeys.HostKey, TestSqlServerHost},
                    {DbParametersKeys.DatabaseKey, _testSqlServerDbName},
                    {DbParametersKeys.UseIntegratedSecurityKey, "true"},
                    {DbParametersKeys.UseTrustedConnectionKey, "true"}
                };
                _connectionString = ConnectionStringBuilder.Build(DbEngine.SqlServer, connectionStringParams);
                _dbManager.CreateDatabase(_connectionString, true);
                // 
                string createDatabaseStatement = File.ReadAllText(Path.GetFullPath(SqlServerCreateDatabaseScript));
                string insertDataStatement = File.ReadAllText(Path.GetFullPath(SqlServerInsertDataScript));
                _dbManager.ExecuteNonQueryAsync(_connectionString, createDatabaseStatement).Wait();
                _dbManager.ExecuteNonQueryAsync(_connectionString, insertDataStatement).Wait();
            }

            private void TearDownSqlServerTestData()
            {
                _dbManager.DropDatabase(_connectionString);
            }

            private void SetUpSqLiteTestData()
            {
                _dbManager = new CommonDbManager(DbEngine.SqLite, _loggerFactory.CreateLogger<CommonDbManager>());
                IDictionary<string, string> connectionStringParams = new Dictionary<string, string>()
                {
                    {DbParametersKeys.DatabaseKey, TestSqLiteDatabase},
                    {DbParametersKeys.DatabaseEngineVersion, "3"}
                };
                _connectionString = ConnectionStringBuilder.Build(DbEngine.SqLite, connectionStringParams);
                _dbManager.CreateDatabase(_connectionString, true);
                string createDatabaseStatement = File.ReadAllText(Path.GetFullPath(SqLiteCreateDatabaseScript));
                string insertDataStatement = File.ReadAllText(Path.GetFullPath(SqLiteInsertDataScript));
                _dbManager.ExecuteNonQueryAsync(_connectionString, createDatabaseStatement).Wait();
                _dbManager.ExecuteNonQueryAsync(_connectionString, insertDataStatement).Wait();
            }

            private void TearDownSqLiteTestData()
            {
                _dbManager.DropDatabase(_connectionString);
            }
        
            private void SetUpMySqlTestData()
            {
                _dbManager = new CommonDbManager(DbEngine.MySql, _loggerFactory.CreateLogger<CommonDbManager>());
                IDictionary<string, string> connectionStringParams = new Dictionary<string, string>()
                {
                    {DbParametersKeys.HostKey, TestMySqlHost},
                    {DbParametersKeys.DatabaseKey, TestMySqlDatabase},
                    // {DbParametersKeys.UseIntegratedSecurityKey, "true"} // is not working ...
                    {DbParametersKeys.LoginKey, "root"},
                    {DbParametersKeys.PasswordKey, "123"}
                };
                _connectionString = ConnectionStringBuilder.Build(DbEngine.MySql, connectionStringParams);
                _dbManager.CreateDatabase(_connectionString, true);
                string createDatabaseStatement = File.ReadAllText(Path.GetFullPath(MySqlCreateDatabaseScript));
                string insertDataStatement = File.ReadAllText(Path.GetFullPath(MySqlInsertDataScript));
                _dbManager.ExecuteNonQueryAsync(_connectionString, createDatabaseStatement).Wait();
                _dbManager.ExecuteNonQueryAsync(_connectionString, insertDataStatement).Wait();
            }

            private void TearDownMySqlTestData()
            {
                _dbManager.DropDatabase(_connectionString);
            }

            private void SetUpPostgresSqlTestData()
            {
                _dbManager = new CommonDbManager(DbEngine.PostgresSql, _loggerFactory.CreateLogger<CommonDbManager>());
                IDictionary<string, string> connectionStringParams = new Dictionary<string, string>()
                {
                    {DbParametersKeys.HostKey, TestPostgresSqlHost},
                    {DbParametersKeys.DatabaseKey, TestPostgresSqlDatabase},
                    // {DbParametersKeys.UseIntegratedSecurityKey, "true"} // is not working ...
                    {DbParametersKeys.LoginKey, "postgres"},
                    {DbParametersKeys.PasswordKey, "123"}
                };
                _connectionString = ConnectionStringBuilder.Build(DbEngine.PostgresSql, connectionStringParams);
                _dbManager.CreateDatabase(_connectionString, true);
                string createDatabaseStatement = File.ReadAllText(Path.GetFullPath(PostgresSqlCreateDatabaseScript));
                string insertDataStatement = File.ReadAllText(Path.GetFullPath(PostgresSqlInsertDataScript));
                _dbManager.ExecuteNonQueryAsync(_connectionString, createDatabaseStatement).Wait();
                _dbManager.ExecuteNonQueryAsync(_connectionString, insertDataStatement).Wait();
            }
        
            private void TearDownPostgresSqlTestData()
            {
                _dbManager.DropDatabase(_connectionString);
            }

            private const string TestExcelTemplate = @"..\..\..\TestExcelTemplates\CitizensTemplate.xlsx";
            private const string ReportFile = @".\Report.xlsx";
            private const string SqlServerDataExecutionConfig = @"..\..\..\ExampleConfig\sqlServerDataExtractionParams.xml";
            private const string SqLiteDataExecutionConfig = @"..\..\..\ExampleConfig\sqLiteDataExtractionParams.xml";
            private const string MySqlDataExecutionConfig = @"..\..\..\ExampleConfig\mySql_testReport4_StoredProcedure.xml";
            private const string PostgresSqlViewDataExecutionConfig = @"..\..\..\ExampleConfig\postgresViewDataExtractionParams.xml";
            private const string PostgresSqlStoredProcedureDataExecutionConfig = @"..\..\..\ExampleConfig\postgresStoredProcDataExtractionParams.xml";

            private const string TestSqlServerHost = @"(localdb)\mssqllocaldb";
            private const string TestSqlServerDatabasePattern = "ReportGeneratorTestDb";
            private const string TestSqLiteDatabase = "ReportGeneratorTestDb.sqlite";
            private const string TestMySqlHost = "localhost";
            private const string TestMySqlDatabase = "ReportGeneratorTestDb";
            private const string TestPostgresSqlHost = "localhost";
            private const string TestPostgresSqlDatabase = "ReportGeneratorTestDb";

            private const string SqlServerCreateDatabaseScript = @"..\..\..\DbScripts\SqlServerCreateDb.sql";
            private const string SqlServerInsertDataScript = @"..\..\..\DbScripts\SqlServerCreateData.sql";
            private const string SqLiteCreateDatabaseScript = @"..\..\..\DbScripts\SqLiteCreateDb.sql";
            private const string SqLiteInsertDataScript = @"..\..\..\DbScripts\SqLiteCreateData.sql";
            private const string MySqlCreateDatabaseScript = @"..\..\..\DbScripts\MySqlCreateDb.sql";
            private const string MySqlInsertDataScript = @"..\..\..\DbScripts\MySqlCreateData.sql";
            private const string PostgresSqlCreateDatabaseScript = @"..\..\..\DbScripts\PostgresSqlCreateDb.sql";
            private const string PostgresSqlInsertDataScript = @"..\..\..\DbScripts\PostgresSqlCreateData.sql";

            private string _testSqlServerDbName;
            private string _connectionString;
            private readonly ILoggerFactory _loggerFactory = new LoggerFactory();
            private IDbManager _dbManager;
        }
    }
  `

## 3 Service for Dependency injection
  
Here is example of how to use ReportGenerator with dependency injection:
`csharp
    using System;
    using System.Collections.Generic;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using ReportGenerator.Core.Database;
    using ReportGenerator.Core.Database.Managers;
    using ReportGenerator.Core.Database.Utils;
    using ReportGenerator.Core.Extensions;
    using ReportGenerator.Core.ReportsGenerator;
    using Xunit;

    namespace ReportGenerator.Core.Tests.Extensions
    {
        public class ServiceCollectionExtensionsTests : IDisposable
        {
            public ServiceCollectionExtensionsTests()
            {
                _testDbName = TestDatabasePattern + "_" + DateTime.Now.ToString("YYYYMMDDHHmmss");
                _dbManager = new CommonDbManager(DbEngine.SqlServer, _loggerFactory.CreateLogger<CommonDbManager>());
                IDictionary<string, string> connectionStringParams = new Dictionary<string, string>()
                {
                    {DbParametersKeys.HostKey, Server},
                    {DbParametersKeys.DatabaseKey, _testDbName},
                    {DbParametersKeys.UseIntegratedSecurityKey, "true"},
                    {DbParametersKeys.UseTrustedConnectionKey, "true"}
                };
                _connectionString = ConnectionStringBuilder.Build(DbEngine.SqlServer, connectionStringParams);
                _dbManager.CreateDatabase(_connectionString, true);
                _services = new ServiceCollection();
                _services.AddScoped<ILoggerFactory>(_ => new LoggerFactory());
                _services.AddReportGenerator(DbEngine.SqlServer, _connectionString);
            }

            public void Dispose()
            {
                _dbManager.DropDatabase(_connectionString);
            }

            [Fact]
            public void TestServiceInstantiationViaProvider()
            {
                IServiceProvider serviceProvider = _services.BuildServiceProvider();
                IReportGeneratorManager reportGenerator = serviceProvider.GetService<IReportGeneratorManager>();
                
                Assert.NotNull(reportGenerator);
            }

            private const string Server = @"(localdb)\mssqllocaldb";
            private const string TestDatabasePattern = "ReportGeneratorTestDb";

            private readonly IServiceCollection _services;
            private readonly string _testDbName;
            private readonly string _connectionString;
            private IDbManager _dbManager;
            private readonly ILoggerFactory _loggerFactory = new LoggerFactory();
        }
    }

4 GUI

A Full Web application (NET Core) with usage Report Generator as DI service will be implemented in this project: https://github.com/Wissance/ReportGeneratorWebGui it is also allow to use Web API for ReportsGeneration

5 Nuget Package

Nuget package is available on nuget.org : https://www.nuget.org/packages/ReportsGenerator/ There are some strange troubles with import of version 1.1.0 and 1.1.1 they are Broken and unlisted. ACTUAL NUGET PACKAGE IS 1.1.2

reportgenerator's People

Contributors

evillord666 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

reportgenerator's Issues

Passing arguments values simultaneously with parameters

Now we have 2 ways to passing data:
-default values in xml describes stored procedure parameters of data filtering
-via separate method (passing ExecutionConfig config from runtime)

It would be good to pass values for ExecutionConfig parameters for method reading config from file

Release 1.1.0

I should do following:

  1. Edit readme
  2. Create new nuget pacakge

Change interface in v2

All generator and managers

  • reportFile should be passes to Generate method, not constructor
  • same for template
  • add possibility to filter result by columns in Genereate method (i.e. ommit some result data if they not present in interested columns scope)

... tbd

Smart logging

Add logging for db reports into *ReportsGeneratorManager (only one point), so we somehow should provide results of task execution maybe via some parameter(s).

Multiple DB data extractor

Possibly it is good to combine data from different instances of databases:

  1. From different databases from same engine
  2. From different databases from different engines

Support other DB engine

Test or maybe support (i don't know is it works with others DB engines or not):
-postgres
-mysql
-sqlite

Unit testing developing

At first time most of tests were developed for Sql Server (MS SQL) during this task we should test with all supported databases, and move common code to private methods (now i have places with copy-paste methods which could be parametrized).

Check parameters type conversion

In SimpleDbExtractor in ExtractAsync method we are passing parametetrs like:

   procedureParameter.Value = parameter.ParameterValue;

what if instead of integer like 4 i'll pass "4" as str. I suppose that there is issue with type conversion

Create extension for DI

Create extension for IServiceCollection that allow to create instances using Dependency Injection.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.