Introduction
In the Reports in TeamWox and Their Practical Use article we've briefly summarized the nature of reports. This tool allows you to visualize various factors of company's performance based on data from TeamWox modules in the form of various charts.
In addition to standard reports for modules shipped with TeamWox, SDK enables you to create custom reports for any TeamWox module. In this way you can extend TeamWox functionality, customizing it for your needs.
In this article we will examine the reporting tools, provided by TeamWox server API. Then we will move from theory to practice by creating a custom report for the Hello World module.
Reports Interfaces
In TeamWox system all reports (both standard and custom) are created using built-in system reports module (TWX_REPORTS
). It sets up the basic environment to display reports pages.
To create a custom report your module must implement the IReportsProvider
interface, i.e. reports provider. A class that will implement it - is a custom reports provider, that provides reports data to module manager.
Reports providers should be initialized during module initialization phase (IModule::Initialize
). On the second phase of module initialization (IModule::PostInitialize
) you must get the reports manager interface IReportsManager
, and using its IReportsManager::Register
method you should register module's reports in the system reports module TWX_REPORTS
.
The following 4 methods should be implemented in your reports provider class:
PreProcess
- Prepare module to process report pages.Process
- Redirect HTTP requests to report pages.GetCount
- Get number of reports for the module.GetInfo
- Report's service information.
When the PreProcess
and Process
methods are called, one of the parameters passed - is the pointer to the IReportsParams
interface, that is used to set up reports parameters.
Reports provider retrieves data and provides results in JSON format. This universal format in addition is used to visualize reports. In UI of page data are visualized using the Chart control from TeamWox library.
User Interface
The user interface of reports module consists of two frames and looks as follows.
The first frame displays the reports module menu that contains the list of available reports. Modules' names are represented as parent nodes, and reports themselves - as their children. For modules from standard TeamWox delivery custom reports are listed first and then come standard reports.
The second frame displays the contents of report page. The report page includes the page header with standard commands, the band with report UI controls (it is set by the IReportsParams::SetFlags
method) and the report charts.
The Chart Control
In TeamWox all the charts (there are several types of charts) are created using the Chart
control. It is created just as well as all other elements of page interface - using the TeamWox.Control()
method. Consider the constructor of the Chart
control in details.
Chart(chartType, data)
The class constructor accepts two parameters:
- chartType - The type of chart. There are several types of charts: regular charts, charts with cumulative and normalized values, pie charts, etc. For complete list see the current version of TeamWox SDK documentation, the TeamWox.Chart.Type section (currently in Russian only).
- data - JSON object containing parameters of selected chart type and data series.
Series - is a sequence of data that are used to create report's chart. There are several types of series that can be applied to certain types of charts. In turn, for a certain type of series a certain set of settings and display types can be applied. For more information, see TeamWox SDK documentation.
Note that it is not necessary to set chart data in the Chart
class constructor (i.e. data
- is an optional parameter). While working with chart data you'll gain much more flexibility using methods of the Chart
class (for complete list see documentation, we will use some of them in our example).
Reports Routing Rules
The TeamWox reports system module (TWX_REPORTS
) defines the URL format for reports pages' HTTP requests. It must consist of 3 parts:
reports/module_name/report_name
Let's consider them in detail:
1. reports. Redirects HTTP request to process page by system reports module. In this case the reports user interface is loaded. In the module's main page header this part of URL should be included only for the reports pages, since they will be processed by the system reports module TWX_REPORTS
.
2. module_name. Module name is set in the structure of the ModuleInfo
type, containing information about the module (the name
field). Module name is displayed as parent node in the first frame of reports UI along with other modules' names, for which reports are available.
3. report_name. This part of URL specifies the first page that will display report in the second frame of the reports module user interface. You can create multiple reports for one module. Each report must have a corresponding page.
You should put command that opens report page into header of module's main page. For example, the Users report page URL (the Tasks module) looks as follows:
/reports/tasks/users
Creating Custom Report for the Hello World Module
In the previous TeamWox SDK: Interaction with DBMS article you have known how the basic interaction with TeamWox DBMS is organized (on example of Hello World training module). We'll continue to work with this module and will add a simple report - distribution of U.S. presidents by their political parties on the simple chart with values on horizontal axis. Here is how it will look like.
Let's begin.
1. Adding New Fields to the Table
So we need a numerical dependency. For this we have to add the new field (lets name it party
) in the table with U.S. presidents names. This field will store numeric values corresponding to the names of political parties.
1.1. In the \API\HelloWorld.h
add the new field in the HelloWorldRecord
structure.
//+------------------------------------------------------------------+ //| Record structure | //+------------------------------------------------------------------+ struct HelloWorldRecord { INT64 id; wchar_t name[256]; int party; };
1.2. Now add this field to DBMS table. To this end, in the CHelloWorldManager::DBTableCheck
method supplement the SQL query text.
//+------------------------------------------------------------------+ //| Create HELLOWORLD table | //+------------------------------------------------------------------+ TWRESULT CHelloWorldManager::DBTableCheck(ISqlBase *sql) { //--- Checks if(sql==NULL) ReturnError(RES_E_INVALID_ARGS); //--- TWRESULT res=RES_S_OK; //--- if(RES_FAILED(res=sql->CheckTable("HELLOWORLD", "ID BIGINT DEFAULT 0 NOT NULL," "NAME VARCHAR(256) DEFAULT '' NOT NULL," "PARTY INTEGER DEFAULT 0 NOT NULL", "PRIMARY KEY (ID)", "DESCENDING INDEX IDX_HELLOWORLD_ID_DESC (ID)", NULL, NULL, 0))) ReturnError(res); //--- ExtLogger(NULL,LOG_STATUS_INFO) << "Table 'HELLOWORLD' checked"; //--- return(RES_S_OK); }
1.3. In the C.R.U.D. methods implementations adjust SQL requests and add the new field into structures containing parameters of requests.
CHelloWorldManager::InfoGet
//--- Text of SQL request to select records from HELLOWORLD table and sort them by ID. char query_select[] ="SELECT id,name,party FROM helloworld ORDER BY id ROWS ? TO ?"; //--- "Bind" data to parameters of request SqlParam params_query_select[] ={ SQL_INT64, &rec_info_get.id, sizeof(rec_info_get.id), SQL_WTEXT, rec_info_get.name, sizeof(rec_info_get.name), SQL_LONG, &rec_info_get.party, sizeof(rec_info_get.party) };
CHelloWorldManager::Update
//--- Text of SQL request to add new record in HELLOWORLD table char query_insert[] ="INSERT INTO helloworld(party,name,id) VALUES(?,?,?)"; //--- Text of SQL request to modify existing record in HELLOWORLD table char query_update[] ="UPDATE helloworld SET party=?,name=? WHERE id=?"; //--- "Bind" data to parameters of request SqlParam params_query[] ={ SQL_LONG, &record->party, sizeof(record->party), SQL_WTEXT, record->name, sizeof(record->name), SQL_INT64, &record->id, sizeof(record->id) };
CHelloWorldManager::Get
//--- Text of SQL request to get row from HELLOWORLD table by specified ID. char query_select_string[] ="SELECT id,name,party FROM helloworld WHERE id=?"; //--- Parameter of request SqlParam params_string[] ={ SQL_INT64,&id,sizeof(id) }; //--- "Bind" data to parameters of request SqlParam params_query_select_string[] ={ SQL_INT64, &record->id, sizeof(record->id), SQL_WTEXT, record->name, sizeof(record->name), SQL_LONG, &record->party, sizeof(record->party) };
1.4. Add new field in HTTP API and UI of pages.
CPageNumberTwo::JSONDataOutput
(Note - the names of parties are represented as keys in module's language file).
TWRESULT CPageNumberTwo::JSONDataOutput(const Context *context,CHelloWorldManager *manager) { TWRESULT res = RES_S_OK; //--- if(context==NULL || context->response==NULL) return(RES_E_INVALID_ARGS); //--- static wchar_t title[][64]={ L"HELLOWORLD_REPORT_REPUBLICAN", L"HELLOWORLD_REPORT_DEMOCRATIC", L"HELLOWORLD_REPORT_DEMOREP", L"HELLOWORLD_REPORT_FEDERALIST", L"HELLOWORLD_REPORT_WHIG", L"HELLOWORLD_REPORT_NATUNION", L"HELLOWORLD_REPORT_NOPARTY" }; //--- m_info_count=_countof(m_info); if(RES_FAILED(res=manager->InfoGet(context,m_info,m_start,&m_info_count))) ReturnErrorExt(res,context,"failed get info records"); //--- CJSON json(context->response); json << CJSON::ARRAY; for(int i=0;i<m_info_count;i++) { json << CJSON::OBJECT; //--- json << L"id" << m_info[i].id; json << L"name" << m_info[i].name; //--- if(m_info[i].party>=0 && m_info[i].party<_countof(title)) json << L"party" << m_server->GetString(context, NULL, title[m_info[i].party], NULL); else json << L"party" << m_server->GetString(context, NULL, L"UNKNOWN_PARTY", NULL); //--- json << CJSON::CLOSE; } //--- json << CJSON::CLOSE; return(RES_S_OK); }
CPageEdit::OnUpdate
//--- Filling out the fields StringCchCopy(m_record.name, _countof(m_record.name), context->request->GetString(IRequest::POST,L"name")); m_record.party=context->request->GetInt32(IRequest::POST,L"party");
CPageEdit::Tag
//--- if(TagCompare(L"party",tag)) { StringCchPrintf(str_party,_countof(str_party),L"",m_record.party); context->response->WriteSafe(str_party,IResponse::REPLACE_JAVASCRIPT); return(false); }
number2.tpl
header: [ {id:'number', content:'<lngj:HELLOWORLD_POSITION/>'}, {id:'name', content:'<lngj:HELLOWORLD_NAME/>'}, {id:'party', content:'<lngj:HELLOWORLD_PARTY/>'} ]
//--- Write data into the records array records.push([ {id:'number', content:data[i].id}, {id:'name', content:data[i].name,toolbar:[['edit',top.Toolbar.Edit],['delete',top.Toolbar.Delete]]}, {id:'party', content:data[i].party}
edit.tpl
. We will set the party by choosing value from the drop-down list. For this we will use theCombobox
class - subclass ofInput
.
items: [ [ TeamWox.Control('Label','<lngj:HELLOWORLD_NAME>','name'), TeamWox.Control('Input','text','name','<tw:name />') ], [ TeamWox.Control('Label','<lngj:HELLOWORLD_PARTY>','party'), TeamWox.Control('Input','combobox','party','<tw:party />', { options : [ [0,'<lngj:HELLOWORLD_REPORT_REPUBLICAN />'], [1,'<lngj:HELLOWORLD_REPORT_DEMOCRATIC />'], [2,'<lngj:HELLOWORLD_REPORT_DEMOREP />'], [3,'<lngj:HELLOWORLD_REPORT_FEDERALIST />'], [4,'<lngj:HELLOWORLD_REPORT_WHIG />'], [5,'<lngj:HELLOWORLD_REPORT_NATUNION />'], [6,'<lngj:HELLOWORLD_REPORT_NOPARTY />'] ] }) ]
2. Implementing Interfaces
2.1. For the simplicity, we will implement reports provider interface in module manager's class. So there is nothing to change in module initialization - module's manager initializes in its regular way. But in the module manager's class you have to inherit methods of the IReportsProvider
interface.
class CHelloWorldManager : public IReportsProvider
2.2. At the second phase of module initialization (the module is already initialized and you can call other modules' interfaces) you have to register reports manager for Hello World module in system reports module TWX_REPORTS
.
//+------------------------------------------------------------------+ //| Post-Initialization | //| You may call other modules' interfaces | //+------------------------------------------------------------------+ TWRESULT CHelloWorldModule::PostInitialize() { IReportsManager *reports_mngr=NULL; // Get interface of system module TWX_REPORTS if(m_server && RES_SUCCEEDED(m_server->GetInterface(TWX_REPORTS, L"IReportsManager", (void**)&reports_mngr))) { TWRESULT res = reports_mngr->Register(HELLOWORLD_MODULE_ID, 0, &m_manager); if(RES_FAILED(res)) ReturnErrorExt(res, NULL, "Failed to register"); } return(RES_S_OK); }
2.3. In the manager declare methods of working with reports.
public: //--- //--- Reports provider's methods virtual TWRESULT PreProcess(const Context *context, int module_id, const wchar_t* path, IReportsParams *params); virtual TWRESULT Process(const Context *context, int module_id, const wchar_t* path, IReportsParams *params) ; //--- virtual TWRESULT GetCount(const Context *context, int module_id, int *count); virtual TWRESULT GetInfo(const Context *context, int module_id, int index, wchar_t *url, int url_len, wchar_t *title, int title_len, wchar_t *img_url, int img_url_len, int *group); //--- Data for reports TWRESULT ReportUSPresidentsParty2JSON(const Context * context);
2.4. Implement these methods.
CHelloWorldManager::PreProcess
. Be sure to set theIReportsParams::PARAM_USE_FRAMEWORK
flag to load TeamWox library of JavaScript components. Without it your page won't generate reports charts using theChart
control.
//+------------------------------------------------------------------+ //| Prepare to process the report page | //+------------------------------------------------------------------+ TWRESULT CHelloWorldManager::PreProcess(const Context *context, int module_id, const wchar_t* path, IReportsParams *params) { //--- проверки if(context==NULL || module_id!=HELLOWORLD_MODULE_ID || path==NULL || params==NULL) ReturnError(RES_E_INVALID_ARGS); if(context->user==NULL) ReturnError(RES_E_INVALID_CONTEXT); if(m_server==NULL) ReturnError(RES_E_FAIL); //--- Process report named as helloworld_report if(StringCompareExactly(L"helloworld_report", path)) { //--- params->SetLiftime(30*60); params->SetHelpUrl(L"helloworld/reports"); params->SetBackUrl(L"/helloworld/index"); params->SetTitle(m_server->GetString(context, NULL, L"MODULE_TITLE", NULL)); params->SetFlags(IReportsParams::PARAM_HEADER | IReportsParams::PARAM_BAND | IReportsParams::PARAM_USE_FRAMEWORK); //--- return(RES_S_OK); } //--- return(RES_E_NOT_FOUND); }
CHelloWorldManager::Process
. Here we redirect HTTP requests to the report page. Here thehelloworld_report
- is the third part of the report page's request URL.
//+------------------------------------------------------------------+ //| Process page | //+------------------------------------------------------------------+ TWRESULT CHelloWorldManager::Process(const Context *context, int module_id, const wchar_t* path, IReportsParams *params) { //--- Checks if(context==NULL || module_id!=HELLOWORLD_MODULE_ID || path==NULL || params==NULL) ReturnError(RES_E_INVALID_ARGS); if(context->user==NULL) ReturnError(RES_E_INVALID_CONTEXT); if(m_server==NULL) ReturnError(RES_E_FAIL); //--- if(StringCompareExactly(L"helloworld_report", path)) return(CPageReportHelloworldReport().Process(context, m_server, path, this, params)); //--- return(RES_S_OK); }
CHelloWorldManager::GetCount
. In our example we have only one report. There can be any number of reports, but their actual number depends on access rights of a particular user.
//+------------------------------------------------------------------+ //| Number of report's pages | //+------------------------------------------------------------------+ TWRESULT CHelloWorldManager::GetCount(const Context *context, int module_id, int *count) { //--- Checks if(count==NULL || module_id!=HELLOWORLD_MODULE_ID) ReturnError(RES_E_INVALID_ARGS); //--- *count=1; //--- return(RES_S_OK); }
CHelloWorldManager::GetInfo
.
//+------------------------------------------------------------------+ //| Get info about module's reports | //+------------------------------------------------------------------+ TWRESULT CHelloWorldManager::GetInfo(const Context *context, int module_id, int index, wchar_t *url, int url_len, wchar_t *title, int title_len, wchar_t *img_url, int img_url_len, int *group) { //--- Checks if(module_id!=HELLOWORLD_MODULE_ID || url==NULL || url_len<=0 || title==NULL || title_len<=0 || img_url==NULL || img_url_len<=0 || group==NULL) ReturnError(RES_E_INVALID_ARGS); if(m_server==NULL) ReturnError(RES_E_FAIL); if(context==NULL) ReturnError(RES_E_INVALID_ARGS); if(context->user==NULL) ReturnError(RES_E_INVALID_CONTEXT); //--- const wchar_t *transl=NULL; //--- switch(index) { case 0: // There is only one report. For each subsequent report add its own 'case' transl=m_server->GetString(context, NULL, L"MENU_HELLOWORLD_REPORTS", NULL); if(FAILED(StringCchCopyEx(title, title_len, transl, NULL, NULL, STRSAFE_IGNORE_NULLS))) ReturnError(RES_E_FAIL); //--- Template filename if(FAILED(StringCchCopy(url, url_len, L"helloworld_report"))) ReturnError(RES_E_FAIL); //--- Path to report's icon if(FAILED(StringCchCopy(img_url, img_url_len, L"/reports/res/i/report.gif"))) ReturnError(RES_E_FAIL); *group=1; break; default: _ASSERT(0); } //--- return(RES_S_OK); }
CHelloWorldManager::ReportUSPresidentsParty2JSON
. This method generates the report data in JSON format. In our case it will be an array of arrays like[Party, Number of presidents in this party]
.
//+------------------------------------------------------------------+ //| Method that generates JSON array for reports | //+------------------------------------------------------------------+ TWRESULT CHelloWorldManager::ReportUSPresidentsParty2JSON(const Context* context) { //--- Checks if(context==NULL) ReturnError(RES_E_INVALID_ARGS); if(context->response==NULL || context->sql==NULL) ReturnError(RES_E_INVALID_CONTEXT); //--- int party = 0; int count = 0; wchar_t title[][64]={ L"HELLOWORLD_REPORT_REPUBLICAN", L"HELLOWORLD_REPORT_DEMOCRATIC", L"HELLOWORLD_REPORT_DEMOREP", L"HELLOWORLD_REPORT_FEDERALIST", L"HELLOWORLD_REPORT_WHIG", L"HELLOWORLD_REPORT_NATUNION", L"HELLOWORLD_REPORT_NOPARTY" }; //--- Parameters to get the request data SqlParam p_out[] = { SQL_LONG, &party,sizeof(party), SQL_LONG, &count,sizeof(count) }; //--- Text of request char query[] = "SELECT party, COUNT(id) AS cnt FROM HELLOWORLD GROUP BY party"; //--- Make the request if(!context->sql->Query(query, NULL, 0, p_out, _countof(p_out))) ReturnErrorExt(RES_E_SQL_ERROR, context, "Failed to sql query"); //--- Generate JSON array of arrays like [Party, Number of presidents in this party] CJSON json(context->response); json << CJSON::ARRAY; while(context->sql->QueryFetch()) { json << CJSON::ARRAY; //--- if(party>=0 && party<_countof(title)) json << m_server->GetString(context, NULL, title[party], NULL); else json << m_server->GetString(context, NULL, L"UNKNOWN_PARTY", NULL); //--- json << count; //--- json << CJSON::CLOSE; } json << CJSON::CLOSE; //--- Release request if(!context->sql->QueryFree()) ReturnErrorExt(RES_E_SQL_ERROR, context, "Failed to sql query free"); //--- return(RES_S_OK); }
3. HTTP API of the Report Page
In the CHelloWorldManager::Process
method we've redirected processing of the report page's request to the CPageReportHelloworldReport
page. Now we need to implement the class of this page and then create the user interface for it.
Create the report page. For logical organization of reports pages they should be separated from the other module's pages and placed in the Pages\reports\
sub-folder both in project and on the server. The same is true for the templates of reports pages (templates\reports
).
- Declaration. The method of page processing now must also accept the report parameters.
\//+------------------------------------------------------------------+ //| TeamWox | //| Copyright c 2006-2008, MetaQuotes Software Corp. | //| https://www.metaquotes.net | //+------------------------------------------------------------------+ #pragma once class CHelloWorldManager; //+------------------------------------------------------------------+ //| Reports page for the HelloWorld module | //+------------------------------------------------------------------+ class CPageReportHelloworldReport : public CPage { private: CHelloWorldManager* m_manager; public: CPageReportHelloworldReport(); ~CPageReportHelloworldReport(); virtual bool Tag(const Context *context,const TagInfo *tag); TWRESULT Process(const Context *context, IServer* server, const wchar_t* path, CHelloWorldManager* manager, IReportsParams *params); };
- Implementation. "Wrap" HTTP response with data in JSON format into the
data_tag
token.
\//+------------------------------------------------------------------+ //| TeamWox | //| Copyright c 2006-2008, MetaQuotes Software Corp. | //| https://www.metaquotes.net | //+------------------------------------------------------------------+ #include "StdAfx.h" #include "PageReportHelloworldReport.h" #include "..\..\Managers\HelloWorldManager.h" //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPageReportHelloworldReport::CPageReportHelloworldReport() : m_manager(NULL) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CPageReportHelloworldReport::~CPageReportHelloworldReport() { m_manager=NULL; } //+------------------------------------------------------------------+ //| Process page | //+------------------------------------------------------------------+ TWRESULT CPageReportHelloworldReport::Process(const Context *context, IServer* server, const wchar_t* path, CHelloWorldManager* manager, IReportsParams *params) { //--- Checks if(context==NULL || path==NULL) ReturnError(RES_E_INVALID_ARGS); if(manager==NULL || server==NULL) ReturnError(RES_E_INVALID_ARGS); if(context->request==NULL || context->user==NULL) ReturnError(RES_E_INVALID_CONTEXT); //--- m_manager = manager; /--- return(server->PageProcess(context, L"templates\\reports\\helloworld_report.tpl", this, TW_PAGEPROCESS_STANDART)); } //+------------------------------------------------------------------+ //| Process token | //+------------------------------------------------------------------+ bool CPageReportHelloworldReport::Tag(const Context *context,const TagInfo *tag) { //--- Checks if(context==NULL || tag==NULL) return(false); if(context->request==NULL || context->user==NULL) return(false); //--- if(TagCompare(L"data_tag", tag)) { TWRESULT res=m_manager->ReportUSPresidentsParty2JSON(context); if(RES_FAILED(res)) ReturnErrorExt(false, context, "Failed to form hellowrold report"); return false; } //--- return false; }
4. User Interface
4.1. Create the user interface for the report page (templates\reports\helloworld_report.tpl
). Since the report page is loaded in the separate frame, you don't need to add the basic HTML tags and the code of TeamWox library initialization - all the necessary environment is loaded, if the IReportsParams::PARAM_USE_FRAMEWORK
flag has been set.
<script type="text/javascript"> var chartTitle = '<lngj:REPORT_HELLOWORLD_REPORT_TITLE /> '; var page_data = <tw:data_tag /> var chart1 = TeamWox.Control('Chart', TeamWox.Chart.Type.BAR, { title : chartTitle, series : [ { type : TeamWox.Chart.SeriesType.TITLE }, { type : TeamWox.Chart.SeriesType.BAR, title: '<lngj:REPORT_HELLOWORLD_REPORT_SERIES />' } ] }).Style("padding", "5px 4px 5px 4px") .SetValues(page_data, true); </script>
4.2. Add the command to call the report into page header of module's main page. Specify the URL according to the reports routing rule.
//+----------------------------------------------+ //| Page Header with commands | //+----------------------------------------------+ var header = TeamWox.Control("PageHeader","#41633C") .Command("<lngj:MENU_HELLOWORLD_NUMBER1>","/helloworld/number_one","<lngj:MENU_HELLOWORLD_NUMBER1>") .Command("<lngj:MENU_HELLOWORLD_NUMBER2>","/helloworld/number_two","<lngj:MENU_HELLOWORLD_NUMBER2>") .Command("<lngj:MENU_HELLOWORLD_REPORTS>","/reports/helloworld/helloworld_report","<lngj:MENU_HELLOWORLD_REPORTS>") .Help("/helloworld/index") .Search(65536);
4.3. Don't forget to add translations for new keys in helloworld.lng
.
[eng] ;--- MENU_HELLOWORLD_REPORTS ="Reports" REPORT_HELLOWORLD_REPORT_TITLE ="US Presidents' parties report" REPORT_HELLOWORLD_REPORT_SERIES="Presidents per party" ;--- HELLOWORLD_REPORT_REPUBLICAN ="Republican" HELLOWORLD_REPORT_DEMOCRATIC ="Democratic" HELLOWORLD_REPORT_DEMOREP ="Democratic-Republican" HELLOWORLD_REPORT_FEDERALIST ="Federalist" HELLOWORLD_REPORT_WHIG ="Whig" HELLOWORLD_REPORT_NATUNION ="National Union" HELLOWORLD_REPORT_NOPARTY ="No Party" ;--- [rus] ;--- MENU_HELLOWORLD_REPORTS ="Отчеты" REPORT_HELLOWORLD_REPORT_TITLE ="Отчет по партиям президентов США" REPORT_HELLOWORLD_REPORT_SERIES="Кол-во президентов на партию" ;--- HELLOWORLD_REPORT_REPUBLICAN ="Республиканец" HELLOWORLD_REPORT_DEMOCRATIC ="Демократ" HELLOWORLD_REPORT_DEMOREP ="Демократ-республиканец" HELLOWORLD_REPORT_FEDERALIST ="Федералист" HELLOWORLD_REPORT_WHIG ="Виг" HELLOWORLD_REPORT_NATUNION ="Национальный союз" HELLOWORLD_REPORT_NOPARTY ="Беспартийный"
5. Custom Report Demonstration
5.1. Compile the module and start the TeamWox server. In Hello World module go to the reports page.
5.2. That opens the user interface of the system reports module. In the first frame we see the module name Hello World, for which we have created report, as well as the name of the report page. The default icon is displayed before the name of report. We have specified the path to this icon in the CHelloWorldManager::GetInfo
method.
5.3. If you switch to another language of TeamWox interface in your profile (in our case it is Russian), then all texts on the chart will update accordingly.
Conclusion
From this article you've learned how to create custom reports in TeamWox groupware. As you can see - it's pretty simple. In our example the report has been implemented directly in the module.
If you want to create a report for module from standard TeamWox delivery, then the implementation approach in this case is slightly differs. Third-party developers don't have access to standard modules' source codes, so the solution here is to create a separate module that implements necessary reports.
An example of such a module called ReportsExt is included in TeamWox SDK. This module implements custom reports for the Chat module (evaluation of the quality of employees' work and quality of clients servicing based on communication between customers with employees in chat) and the Service Desk (users activity of posting requests sorted by category).
In our next article we are going to highlight the aspects of using TeamWox file storage.
- How to Add a Ready-made Module to TeamWox
- How to Add Page into TeamWox Module
- Building User Interface
- Interaction with DBMS
- Creating Custom Reports
- TeamWox File Storage - Part 1
- TeamWox File Storage - Part 2
- Setting Up Custom Modules Environment - Part 1
- Setting Up Custom Modules Environment - Part 2
- Search and Filtering - Part 1
- Search and Filtering - Part 2
- Setting Up Online Assistant On Your Site
- How To Create Additional Language Package For TeamWox
2011.01.07