Авторизуйтесь или зарегистрируйтесь, чтобы создать новую тему

TeamWox API работа с задачами

dmitry_bezer
6
dmitry_bezer 12.11.2009 08:22 | #

1) как добраться до интефейса менеджера задач ITasks?

2) как получить список тех, кому эта задача назначена? в списке методов ITaskRecord соответствующего метод чтото не видно...

TeamWox SDK: Настройка окружения пользовательских модулей - Часть 1

В этой статье мы поговорим о том, как правильно настроить окружение пользовательских модулей TeamWox для их последующего распространения, в том числе на платной основе.

alexey
487
alexey 12.11.2009 16:40 | #
dmitry_bezer :

1) как добраться до интефейса менеджера задач ITasks?

2) как получить список тех, кому эта задача назначена? в списке методов ITaskRecord соответствующего метод чтото не видно...

Для того чтобы получить указатель на интерфейс менеджера задач Вам нужно:

  1. Подключить .h файл с API модуля задания. В файл stdafx.h добавьте строчку:
    #include "..\..\SDK\api\Team.h"
    путь до файла скорректируйте в зависимости от пути проекта модуля и пути SDK.

  2. При инициализации модуля получите ссылку на нужный менеджер. Например, это может быть сделано в вызове метода Initialize менеджера Вашего модуля:
    TWRESULT CYourModuleMananger::Initialize(IServer *server)
      {
    //--- проверки
       if(server==NULL) ReturnError(RES_E_INVALID_ARGS);
    //--- инициализируем интерфейсы
       TWRESULT res=server->GetInterface(TWX_TASKS, L"ITasks", (void**)&m_tasks_manager);
       if(RES_FAILED(res) || m_tasks_manager==NULL)
          ReturnErrorExt(res,NULL,"failed get ITasks");
    //--- другой код инициализации
       ..........
    //---
       return(RES_S_OK);
      }
  3. Далее Вы можете запросить какое либо задание и получить список назначенных:
    ITaskRecord *task=NULL;
    //--- проверки
    if(m_tasks_manager==NULL) ReturnError(RES_E_INVALID_ARGS);
    //--- получим задание
    res=m_tasks_manager->TaskGet(context,task_id,&task,false);
    if(RES_FAILED(res) || task==NULL)
       ReturnErrorExt(context,res,"failed get task");
    //---
    INT64 assigned[32]  ={0};                // список назначенных пользователей
    int   assigned_count=_countof(assigned); // количество назначенных
    //--- получим список назначенных
    task->GetAssigned(assigned,&assigned_count);
    //--- другой код
    .....
    //---
    task->Release();
    task=NULL;

dmitry_bezer
6
dmitry_bezer 13.11.2009 10:49 | #

Упс.. GetAssigned() проглядел, самым позорным образом.. Спасибо. 

А как получить количество назначенных? чтобы заранее знать сколько памяти понадобится под буфер? 

еще пара вопросов, 

1) насколько я понимаю получение интерфейса через  GetInterface сделано по образу COM IUnknown->QueryInterface. Если так, но надо ли мне заботиться об освобождении, типа m_task_manager->Release(). Или GetInterface это просто получение указателя, без reference counting?

2)Мне нужно сделать экспорт задач в определенном формате, можно ли как нибудь добавить пункт в сущетсвующее меню, а не делать новую закладку в верхнем меню, путем добавления нового модуля?  типа так:

 


P.S. плохо без документации.. 

alexey
487
alexey 13.11.2009 14:15 | #
dmitry_bezer :

Упс.. GetAssigned() проглядел, самым позорным образом.. Спасибо. 

А как получить количество назначенных? чтобы заранее знать сколько памяти понадобится под буфер? 

еще пара вопросов, 

1) насколько я понимаю получение интерфейса через  GetInterface сделано по образу COM IUnknown->QueryInterface. Если так, но надо ли мне заботиться об освобождении, типа m_task_manager->Release(). Или GetInterface это просто получение указателя, без reference counting?

2)Мне нужно сделать экспорт задач в определенном формате, можно ли как нибудь добавить пункт в сущетсвующее меню, а не делать новую закладку в верхнем меню, путем добавления нового модуля?  типа так:

P.S. плохо без документации.. 

Приносим свои извинения за отсутствие документации.

Количество назначенных меньше или равно 32.

1) Идеология работы с интерфейсами действительно схожа с COM. Интерфейс, который надо освобождать имеет в наличии метод Release(). Например, "ITasks" не имеет такого метода, его освобождать не нужно.

2) К сожалению, на данный момент добавлять пункты меню нельзя. Мы реализуем интерфейсы, которые позволят добавлять свои отчеты (пункты меню) для модулей.

Хотелось бы так же отметить, что сейчас проводится активная доработка модуля для обмена данными (импорта/экспорта) в разных форматах. По умолчанию будут доступны форматы CSV, Excel/HTML (html файл, который открывается MS Excel-ем). В будущем планируется добавить поддержку 1С. Так же разработчики смогут добавить поддержку своего формата реализовав класс поддерживающий интерфейс IImportReader или IExportWriter, для чтения и записи данных в нужном формате.

dmitry_bezer
6
dmitry_bezer 27.01.2010 12:13 | #

Еще несколько вопросов:

есть такая проблема: надо сделать средней сложности отчет по задачам, с группировками итд.

1) Правильно ли делать выборку задач для отчета при помощи SQL запроса? я не вижу другого разумного способа и меня настораживает то что это делается в обход интерфейсов ITasks и ITask, которые предназначены для доступа к задачам. то есть появляется зависимость от структуры БД - имен таблиц, полей итд. 

2) Как рисовать красивые флешовые диаграммы, такие как на Задачи -> Отчеты -> Пользователи? Желательно пример кода.

alexey
487
alexey 28.01.2010 21:01 | #
dmitry_bezer :

Еще несколько вопросов:

есть такая проблема: надо сделать средней сложности отчет по задачам, с группировками итд.

1) Правильно ли делать выборку задач для отчета при помощи SQL запроса? я не вижу другого разумного способа и меня настораживает то что это делается в обход интерфейсов ITasks и ITask, которые предназначены для доступа к задачам. то есть появляется зависимость от структуры БД - имен таблиц, полей итд. 

2) Как рисовать красивые флешовые диаграммы, такие как на Задачи -> Отчеты -> Пользователи? Желательно пример кода.

1) Отчеты с группировками, сортировками требуют значительное количество ресурсов и могут выполнятся достаточно долгое время. В процессе выборки данных из кеша (а выборки списка задач кешируются) происходит синхронизация доступа к кешу, что скажется на работе обычных пользователей с системой. Названия таблиц и полей, так же как и интерфейсы меняться не будут.

Хотелось бы так же проинформировать, что сейчас в разработке находится серверный модуль Отчеты, который:

  • позволит легко расширять список отчетов модулей отчетами из разработанных пользователями модулей;
  • имеет функционал кеширований результатов отчетов.

Так же будет расширено количество примеров модулей в TeamWox SDK - будут добавлены примеры модулей:

  • дополнительных отчетов;
  • источников (import) и приемников (export) данных для расширения возможности TeamWox  обмена информации с другими информационными системами.

2)

Для построения графиков используется следующие интерфейсы сервера:
   - IChart    - визуализация графиков
   - IChartMap - визуализация карт

Алгоритм работы при построении графика следующий:

  1. Запрашиваем интерфейс IChart или IChartMap у сервера;
  2. Получим данные и добавим их для отображения в графике:
    a) Запрашиваем у полученного интерфейса серии (IChartSeries) для заполнения данных;
    б) Задаем настройки серий (SetType, AddOption);
    в) Заполняем серии данными (AddValue);
  3. Задаем настройки отображения для графиков;
  4. Вывод графика с данными на страницу (ShowChart или ShowChartExt);
  5. Освобождение интерфейса графика (Release).

Описанный алгоритм в исходном коде выглядит следующим образом:

Часть файла класса страницы с отчетами PageReportCount.cpp:

//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CPageReportCount::~CPageReportCount()
  {
//--- 5. Освобождение интерфейса графика (Release)
   if(m_report!=NULL)
     {
      m_report->Release();
      m_report=NULL;
     }
//---
  }
//+------------------------------------------------------------------+
//| Обработка запросов на странице для отображения отчета            |
//+------------------------------------------------------------------+
TWRESULT CPageReportCount::Process(const Context *context,IServer *server,const wchar_t *path,CHelloWorldManager *manager)
  {
//--- проверки
   if(context==NULL || manager==NULL || path==NULL || server==NULL) ReturnError(RES_E_INVALID_ARGS);
   if(context->user==NULL)                                          ReturnError(RES_E_INVALID_CONTEXT);
//--- проверим право на просмотр отчетов
   if(!context->user->PermissionCheck(MODULE_HELLO_WORLD_ID,HELLO_WORLD_PERM_REPORT))
      return(RES_E_ACCESS_DENIED);
//---
TWRESULT res = RES_S_OK;
//--- 1. Запрашиваем интерфейс IChart или IChartMap у сервера
   res=m_server->GetInterface(L"IChart",(void**)m_report);
   if(RES_FAILED(res) || m_report==NULL || (*m_report)==NULL)
      ReturnErrorExt(res,context,"failed get interface IChart");
//--- 2. Получим данные и добавим их для отображения в графике
   res=manager->ReportCount(context,&m_report);
   if(RES_FAILED(res))
     {
      if(res!=RES_E_ACCESS_DENIED && res!=RES_E_NOT_FOUND)
         ReturnErrorExt(res,context,"failed get report count");
      //---
      return(res);
     }
//--- покажем шаблон страницы
   return(server->PageProcess(context,L"templates\\reports\\count.tpl",this,TW_PAGEPROCESS_NOCACHE));
  }
//+------------------------------------------------------------------+
//| Обработка тегов на шаблоне с отчетами                            |
//+------------------------------------------------------------------+
bool CPageReportCount::Tag(const Context *context,const TagInfo *tag)
  {
//--- проверки
   if(context==NULL || tag==NULL || m_report==NULL || context->response==NULL) return(false);
//--- шапка страницы с командами и ссылкой на помощь
   if(TagCompare(L"page_header",tag))
     {
      CPageHeader header(context,m_server);
      //---
      header.Header(L"#41633C");
      header.Link(L"/helloworld/index", L"", L"MENU_HELLOWORLD_LIST",L"MENU_HELLOWORLD_LIST_TOOLTIP");
      header.Footer(true,L"helloworld/reports");
      //---
      return(false);
     }
//--- выведем на страницу отчет
   if(TagCompare(L"report",tag))
     {
      //--- 3. Задаем настройки отображения для графиков
      ChartOptions    options;
      ChartOptionsExt options_ext={0};
      //---
      m_report->OptionsDefault(CHART_TYPE_BAR_ACCUMULATION,&options);
      //---
      StringCchCopy(options.tooltip.message,_countof(options.tooltip.message),L"X_DATA; ;LINE_TITLE;");
      //---
      StringCchCopy(options.axis_x.title,_countof(options.axis_x.title),L"Tasks");
      StringCchCopy(options.axis_y.title,_countof(options.axis_y.title),L"Users");
      //---
      options.axis_x.legend=false;
      options.axis_y.legend=false;
      options.margin.top  =8;
      options.margin.right=8;
      //---
      options_ext.bar.padding=4;
      //--- 4. Вывод графика с данными на страницу (ShowChart или ShowChartExt)
      m_report->ShowChartExt(context,&options,&options_ext);
      //---
      return(false);
     }
//---
   return(false);
  }
//+------------------------------------------------------------------+

Часть файла менеджера модуля HelloWorldManager.cpp:

//+------------------------------------------------------------------+
//| Формирование данных для отчета по количеству задач на человека   |
//|                                                   за все время   |
//+------------------------------------------------------------------+
TWRESULT CHelloWorldManager::ReportCount(const Context *context,IChart *report)
  {
//--- проверки
   if(context==NULL || report==NULL || m_user_manager==NULL) ReturnError(RES_E_INVALID_ARGS);
   if(context->user==NULL || context->sql==NULL)             ReturnError(RES_E_INVALID_CONTEXT);
//--- проверим право на просмотр отчетов
   if(!context->user->PermissionCheck(MODULE_HELLO_WORLD_ID,HELLO_WORLD_PERM_REPORT))
      return(RES_E_ACCESS_DENIED);
//---
   UserInfo user_info={0};
   INT64    user_id  =0;
   int      cnt      =0;
//--- 2.a) Запршиваем у полученного интерфейса серии (IChartSeries) для заполнения данных
   IChartSeries *series_title=report->AddYAxes();
   IChartSeries *series_data =report->AddSeries();
//--- 2.б) Задаем опции для серий (SetType, AddOption)
   series_data->SetType(CHART_SERIES_TYPE_BAR);
   series_data->AddOption("title",m_server->GetString(context,NULL,L"HELLO_WORLD_REPORT_COUNT_INCOMING",NULL));
   series_data->AddOption("tooltip",L"%TITLE%: %VALUE%");
   series_data->AddOption("color",L"#A34040");
   series_data->AddOption("alpha",L"1");
   series_data->AddOption("column_type", L"gradient_top");
//--- подготовим запрос
   char sql[]= "SELECT COUNT(t.id) AS cnt_id,tu.user_id "
               "FROM tasks t,tasks_users tu "
               "WHERE tu.task_id=t.id"
               "GROUP BY tu.user_id "
               "ORDER BY 1 DESC";
//--- подготовим параметры
   SqlParam params_res[]=
     {
      SQL_LONG, &cnt,    sizeof(cnt),
      SQL_INT64,&user_id,sizeof(user_id),
      };
//--- выполним запрос
   if(context->sql->Query(sql,NULL,0,params_res,_countof(params_res))==false)
      ReturnErrorExt(RES_E_SQL_ERROR,context,"failed SQL query for report");
//--- 2.в) Заполняем серии данными (AddValue)
   while(context->sql->QueryFetch())
     {
      //--- получим информацию о пользователе
      if(RES_FAILED(m_user_manager->UserGet(context,user_id,&user_info))) continue;
      //--- пропустим отключенных пользователей и группы/отделы
      if(user_info.status!=USER_STATUS_ACTIVE ||
         (user_info.type==USER_TYPE_USER && (flags&TASKS_REPORT_USER)==0) ||
         (user_info.type!=USER_TYPE_USER && (flags&TASKS_REPORT_GROUP)==0)) continue;
      //---
      series_title->AddValue(user_info.fullname);
      series_data->AddValue(cnt);
     }
//--- освободим запрос
   context->sql->QueryFree();
//---
   return(false);
  }
dmitry_bezer
6
dmitry_bezer 16.02.2010 21:58 | #

Уж извините, но ввиду отсутствия документации продолжаю донимать вас вопросами...


Итак, спасибо за пример кода -  простая диаграмма нарисовалась. Теперь бы хотелось нарисовать диаграмму, в которой каждая секция может быть поделена на несколько частей - например как в отчете Задания - у каждого сотрудника секция поделена на Входящие и Исходящие.


Пытаясь построить SQL запрос, не могу понять как определить характер связи между заданием и юзером. Вижу таблицу TASKS_USERS но из нее непонятно является ли юзер ответственным или просто назначеным. Как, скажем, выбрать всех пользователей с колличеством задач, за которое они отвественны?

alexey
487
alexey 01.03.2010 15:56 | #
dmitry_bezer :

Уж извините, но ввиду отсутствия документации продолжаю донимать вас вопросами...

Итак, спасибо за пример кода -  простая диаграмма нарисовалась. Теперь бы хотелось нарисовать диаграмму, в которой каждая секция может быть поделена на несколько частей - например как в отчете Задания - у каждого сотрудника секция поделена на Входящие и Исходящие.

Для того, чтобы на графике отобразились несколько серий данных на графике необходимо добавить нужное количество серий в коде наполнения данных и задать для них тип:

//+------------------------------------------------------------------+
//| Формирование данных для отчета по количеству задач на человека   |
//|                                                   за все время   |
//+------------------------------------------------------------------+
TWRESULT CHelloWorldManager::ReportCount(const Context *context,IChart *report)
  {
..............................
//--- 2.a) Запрашиваем у полученного интерфейса серии (IChartSeries) для заполнения данных
   IChartSeries *series_title =report->AddYAxes();
   IChartSeries *series_data =report->AddSeries();
   IChartSeries *series_data2=report->AddSeries();
   IChartSeries *series_data3=report->AddSeries();
..............................
series_data->SetType(CHART_SERIES_TYPE_BAR);
series_data2->SetType(CHART_SERIES_TYPE_BAR);
series_data3->
SetType(CHART_SERIES_TYPE_BAR);
  }
Выбрать необходимый тип диаграмм:
//+------------------------------------------------------------------+
//| Обработка тегов на шаблоне с отчетами                            |
//+------------------------------------------------------------------+
bool CPageReportCount::Tag(const Context *context,const TagInfo *tag)
  {
.....................................................................
      //--- 3. Задаем настройки отображения для графиков
      ChartOptions    options;
      ChartOptionsExt options_ext={0};
      //---
      m_report->OptionsDefault(CHART_TYPE_BAR_ACCUMULATION,&options);
.....................................................................
}

Отчетная система TeamWox поддерживает следующие типы графиков:

  1. Линия (тип серии: CHART_SERIES_TYPE_GRAPH; тип графика: CHART_TYPE_GRAPH_AND_COLUMN)

  2. Область с накоплением (тип серии: CHART_SERIES_TYPE_AREA; тип графика: CHART_TYPE_GRAPH_AND_COLUMN_ACCUMULATION)


  3. Область с нормализацией (тип серии: CHART_SERIES_TYPE_AREA; тип графика: CHART_TYPE_GRAPH_AND_COLUMN_NORMALIZED)


  4. Область с перекрытием (тип серии: CHART_SERIES_TYPE_AREA; тип графика: CHART_TYPE_GRAPH_AND_COLUMN_STACKED)


  5. Гистограмма (тип серии: CHART_SERIES_TYPE_COLUMN; тип графика: CHART_TYPE_GRAPH_AND_COLUMN)


  6. Гистограмма с накоплением (тип серии: CHART_SERIES_TYPE_COLUMN; тип графика: CHART_TYPE_GRAPH_AND_COLUMN_ACCUMULATION)


  7. Гистограмма с нормализацией (тип серии: CHART_SERIES_TYPE_COLUMN; тип графика: CHART_TYPE_GRAPH_AND_COLUMN_NORMALIZED)


  8. Гистограмма с перекрытием (тип серии: CHART_SERIES_TYPE_COLUMN; тип графика: CHART_TYPE_GRAPH_AND_COLUMN_STACKED)


  9. Линейчатая (тип серии: CHART_SERIES_TYPE_BAR; тип графика: CHART_TYPE_BAR)


  10. Линейчатая с накоплением (тип серии: CHART_SERIES_TYPE_BAR; тип графика: CHART_TYPE_BAR_ACCUMULATION)


  11. Линейчатая с нормализацией (тип серии: CHART_SERIES_TYPE_BAR; тип графика: CHART_TYPE_BAR_NORMALIZED)


  12. Линейчатая с перекрытием (тип серии: CHART_SERIES_TYPE_BAR; тип графика: CHART_TYPE_BAR_STACKED)


  13. Круговая (тип серии: CHART_SERIES_TYPE_PIE; тип графика: CHART_TYPE_PIE)


Пытаясь построить SQL запрос, не могу понять как определить характер связи между заданием и юзером. Вижу таблицу TASKS_USERS но из нее непонятно является ли юзер ответственным или просто назначеным. Как, скажем, выбрать всех пользователей с колличеством задач, за которое они отвественны?

На данный момент идентификация пользователя как ответственного можно сделать только через API, в базе данных в таблице TASKS_USERS информации об этом нет. Поле с такой информацией мы постараемся добавим к ближайшему релизу.
dmitry_bezer
6
dmitry_bezer 09.03.2010 21:20 | #

спасибо за ответы, теперь приключилась такая вот беда:

менеджер задач ITasks  *иногда* возвращает NULL , хотя точно известно что задача существует

пример кода:

sql = "SELECT TASKS.ID FROM TASKS";

INT64 qTaskId = 0;
SqlParam qParams[]=
{
    SQL_INT64,&qTaskId,sizeof(qTaskId),
};
ctx->sql->Query( sql.c_str(), NULL, 0, qParams, _countof(qParams) );

while( ctx->sql->QueryFetch() )
{
    ITaskRecord* task = NULL;
    taskMgr->TaskGet( ctx, qTaskId, &task );
    ctx->response->Write( fmt(L"taskId:%I64d, task:%p<br/>", qTaskId, task).c_str() );
    if( task )
    {
        task->Release();
        task = NULL;
    }
}
на выходе имеем:
taskId:2, task:0EE4001C
taskId:3, task:0EC9001C
taskId:4, task:0EE4001C
taskId:5, task:0EC9001C
taskId:6, task:0EE4001C
taskId:7, task:0EC9001C
taskId:8, task:0EE4001C
taskId:9, task:0EC9001C
taskId:10, task:00000000
taskId:11, task:00000000
taskId:12, task:00000000
taskId:13, task:00000000
taskId:14, task:00000000
taskId:15, task:00000000
taskId:16, task:0EE4001C
taskId:17, task:0EC9001C

dmitry_bezer
6
dmitry_bezer 25.03.2010 11:09 | #
up!
К списку тем  | 12

Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий