«История разработки eazyBI», Raimonds Simanovskis (eazyBI, Латвия)

  • View
    3.250

  • Download
    4

  • Category

    Software

Preview:

Citation preview

История разработки

Raimonds Simanovskis@rsim

Из Латвии

Из Юрмалы

В Москвев 2003 году

Что такое

Easy-to-useBusiness Intelligence

web application

?

Различные источники данных

Импорт данных

Простое создание отчетов

Много разных диаграмм

Мощные расчеты

История разработки2011 eazyBI.com hosted service

REST API import2012Private eazyBI standalone server

eazyBI plugin2013 Atlassian UI design framework

eazyBI add-on for JIRA Cloudusing Atlassian Connect

data import

2014 Custom REST API, SQL importIntegrations with other JIRA plugins

2015 JIRA Data Center supportSeparate child JVM process

2016 New UI design improvements

История разработки

Упрощенная архитектураИсточники данных

JIRA импорт

REST импорт

CSV импорт

МногомерныйOLAP куб

ИзмеренияФакты

схема “звезды”

Pасчетные формулы используя язык MDX

Создание табличных отчетов

Создание панелей из несколько отчетов

Гаджеты на рабочем столе JIRA и на страницах Confluence

Создание диаграмм разных типов

Основные технические компоненты

MySQLPostgre

SQLOracle MS

SQL

JRuby on Rails

Mondrian OLAP

<?xml version="1.0" encoding="UTF-8"?><Schema name="default"> <Cube name="Sales"> <Table name="sales"/> <Dimension foreignKey="customer_id" name="Customers"> <Hierarchy allMemberName="All Customers" hasAll="true" primaryKey="id"> <Table name="customers"/> <Level column="country" name="Country" uniqueMembers="true"/> <Level column="state_province" name="State Province" uniqueMembers="true"/> <Level column="city" name="City" uniqueMembers="false"/> <Level column="fullname" name="Name" uniqueMembers="false"/> </Hierarchy> </Dimension> <Dimension foreignKey="time_id" name="Time" type="TimeDimension"> <Hierarchy hasAll="false" primaryKey="id"> <Table name="time"/> <Level column="the_year" levelType="TimeYears" name="Year" type="Numeric" uniqueMembers="true"/> <Level column="quarter" levelType="TimeQuarters" name="Quarter" uniqueMembers="false"/> <Level column="month_of_year" levelType="TimeMonths" name="Month" type="Numeric" uniqueMembers="false"/> </Hierarchy> </Dimension> <Measure aggregator="sum" column="unit_sales" name="Unit Sales"/> <Measure aggregator="sum" column="store_sales" name="Store Sales"/> </Cube></Schema>

XML или …

schema = Mondrian::OLAP::Schema.define do cube 'Sales' do table 'sales' dimension 'Customers', foreign_key: 'customer_id' do hierarchy has_all: true, all_member_name: 'All Customers', primary_key: 'id' do table 'customers' level 'Country', column: 'country', unique_members: true level 'State Province', column: 'state_province', unique_members: true level 'City', column: 'city', unique_members: false level 'Name', column: 'fullname', unique_members: false end end dimension 'Time', foreign_key: 'time_id', type: 'TimeDimension' do hierarchy has_all: false, primary_key: 'id' do table 'time' level 'Year', column: 'the_year', type: 'Numeric', unique_members: true, level_type: 'TimeYears' level 'Quarter', column: 'quarter', unique_members: false, level_type: 'TimeQuarters' level 'Month', column: 'month_of_year', type: 'Numeric', unique_members: false, level_type: 'TimeMonths' end end measure 'Unit Sales', column: 'unit_sales', aggregator: 'sum' measure 'Store Sales', column: 'store_sales', aggregator: 'sum' end end

DSL в Ruby

Фреймворк веб приложенийRuby on Rails

ActionController

ActiveRecord

ActionView

Browser

Request Router

Response

Database

SQL

Продукты

eazyBICloud

eazyBIfor JIRA

PrivateeazyBI

Build & DeployRuby on Rails application

JRubyTorqueBox

Application Gems

.com

deploy

Mondrian OLAP engine

Other Java libraries

Packaged application

Application Gems

TorqueBox

Privatepackage

pluginOSGi bundleJRuby *.jar

Application Gems

jruby-rack

package

ИнтеграцияeazyBI / JRuby

c JIRA Java API

Вызов JIRA Java APImodule SAL { :userManager => Java::com.atlassian.sal.api.user.UserManager, :loginUriProvider => Java::com.atlassian.sal.api.auth.LoginUriProvider, :pluginSettingsFactory => Java::com.atlassian.sal.api.pluginsettings.PluginSettingsFactory, :applicationProperties => Java::com.atlassian.sal.api.ApplicationProperties }.each do |method_name, klass| mattr_accessor method_name send "#{method_name}=", JiraComponentAccessor.getOSGiComponentInstanceOfType(klass.java_class) end # get global plugin settings mattr_accessor :pluginSettings self.pluginSettings = pluginSettingsFactory.createGlobalSettings

if SAL.applicationProperties.getVersion.split('.').first.to_i >= 6 def self.get_base_url SAL.applicationProperties.getBaseUrl(Java::com.atlassian.sal.api.UrlMode::CANONICAL) end else def self.get_base_url SAL.applicationProperties.getBaseUrl end endend

Поддержка разных версий JIRA

JAVA reflection

def get_jira_user_key(jira_user) if jira_user.respond_to?(:getKey) jira_user.getKey || jira_user.getName elsif JIRA.userManager.respond_to?(:getUserByName) jira_user = JIRA.userManager.getUserByName(jira_user.getName) jira_user.getKey || jira_user.getName else jira_user.getName endend

Исследование, как получить данные из

JIRA Service Deskjcft = jcf.getCustomFieldType

if field = jcft.java_class.declared_fields.detect{|f| f.name == "goalService"} field.accessible = true @service_desk_goal_service = field.value(jcft)# Starting from JIRA Service Desk 3.1.0elsif field = jcft.java_class.declared_fields.detect{|f| f.name == "goalViewService"} field.accessible = true goal_view_service = field.value(jcft) field = goal_view_service.java_class.declared_field("goalService") field.accessible = true @service_desk_goal_service = field.value(goal_view_service)else raise ArgumentError, "Cannot get #{jcft.java_class} goalService"end

Извлечение файлов из jar арxива плагина

# expand rails.root and gem.home bundle directories$bundle = $servlet_context.getClass.getClassLoader.getBundlem_archive_field = $bundle.java_class.declared_field("m_archive")m_archive_field.accessible = truebundle_archive = m_archive_field.value($bundle)

jar_content = if bundle_archive.respond_to?(:getRevisionCount) bundle_archive.getRevision(bundle_archive.getRevisionCount - 1).getContentelse bundle_archive.getCurrentRevision.getContentend%w(rails.root gem.home).each do |directory_name| dir = jar_content.getEntryAsContent("META-INF/#{directory_name}") dir.getEntries.each{|e| dir.getEntryAsNativeLibrary(e)}end

Поддержка разных СУБД

eazyBI accountsAccount 1 Account 2 Account N

Projects A,B Project C Projects X,Y,Z

DB schemaeazybi_jira_dwh_1

Measur

Dimensi Dimensi

DimensiDimensi

DB schemaeazybi_jira_dwh_2

Measur

Dimensi Dimensi

DimensiDimensi

DB schemaeazybi_jira_dwh_N

Measur

Dimensi Dimensi

DimensiDimensi

MySQL databases

eazybi_jira_dwh_1jira_projects

jira_statuses

jira_issues

eazybi_jira_dwh_2jira_projects

jira_statuses

jira_issues

eazybi_jira_dwh_3jira_projects

jira_statuses

jira_issues

eazybi_jirausers

accounts

cube_reports

eazybi_jira database

PostgreSQL, MS SQL schemas

eazybi_jira_dwh_1jira_projects

jira_statuses

jira_issues

eazybi_jira_dwh_2jira_projects

jira_statuses

jira_issues

eazybi_jira_dwh_3jira_projects

jira_statuses

jira_issues

default schemausers

accounts

cube_reports

eazybi_jira schema

Oracle prefixed tables

#1_jira_projects

#1_jira_statuses

#1_jira_issues

#2_jira_projects

#2_jira_statuses

#2_jira_issues

#3_jira_projects

#3_jira_statuses

#3_jira_issues

users

accounts

cube_reports

Другие различия

• Размер имени таблиц/колонок в Oracle (до 30 символов)

• Разный синтаксис и функции SQL

• Разные типи данних

• Разные исключения и ошибки

Общая JIRA JVM JIRA JVM process heap

JIRA objects…

……

Plugin 1…

……

Plugin 2…

……

eazyBI…

……

Mondrian OLAP engine…

……

Results cache

Дочерний JVM процесс JIRA JVM process heap

JIRA objects…

……

Plugin 1…

……

eazyBI…

……

eazyBI child processMondrian OLAP engine

……

eazyBI…

……

Results cache

HTTP

Atlassian Connect –фреймворк для

JIRA Cloud плагинов

Atlassian Connect JIRA Cloud eazyBI Cloud

iframeeazyBI web app

……JIRA Cloud accounts

eazyBI jobs…

…JIRA import

REST API

JWT authentication

JWT authentication

JIRA instance…

…Data

DB

Импорт JIRA issues используя REST API

searchstartAt=0 maxResults=200

searchstartAt=200 maxResults=200

searchstartAt=400 maxResults=200

searchstartAt=600 maxResults=200

searchstartAt=800 maxResults=200

searchstartAt=1000 maxResults=200

HTTP 504 timeout

Ключевые факторы успеха

• Гибкая архитектура

• JVM платформа

• Повторное использование существующих библиотек

• Участие в open source проектах:JRuby, Ruby on Rails, Mondrian

Recommended